mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-04-20 17:14:42 +01:00
Merge branch 'master' into TVFix
This commit is contained in:
@@ -136,7 +136,7 @@ namespace Jellyfin.Api.Tests.Auth
|
||||
_jellyfinAuthServiceMock.Setup(
|
||||
a => a.Authenticate(
|
||||
It.IsAny<HttpRequest>()))
|
||||
.Returns(authorizationInfo);
|
||||
.Returns(Task.FromResult(authorizationInfo));
|
||||
|
||||
return authorizationInfo;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using AutoFixture;
|
||||
using AutoFixture.AutoMoq;
|
||||
using Jellyfin.Api.Auth.DefaultAuthorizationPolicy;
|
||||
using Jellyfin.Api.Constants;
|
||||
using Jellyfin.Server.Implementations.Security;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@@ -49,5 +50,61 @@ namespace Jellyfin.Api.Tests.Auth.DefaultAuthorizationPolicy
|
||||
await _sut.HandleAsync(context);
|
||||
Assert.True(context.HasSucceeded);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(GetParts_ValidAuthHeader_Success_Data))]
|
||||
public void GetParts_ValidAuthHeader_Success(string input, Dictionary<string, string> parts)
|
||||
{
|
||||
var dict = AuthorizationContext.GetParts(input);
|
||||
foreach (var (key, value) in parts)
|
||||
{
|
||||
Assert.Equal(dict[key], value);
|
||||
}
|
||||
}
|
||||
|
||||
private static TheoryData<string, Dictionary<string, string>> GetParts_ValidAuthHeader_Success_Data()
|
||||
{
|
||||
var data = new TheoryData<string, Dictionary<string, string>>();
|
||||
|
||||
data.Add(
|
||||
"x=\"123,123\",y=\"123\"",
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
{ "x", "123,123" },
|
||||
{ "y", "123" }
|
||||
});
|
||||
|
||||
data.Add(
|
||||
"x=\"123,123\", y=\"123\",z=\"'hi'\"",
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
{ "x", "123,123" },
|
||||
{ "y", "123" },
|
||||
{ "z", "'hi'" }
|
||||
});
|
||||
|
||||
data.Add(
|
||||
"x=\"ab\"",
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
{ "x", "ab" }
|
||||
});
|
||||
|
||||
data.Add(
|
||||
"param=Hörbücher",
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
{ "param", "Hörbücher" }
|
||||
});
|
||||
|
||||
data.Add(
|
||||
"param=%22%Hörbücher",
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
{ "param", "\"%Hörbücher" }
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using AutoFixture;
|
||||
using AutoFixture.AutoMoq;
|
||||
using Jellyfin.Api.Controllers;
|
||||
using Jellyfin.Api.Helpers;
|
||||
using Jellyfin.Api.Models.StreamingDtos;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Api.Tests.Controllers
|
||||
{
|
||||
public class DynamicHlsControllerTests
|
||||
{
|
||||
[Theory]
|
||||
[MemberData(nameof(GetSegmentLengths_Success_TestData))]
|
||||
public void GetSegmentLengths_Success(long runtimeTicks, int segmentlength, double[] expected)
|
||||
{
|
||||
var res = DynamicHlsController.GetSegmentLengthsInternal(runtimeTicks, segmentlength);
|
||||
Assert.Equal(expected.Length, res.Length);
|
||||
for (int i = 0; i < expected.Length; i++)
|
||||
{
|
||||
Assert.Equal(expected[i], res[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> GetSegmentLengths_Success_TestData()
|
||||
{
|
||||
yield return new object[] { 0, 6, Array.Empty<double>() };
|
||||
yield return new object[]
|
||||
{
|
||||
TimeSpan.FromSeconds(3).Ticks,
|
||||
6,
|
||||
new double[] { 3 }
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
TimeSpan.FromSeconds(6).Ticks,
|
||||
6,
|
||||
new double[] { 6 }
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
TimeSpan.FromSeconds(3.3333333).Ticks,
|
||||
6,
|
||||
new double[] { 3.3333333 }
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
TimeSpan.FromSeconds(9.3333333).Ticks,
|
||||
6,
|
||||
new double[] { 6, 3.3333333 }
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,22 +8,19 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Nullable>enable</Nullable>
|
||||
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoFixture" Version="4.16.0" />
|
||||
<PackageReference Include="AutoFixture.AutoMoq" Version="4.16.0" />
|
||||
<PackageReference Include="AutoFixture.Xunit2" Version="4.16.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.5" />
|
||||
<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="5.0.9" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.0.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -171,11 +171,11 @@ namespace Jellyfin.Common.Tests.Cryptography
|
||||
[InlineData("$PBKDF2$=$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid parmeter
|
||||
[InlineData("$PBKDF2$=1000$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid parmeter
|
||||
[InlineData("$PBKDF2$iterations=$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid parmeter
|
||||
[InlineData("$PBKDF2$iterations=$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$")] // Ends on $
|
||||
[InlineData("$PBKDF2$iterations=$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$")] // Extra segment
|
||||
[InlineData("$PBKDF2$iterations=$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$anotherone")] // Extra segment
|
||||
[InlineData("$PBKDF2$iterations=$invalidstalt$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid salt
|
||||
[InlineData("$PBKDF2$iterations=$69F420$invalid hash")] // Invalid hash
|
||||
[InlineData("$PBKDF2$iterations=1000$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$")] // Ends on $
|
||||
[InlineData("$PBKDF2$iterations=1000$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$")] // Extra segment
|
||||
[InlineData("$PBKDF2$iterations=1000$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$anotherone")] // Extra segment
|
||||
[InlineData("$PBKDF2$iterations=1000$invalidstalt$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid salt
|
||||
[InlineData("$PBKDF2$iterations=1000$69F420$invalid hash")] // Invalid hash
|
||||
[InlineData("$PBKDF2$69F420$")] // Empty hash
|
||||
public static void Parse_InvalidFormat_ThrowsFormatException(string passwordHash)
|
||||
{
|
||||
|
||||
@@ -8,17 +8,15 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Nullable>enable</Nullable>
|
||||
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.0.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0" />
|
||||
<PackageReference Include="FsCheck.Xunit" Version="2.16.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
using System.Text.Json;
|
||||
using MediaBrowser.Common.Json.Converters;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Common.Tests.Json
|
||||
{
|
||||
public static class JsonBoolNumberTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("1", true)]
|
||||
[InlineData("0", false)]
|
||||
[InlineData("2", true)]
|
||||
[InlineData("true", true)]
|
||||
[InlineData("false", false)]
|
||||
public static void Deserialize_Number_Valid_Success(string input, bool? output)
|
||||
{
|
||||
var options = new JsonSerializerOptions();
|
||||
options.Converters.Add(new JsonBoolNumberConverter());
|
||||
var value = JsonSerializer.Deserialize<bool>(input, options);
|
||||
Assert.Equal(value, output);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, "true")]
|
||||
[InlineData(false, "false")]
|
||||
public static void Serialize_Bool_Success(bool input, string output)
|
||||
{
|
||||
var options = new JsonSerializerOptions();
|
||||
options.Converters.Add(new JsonBoolNumberConverter());
|
||||
var value = JsonSerializer.Serialize(input, options);
|
||||
Assert.Equal(value, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,18 +8,15 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Nullable>enable</Nullable>
|
||||
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.0.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
|
||||
131
tests/Jellyfin.Dlna.Tests/DlnaManagerTests.cs
Normal file
131
tests/Jellyfin.Dlna.Tests/DlnaManagerTests.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
using Emby.Dlna;
|
||||
using Emby.Dlna.PlayTo;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Dlna.Tests
|
||||
{
|
||||
public class DlnaManagerTests
|
||||
{
|
||||
private DlnaManager GetManager()
|
||||
{
|
||||
var xmlSerializer = new Mock<IXmlSerializer>();
|
||||
var fileSystem = new Mock<IFileSystem>();
|
||||
var appPaths = new Mock<IApplicationPaths>();
|
||||
var loggerFactory = new Mock<ILoggerFactory>();
|
||||
var appHost = new Mock<IServerApplicationHost>();
|
||||
|
||||
return new DlnaManager(xmlSerializer.Object, fileSystem.Object, appPaths.Object, loggerFactory.Object, appHost.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsMatch_GivenMatchingName_ReturnsTrue()
|
||||
{
|
||||
var device = new DeviceInfo()
|
||||
{
|
||||
Name = "My Device",
|
||||
Manufacturer = "LG Electronics",
|
||||
ManufacturerUrl = "http://www.lge.com",
|
||||
ModelDescription = "LG WebOSTV DMRplus",
|
||||
ModelName = "LG TV",
|
||||
ModelNumber = "1.0",
|
||||
};
|
||||
|
||||
var profile = new DeviceProfile()
|
||||
{
|
||||
Name = "Test Profile",
|
||||
FriendlyName = "My Device",
|
||||
Manufacturer = "LG Electronics",
|
||||
ManufacturerUrl = "http://www.lge.com",
|
||||
ModelDescription = "LG WebOSTV DMRplus",
|
||||
ModelName = "LG TV",
|
||||
ModelNumber = "1.0",
|
||||
Identification = new ()
|
||||
{
|
||||
FriendlyName = "My Device",
|
||||
Manufacturer = "LG Electronics",
|
||||
ManufacturerUrl = "http://www.lge.com",
|
||||
ModelDescription = "LG WebOSTV DMRplus",
|
||||
ModelName = "LG TV",
|
||||
ModelNumber = "1.0",
|
||||
}
|
||||
};
|
||||
|
||||
var profile2 = new DeviceProfile()
|
||||
{
|
||||
Name = "Test Profile",
|
||||
FriendlyName = "My Device",
|
||||
Identification = new DeviceIdentification()
|
||||
{
|
||||
FriendlyName = "My Device",
|
||||
}
|
||||
};
|
||||
|
||||
var deviceMatch = GetManager().IsMatch(device.ToDeviceIdentification(), profile2.Identification);
|
||||
var deviceMatch2 = GetManager().IsMatch(device.ToDeviceIdentification(), profile.Identification);
|
||||
|
||||
Assert.True(deviceMatch);
|
||||
Assert.True(deviceMatch2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsMatch_GivenNamesAndManufacturersDoNotMatch_ReturnsFalse()
|
||||
{
|
||||
var device = new DeviceInfo()
|
||||
{
|
||||
Name = "My Device",
|
||||
Manufacturer = "JVC"
|
||||
};
|
||||
|
||||
var profile = new DeviceProfile()
|
||||
{
|
||||
Name = "Test Profile",
|
||||
FriendlyName = "My Device",
|
||||
Manufacturer = "LG Electronics",
|
||||
ManufacturerUrl = "http://www.lge.com",
|
||||
ModelDescription = "LG WebOSTV DMRplus",
|
||||
ModelName = "LG TV",
|
||||
ModelNumber = "1.0",
|
||||
Identification = new ()
|
||||
{
|
||||
FriendlyName = "My Device",
|
||||
Manufacturer = "LG Electronics",
|
||||
ManufacturerUrl = "http://www.lge.com",
|
||||
ModelDescription = "LG WebOSTV DMRplus",
|
||||
ModelName = "LG TV",
|
||||
ModelNumber = "1.0",
|
||||
}
|
||||
};
|
||||
|
||||
var deviceMatch = GetManager().IsMatch(device.ToDeviceIdentification(), profile.Identification);
|
||||
|
||||
Assert.False(deviceMatch);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsMatch_GivenNamesAndRegExMatch_ReturnsTrue()
|
||||
{
|
||||
var device = new DeviceInfo()
|
||||
{
|
||||
Name = "My Device"
|
||||
};
|
||||
|
||||
var profile = new DeviceProfile()
|
||||
{
|
||||
Name = "Test Profile",
|
||||
FriendlyName = "My .*",
|
||||
Identification = new ()
|
||||
};
|
||||
|
||||
var deviceMatch = GetManager().IsMatch(device.ToDeviceIdentification(), profile.Identification);
|
||||
|
||||
Assert.True(deviceMatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,17 +3,15 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Nullable>enable</Nullable>
|
||||
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.0.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Controller.Sorting;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Controller.Tests
|
||||
namespace Jellyfin.Extensions.Tests
|
||||
{
|
||||
public class AlphanumComparatorTests
|
||||
public class AlphanumericComparatorTests
|
||||
{
|
||||
// InlineData is pre-sorted
|
||||
[Theory]
|
||||
@@ -20,10 +19,10 @@ namespace Jellyfin.Controller.Tests
|
||||
[InlineData("12345678912345678912345678913234567891", "12345678912345678912345678913234567892")]
|
||||
[InlineData("12345678912345678912345678913234567891a", "12345678912345678912345678913234567891a")]
|
||||
[InlineData("12345678912345678912345678913234567891a", "12345678912345678912345678913234567891b")]
|
||||
public void AlphanumComparatorTest(params string?[] strings)
|
||||
public void AlphanumericComparatorTest(params string?[] strings)
|
||||
{
|
||||
var copy = strings.Reverse().ToArray();
|
||||
Array.Sort(copy, new AlphanumComparator());
|
||||
Array.Sort(copy, new AlphanumericComparator());
|
||||
Assert.True(strings.SequenceEqual(copy));
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Common.Tests.Extensions
|
||||
namespace Jellyfin.Extensions.Tests
|
||||
{
|
||||
public static class CopyToExtensionsTests
|
||||
{
|
||||
@@ -0,0 +1,35 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="FsCheck.Xunit" Version="2.16.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../MediaBrowser.Model/MediaBrowser.Model.csproj" />
|
||||
<ProjectReference Include="../../src/Jellyfin.Extensions/Jellyfin.Extensions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,45 @@
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
using FsCheck;
|
||||
using FsCheck.Xunit;
|
||||
using Jellyfin.Extensions.Json.Converters;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Extensions.Tests.Json.Converters
|
||||
{
|
||||
public class JsonBoolNumberTests
|
||||
{
|
||||
private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions()
|
||||
{
|
||||
Converters =
|
||||
{
|
||||
new JsonBoolNumberConverter()
|
||||
}
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[InlineData("1", true)]
|
||||
[InlineData("0", false)]
|
||||
[InlineData("2", true)]
|
||||
[InlineData("true", true)]
|
||||
[InlineData("false", false)]
|
||||
public void Deserialize_Number_Valid_Success(string input, bool? output)
|
||||
{
|
||||
var value = JsonSerializer.Deserialize<bool>(input, _jsonOptions);
|
||||
Assert.Equal(value, output);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, "true")]
|
||||
[InlineData(false, "false")]
|
||||
public void Serialize_Bool_Success(bool input, string output)
|
||||
{
|
||||
var value = JsonSerializer.Serialize(input, _jsonOptions);
|
||||
Assert.Equal(value, output);
|
||||
}
|
||||
|
||||
[Property]
|
||||
public Property Deserialize_NonZeroInt_True(NonZeroInt input)
|
||||
=> JsonSerializer.Deserialize<bool>(input.ToString(), _jsonOptions).ToProperty();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Jellyfin.Common.Tests.Models;
|
||||
using Jellyfin.Extensions.Tests.Json.Models;
|
||||
using MediaBrowser.Model.Session;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Common.Tests.Json
|
||||
namespace Jellyfin.Extensions.Tests.Json.Converters
|
||||
{
|
||||
public static class JsonCommaDelimitedArrayTests
|
||||
{
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Jellyfin.Common.Tests.Models;
|
||||
using Jellyfin.Extensions.Tests.Json.Models;
|
||||
using MediaBrowser.Model.Session;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Common.Tests.Json
|
||||
namespace Jellyfin.Extensions.Tests.Json.Converters
|
||||
{
|
||||
public static class JsonCommaDelimitedIReadOnlyListTests
|
||||
{
|
||||
@@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using MediaBrowser.Common.Json.Converters;
|
||||
using Jellyfin.Extensions.Json.Converters;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Common.Tests.Json
|
||||
namespace Jellyfin.Extensions.Tests.Json.Converters
|
||||
{
|
||||
public class JsonGuidConverterTests
|
||||
{
|
||||
@@ -1,9 +1,10 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Jellyfin.Extensions.Json.Converters;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Model.Tests.Entities
|
||||
namespace Jellyfin.Extensions.Tests.Json.Converters
|
||||
{
|
||||
public class JsonLowerCaseConverterTests
|
||||
{
|
||||
@@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using MediaBrowser.Common.Json.Converters;
|
||||
using Jellyfin.Extensions.Json.Converters;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Common.Tests.Json
|
||||
namespace Jellyfin.Extensions.Tests.Json.Converters
|
||||
{
|
||||
public class JsonNullableGuidConverterTests
|
||||
{
|
||||
@@ -1,19 +1,18 @@
|
||||
using System.Text.Json;
|
||||
using MediaBrowser.Common.Json.Converters;
|
||||
using System.Text.Json;
|
||||
using Jellyfin.Extensions.Json.Converters;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Common.Tests.Json
|
||||
namespace Jellyfin.Extensions.Tests.Json.Converters
|
||||
{
|
||||
public class JsonStringConverterTests
|
||||
{
|
||||
private readonly JsonSerializerOptions _jsonSerializerOptions
|
||||
= new ()
|
||||
private readonly JsonSerializerOptions _jsonSerializerOptions = new ()
|
||||
{
|
||||
Converters =
|
||||
{
|
||||
Converters =
|
||||
{
|
||||
new JsonStringConverter()
|
||||
}
|
||||
};
|
||||
new JsonStringConverter()
|
||||
}
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[InlineData("\"test\"", "test")]
|
||||
@@ -36,4 +35,4 @@ namespace Jellyfin.Common.Tests.Json
|
||||
Assert.Equal(deserialized, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using MediaBrowser.Common.Json.Converters;
|
||||
using Jellyfin.Extensions.Json.Converters;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Common.Tests.Json
|
||||
namespace Jellyfin.Extensions.Tests.Json.Converters
|
||||
{
|
||||
public class JsonVersionConverterTests
|
||||
{
|
||||
@@ -1,8 +1,8 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Json.Serialization;
|
||||
using MediaBrowser.Common.Json.Converters;
|
||||
using Jellyfin.Extensions.Json.Converters;
|
||||
|
||||
namespace Jellyfin.Common.Tests.Models
|
||||
namespace Jellyfin.Extensions.Tests.Json.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// The generic body model.
|
||||
@@ -1,8 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using MediaBrowser.Common.Json.Converters;
|
||||
using Jellyfin.Extensions.Json.Converters;
|
||||
|
||||
namespace Jellyfin.Common.Tests.Models
|
||||
namespace Jellyfin.Extensions.Tests.Json.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// The generic body <c>IReadOnlyList</c> model.
|
||||
@@ -1,8 +1,7 @@
|
||||
using System;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Common.Tests.Extensions
|
||||
namespace Jellyfin.Extensions.Tests
|
||||
{
|
||||
public static class ShuffleExtensionsTests
|
||||
{
|
||||
18
tests/Jellyfin.Extensions.Tests/StringExtensionsTests.cs
Normal file
18
tests/Jellyfin.Extensions.Tests/StringExtensionsTests.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Extensions.Tests
|
||||
{
|
||||
public class StringExtensionsTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("", '_', 0)]
|
||||
[InlineData("___", '_', 3)]
|
||||
[InlineData("test\x00", '\x00', 1)]
|
||||
[InlineData("Imdb=tt0119567|Tmdb=330|TmdbCollection=328", '|', 2)]
|
||||
public void ReadOnlySpan_Count_Success(string str, char needle, int count)
|
||||
{
|
||||
Assert.Equal(count, str.AsSpan().Count(needle));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,15 +9,18 @@ namespace Jellyfin.MediaEncoding.Tests
|
||||
{
|
||||
public class EncoderValidatorTests
|
||||
{
|
||||
private readonly EncoderValidator _encoderValidator = new EncoderValidator(new NullLogger<EncoderValidatorTests>(), "ffmpeg");
|
||||
|
||||
[Theory]
|
||||
[ClassData(typeof(GetFFmpegVersionTestData))]
|
||||
public void GetFFmpegVersionTest(string versionOutput, Version? version)
|
||||
{
|
||||
var val = new EncoderValidator(new NullLogger<EncoderValidatorTests>());
|
||||
Assert.Equal(version, val.GetFFmpegVersion(versionOutput));
|
||||
Assert.Equal(version, _encoderValidator.GetFFmpegVersionInternal(versionOutput));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(EncoderValidatorTestsData.FFmpegV44Output, true)]
|
||||
[InlineData(EncoderValidatorTestsData.FFmpegV432Output, true)]
|
||||
[InlineData(EncoderValidatorTestsData.FFmpegV431Output, true)]
|
||||
[InlineData(EncoderValidatorTestsData.FFmpegV43Output, true)]
|
||||
[InlineData(EncoderValidatorTestsData.FFmpegV421Output, true)]
|
||||
@@ -28,14 +31,15 @@ namespace Jellyfin.MediaEncoding.Tests
|
||||
[InlineData(EncoderValidatorTestsData.FFmpegGitUnknownOutput, false)]
|
||||
public void ValidateVersionInternalTest(string versionOutput, bool valid)
|
||||
{
|
||||
var val = new EncoderValidator(new NullLogger<EncoderValidatorTests>());
|
||||
Assert.Equal(valid, val.ValidateVersionInternal(versionOutput));
|
||||
Assert.Equal(valid, _encoderValidator.ValidateVersionInternal(versionOutput));
|
||||
}
|
||||
|
||||
private class GetFFmpegVersionTestData : IEnumerable<object?[]>
|
||||
{
|
||||
public IEnumerator<object?[]> GetEnumerator()
|
||||
{
|
||||
yield return new object?[] { EncoderValidatorTestsData.FFmpegV44Output, new Version(4, 4) };
|
||||
yield return new object?[] { EncoderValidatorTestsData.FFmpegV432Output, new Version(4, 3, 2) };
|
||||
yield return new object?[] { EncoderValidatorTestsData.FFmpegV431Output, new Version(4, 3, 1) };
|
||||
yield return new object?[] { EncoderValidatorTestsData.FFmpegV43Output, new Version(4, 3) };
|
||||
yield return new object?[] { EncoderValidatorTestsData.FFmpegV421Output, new Version(4, 2, 1) };
|
||||
|
||||
@@ -2,6 +2,30 @@ namespace Jellyfin.MediaEncoding.Tests
|
||||
{
|
||||
internal static class EncoderValidatorTestsData
|
||||
{
|
||||
public const string FFmpegV44Output = @"ffmpeg version 4.4-Jellyfin Copyright (c) 2000-2021 the FFmpeg developers
|
||||
built with gcc 10.3.0 (Rev5, Built by MSYS2 project)
|
||||
configuration: --disable-static --enable-shared --extra-version=Jellyfin --disable-ffplay --disable-debug --enable-gpl --enable-version3 --enable-bzlib --enable-iconv --enable-lzma --enable-zlib --enable-sdl2 --enable-fontconfig --enable-gmp --enable-libass --enable-libzimg --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libwebp --enable-libvpx --enable-libx264 --enable-libx265 --enable-libdav1d --enable-opencl --enable-dxva2 --enable-d3d11va --enable-amf --enable-libmfx --enable-cuda --enable-cuda-llvm --enable-cuvid --enable-nvenc --enable-nvdec --enable-ffnvcodec --enable-gnutls
|
||||
libavutil 56. 70.100 / 56. 70.100
|
||||
libavcodec 58.134.100 / 58.134.100
|
||||
libavformat 58. 76.100 / 58. 76.100
|
||||
libavdevice 58. 13.100 / 58. 13.100
|
||||
libavfilter 7.110.100 / 7.110.100
|
||||
libswscale 5. 9.100 / 5. 9.100
|
||||
libswresample 3. 9.100 / 3. 9.100
|
||||
libpostproc 55. 9.100 / 55. 9.100";
|
||||
|
||||
public const string FFmpegV432Output = @"ffmpeg version n4.3.2-Jellyfin Copyright (c) 2000-2021 the FFmpeg developers
|
||||
built with gcc 10.2.0 (Rev9, Built by MSYS2 project)
|
||||
configuration: --disable-static --enable-shared --cc='ccache gcc' --cxx='ccache g++' --extra-version=Jellyfin --disable-ffplay --disable-debug --enable-lto --enable-gpl --enable-version3 --enable-bzlib --enable-iconv --enable-lzma --enable-zlib --enable-sdl2 --enable-fontconfig --enable-gmp --enable-libass --enable-libzimg --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libwebp --enable-libvpx --enable-libx264 --enable-libx265 --enable-libdav1d --enable-opencl --enable-dxva2 --enable-d3d11va --enable-amf --enable-libmfx --enable-cuda --enable-cuda-llvm --enable-cuvid --enable-nvenc --enable-nvdec --enable-ffnvcodec --enable-gnutls
|
||||
libavutil 56. 51.100 / 56. 51.100
|
||||
libavcodec 58. 91.100 / 58. 91.100
|
||||
libavformat 58. 45.100 / 58. 45.100
|
||||
libavdevice 58. 10.100 / 58. 10.100
|
||||
libavfilter 7. 85.100 / 7. 85.100
|
||||
libswscale 5. 7.100 / 5. 7.100
|
||||
libswresample 3. 7.100 / 3. 7.100
|
||||
libpostproc 55. 7.100 / 55. 7.100";
|
||||
|
||||
public const string FFmpegV431Output = @"ffmpeg version n4.3.1 Copyright (c) 2000-2020 the FFmpeg developers
|
||||
built with gcc 10.1.0 (GCC)
|
||||
configuration: --prefix=/usr --disable-debug --disable-static --disable-stripping --enable-avisynth --enable-fontconfig --enable-gmp --enable-gnutls --enable-gpl --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libdav1d --enable-libdrm --enable-libfreetype --enable-libfribidi --enable-libgsm --enable-libiec61883 --enable-libjack --enable-libmfx --enable-libmodplug --enable-libmp3lame --enable-libopencore_amrnb --enable-libopencore_amrwb --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-librav1e --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libssh --enable-libtheora --enable-libv4l2 --enable-libvidstab --enable-libvmaf --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxcb --enable-libxml2 --enable-libxvid --enable-nvdec --enable-nvenc --enable-omx --enable-shared --enable-version3
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.MediaEncoding.Probing;
|
||||
using Xunit;
|
||||
|
||||
@@ -14,9 +14,10 @@ namespace Jellyfin.MediaEncoding.Tests
|
||||
public async Task Test(string fileName)
|
||||
{
|
||||
var path = Path.Join("Test Data", fileName);
|
||||
using (var stream = File.OpenRead(path))
|
||||
await using (var stream = File.OpenRead(path))
|
||||
{
|
||||
await JsonSerializer.DeserializeAsync<InternalMediaInfoResult>(stream, JsonDefaults.Options).ConfigureAwait(false);
|
||||
var res = await JsonSerializer.DeserializeAsync<InternalMediaInfoResult>(stream, JsonDefaults.Options).ConfigureAwait(false);
|
||||
Assert.NotNull(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Nullable>enable</Nullable>
|
||||
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -21,10 +18,10 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.0.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.MediaEncoding.Probing;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
@@ -17,9 +20,9 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
|
||||
[Fact]
|
||||
public void GetMediaInfo_MetaData_Success()
|
||||
{
|
||||
var bytes = File.ReadAllBytes("Test Data/Probing/some_matadata.json");
|
||||
var bytes = File.ReadAllBytes("Test Data/Probing/video_metadata.json");
|
||||
var internalMediaInfoResult = JsonSerializer.Deserialize<InternalMediaInfoResult>(bytes, _jsonOptions);
|
||||
MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, VideoType.VideoFile, false, "Test Data/Probing/some_matadata.mkv", MediaProtocol.File);
|
||||
MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, VideoType.VideoFile, false, "Test Data/Probing/video_metadata.mkv", MediaProtocol.File);
|
||||
|
||||
Assert.Single(res.MediaStreams);
|
||||
|
||||
@@ -52,5 +55,75 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
|
||||
Assert.Empty(res.Chapters);
|
||||
Assert.Equal("Just color bars", res.Overview);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetMediaInfo_MusicVideo_Success()
|
||||
{
|
||||
var bytes = File.ReadAllBytes("Test Data/Probing/music_video_metadata.json");
|
||||
var internalMediaInfoResult = JsonSerializer.Deserialize<InternalMediaInfoResult>(bytes, _jsonOptions);
|
||||
MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, VideoType.VideoFile, false, "Test Data/Probing/music_video.mkv", MediaProtocol.File);
|
||||
|
||||
Assert.Equal("The Title", res.Name);
|
||||
Assert.Equal("Title, The", res.ForcedSortName);
|
||||
Assert.Single(res.Artists);
|
||||
Assert.Equal("The Artist", res.Artists[0]);
|
||||
Assert.Equal("Album", res.Album);
|
||||
Assert.Equal(2021, res.ProductionYear);
|
||||
Assert.True(res.PremiereDate.HasValue);
|
||||
Assert.Equal(DateTime.Parse("2021-01-01T00:00Z", DateTimeFormatInfo.CurrentInfo).ToUniversalTime(), res.PremiereDate);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetMediaInfo_GivenOriginalDateContainsOnlyYear_Success()
|
||||
{
|
||||
var bytes = File.ReadAllBytes("Test Data/Probing/music_year_only_metadata.json");
|
||||
var internalMediaInfoResult = JsonSerializer.Deserialize<InternalMediaInfoResult>(bytes, _jsonOptions);
|
||||
MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, null, true, "Test Data/Probing/music.flac", MediaProtocol.File);
|
||||
|
||||
Assert.Equal("Baker Street", res.Name);
|
||||
Assert.Single(res.Artists);
|
||||
Assert.Equal("Gerry Rafferty", res.Artists[0]);
|
||||
Assert.Equal("City to City", res.Album);
|
||||
Assert.Equal(1978, res.ProductionYear);
|
||||
Assert.True(res.PremiereDate.HasValue);
|
||||
Assert.Equal(DateTime.Parse("1978-01-01T00:00Z", DateTimeFormatInfo.CurrentInfo).ToUniversalTime(), res.PremiereDate);
|
||||
Assert.Contains("Electronic", res.Genres);
|
||||
Assert.Contains("Ambient", res.Genres);
|
||||
Assert.Contains("Pop", res.Genres);
|
||||
Assert.Contains("Jazz", res.Genres);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetMediaInfo_Music_Success()
|
||||
{
|
||||
var bytes = File.ReadAllBytes("Test Data/Probing/music_metadata.json");
|
||||
var internalMediaInfoResult = JsonSerializer.Deserialize<InternalMediaInfoResult>(bytes, _jsonOptions);
|
||||
MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, null, true, "Test Data/Probing/music.flac", MediaProtocol.File);
|
||||
|
||||
Assert.Equal("UP NO MORE", res.Name);
|
||||
Assert.Single(res.Artists);
|
||||
Assert.Equal("TWICE", res.Artists[0]);
|
||||
Assert.Equal("Eyes wide open", res.Album);
|
||||
Assert.Equal(2020, res.ProductionYear);
|
||||
Assert.True(res.PremiereDate.HasValue);
|
||||
Assert.Equal(DateTime.Parse("2020-10-26T00:00Z", DateTimeFormatInfo.CurrentInfo).ToUniversalTime(), res.PremiereDate);
|
||||
Assert.Equal(22, res.People.Length);
|
||||
Assert.Equal("Krysta Youngs", res.People[0].Name);
|
||||
Assert.Equal(PersonType.Composer, res.People[0].Type);
|
||||
Assert.Equal("Julia Ross", res.People[1].Name);
|
||||
Assert.Equal(PersonType.Composer, res.People[1].Type);
|
||||
Assert.Equal("Yiwoomin", res.People[2].Name);
|
||||
Assert.Equal(PersonType.Composer, res.People[2].Type);
|
||||
Assert.Equal("Ji-hyo Park", res.People[3].Name);
|
||||
Assert.Equal(PersonType.Lyricist, res.People[3].Type);
|
||||
Assert.Equal("Yiwoomin", res.People[4].Name);
|
||||
Assert.Equal(PersonType.Actor, res.People[4].Type);
|
||||
Assert.Equal("Electric Piano", res.People[4].Role);
|
||||
Assert.Equal(4, res.Genres.Length);
|
||||
Assert.Contains("Electronic", res.Genres);
|
||||
Assert.Contains("Trance", res.Genres);
|
||||
Assert.Contains("Dance", res.Genres);
|
||||
Assert.Contains("Jazz", res.Genres);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
{
|
||||
"streams": [
|
||||
{
|
||||
"index": 0,
|
||||
"codec_name": "flac",
|
||||
"codec_long_name": "FLAC (Free Lossless Audio Codec)",
|
||||
"codec_type": "audio",
|
||||
"codec_tag_string": "[0][0][0][0]",
|
||||
"codec_tag": "0x0000",
|
||||
"sample_fmt": "s16",
|
||||
"sample_rate": "44100",
|
||||
"channels": 2,
|
||||
"channel_layout": "stereo",
|
||||
"bits_per_sample": 0,
|
||||
"r_frame_rate": "0/0",
|
||||
"avg_frame_rate": "0/0",
|
||||
"time_base": "1/44100",
|
||||
"start_pts": 0,
|
||||
"start_time": "0.000000",
|
||||
"duration_ts": 9447984,
|
||||
"duration": "214.240000",
|
||||
"bits_per_raw_sample": "16",
|
||||
"disposition": {
|
||||
"default": 0,
|
||||
"dub": 0,
|
||||
"original": 0,
|
||||
"comment": 0,
|
||||
"lyrics": 0,
|
||||
"karaoke": 0,
|
||||
"forced": 0,
|
||||
"hearing_impaired": 0,
|
||||
"visual_impaired": 0,
|
||||
"clean_effects": 0,
|
||||
"attached_pic": 0,
|
||||
"timed_thumbnails": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"codec_name": "mjpeg",
|
||||
"codec_long_name": "Motion JPEG",
|
||||
"profile": "Baseline",
|
||||
"codec_type": "video",
|
||||
"codec_tag_string": "[0][0][0][0]",
|
||||
"codec_tag": "0x0000",
|
||||
"width": 500,
|
||||
"height": 500,
|
||||
"coded_width": 500,
|
||||
"coded_height": 500,
|
||||
"closed_captions": 0,
|
||||
"has_b_frames": 0,
|
||||
"sample_aspect_ratio": "1:1",
|
||||
"display_aspect_ratio": "1:1",
|
||||
"pix_fmt": "yuvj420p",
|
||||
"level": -99,
|
||||
"color_range": "pc",
|
||||
"color_space": "bt470bg",
|
||||
"chroma_location": "center",
|
||||
"refs": 1,
|
||||
"r_frame_rate": "90000/1",
|
||||
"avg_frame_rate": "0/0",
|
||||
"time_base": "1/90000",
|
||||
"start_pts": 0,
|
||||
"start_time": "0.000000",
|
||||
"duration_ts": 19281600,
|
||||
"duration": "214.240000",
|
||||
"bits_per_raw_sample": "8",
|
||||
"disposition": {
|
||||
"default": 0,
|
||||
"dub": 0,
|
||||
"original": 0,
|
||||
"comment": 0,
|
||||
"lyrics": 0,
|
||||
"karaoke": 0,
|
||||
"forced": 0,
|
||||
"hearing_impaired": 0,
|
||||
"visual_impaired": 0,
|
||||
"clean_effects": 0,
|
||||
"attached_pic": 1,
|
||||
"timed_thumbnails": 0
|
||||
},
|
||||
"tags": {
|
||||
"comment": "Cover (front)"
|
||||
}
|
||||
}
|
||||
],
|
||||
"format": {
|
||||
"filename": "03 UP NO MORE.flac",
|
||||
"nb_streams": 2,
|
||||
"nb_programs": 0,
|
||||
"format_name": "flac",
|
||||
"format_long_name": "raw FLAC",
|
||||
"start_time": "0.000000",
|
||||
"duration": "214.240000",
|
||||
"size": "28714641",
|
||||
"bit_rate": "1072242",
|
||||
"probe_score": 100,
|
||||
"tags": {
|
||||
"MUSICBRAINZ_RELEASEGROUPID": "aa05ff10-8589-4c9c-a0d4-6b024f4e4556",
|
||||
"ORIGINALDATE": "2020-10-26",
|
||||
"ORIGINALYEAR": "2020",
|
||||
"RELEASETYPE": "album",
|
||||
"MUSICBRAINZ_ALBUMID": "222e6610-75c9-400e-8dc3-bb61f9fc5ca7",
|
||||
"SCRIPT": "Latn",
|
||||
"ALBUM": "Eyes wide open",
|
||||
"RELEASECOUNTRY": "JP",
|
||||
"BARCODE": "190295105280",
|
||||
"LABEL": "JYP Entertainment",
|
||||
"RELEASESTATUS": "official",
|
||||
"DATE": "2020-10-26",
|
||||
"MUSICBRAINZ_ALBUMARTISTID": "8da127cc-c432-418f-b356-ef36210d82ac",
|
||||
"album_artist": "TWICE",
|
||||
"ALBUMARTISTSORT": "TWICE",
|
||||
"TOTALDISCS": "1",
|
||||
"TOTALTRACKS": "13",
|
||||
"MEDIA": "Digital Media",
|
||||
"disc": "1",
|
||||
"MUSICBRAINZ_TRACKID": "7d1a1044-b564-480d-9df3-22f9656fdb97",
|
||||
"TITLE": "UP NO MORE",
|
||||
"ISRC": "US5TA2000136",
|
||||
"PERFORMER": "Yiwoomin (electric piano);Yiwoomin (synthesizer);Yiwoomin (bass);Yiwoomin (guitar);TWICE;Tzu-yu Chou (vocals);Momo Hirai (vocals);Na-yeon Im (vocals);Da-hyun Kim (vocals);Sana Minatozaki (vocals);Mina Myoui (vocals);Ji-hyo Park (vocals);Chae-young Son (vocals);Jeong-yeon Yoo (vocals);Perrie (background vocals)",
|
||||
"MIXER": "Bong Won Shin",
|
||||
"ARRANGER": "Krysta Youngs;Julia Ross;Yiwoomin",
|
||||
"MUSICBRAINZ_WORKID": "02b37083-0337-4721-9f17-bf31971043e8",
|
||||
"LANGUAGE": "kor;eng",
|
||||
"WORK": "Up No More",
|
||||
"COMPOSER": "Krysta Youngs;Julia Ross;Yiwoomin",
|
||||
"COMPOSERSORT": "Krysta Youngs;Ross, Julia;Yiwoomin",
|
||||
"LYRICIST": "Ji-hyo Park",
|
||||
"MUSICBRAINZ_ARTISTID": "8da127cc-c432-418f-b356-ef36210d82ac",
|
||||
"ARTIST": "TWICE",
|
||||
"ARTISTSORT": "TWICE",
|
||||
"ARTISTS": "TWICE",
|
||||
"MUSICBRAINZ_RELEASETRACKID": "ad49b840-da9e-4e7c-924b-29fdee187052",
|
||||
"track": "3",
|
||||
"GENRE": "Electronic;Trance;Dance;Jazz",
|
||||
"WEBSITE": "http://twice.jype.com/;http://www.twicejapan.com/",
|
||||
"ACOUSTID_ID": "aae2e972-108c-4d0c-8e31-9d078283e3dc",
|
||||
"MOOD": "Not acoustic;Not aggressive;Electronic;Happy;Party;Not relaxed;Not sad",
|
||||
"TRACKTOTAL": "13",
|
||||
"DISCTOTAL": "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"streams": [
|
||||
{
|
||||
"index": 0,
|
||||
"codec_name": "h264",
|
||||
"codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
|
||||
"profile": "High",
|
||||
"codec_type": "video",
|
||||
"codec_time_base": "1001/48000",
|
||||
"codec_tag_string": "[0][0][0][0]",
|
||||
"codec_tag": "0x0000",
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"coded_width": 1920,
|
||||
"coded_height": 1088,
|
||||
"closed_captions": 0,
|
||||
"has_b_frames": 0,
|
||||
"sample_aspect_ratio": "1:1",
|
||||
"display_aspect_ratio": "16:9",
|
||||
"pix_fmt": "yuv420p",
|
||||
"level": 42,
|
||||
"chroma_location": "left",
|
||||
"field_order": "progressive",
|
||||
"refs": 1,
|
||||
"is_avc": "true",
|
||||
"nal_length_size": "4",
|
||||
"r_frame_rate": "24000/1001",
|
||||
"avg_frame_rate": "24000/1001",
|
||||
"time_base": "1/1000",
|
||||
"start_pts": 0,
|
||||
"start_time": "0.000000",
|
||||
"bits_per_raw_sample": "8",
|
||||
"disposition": {
|
||||
"default": 1,
|
||||
"dub": 0,
|
||||
"original": 0,
|
||||
"comment": 0,
|
||||
"lyrics": 0,
|
||||
"karaoke": 0,
|
||||
"forced": 0,
|
||||
"hearing_impaired": 0,
|
||||
"visual_impaired": 0,
|
||||
"clean_effects": 0,
|
||||
"attached_pic": 0,
|
||||
"timed_thumbnails": 0
|
||||
},
|
||||
"tags": {
|
||||
"language": "eng"
|
||||
}
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"codec_name": "aac",
|
||||
"codec_long_name": "AAC (Advanced Audio Coding)",
|
||||
"profile": "LC",
|
||||
"codec_type": "audio",
|
||||
"codec_time_base": "1/48000",
|
||||
"codec_tag_string": "[0][0][0][0]",
|
||||
"codec_tag": "0x0000",
|
||||
"sample_fmt": "fltp",
|
||||
"sample_rate": "48000",
|
||||
"channels": 2,
|
||||
"channel_layout": "stereo",
|
||||
"bits_per_sample": 0,
|
||||
"r_frame_rate": "0/0",
|
||||
"avg_frame_rate": "0/0",
|
||||
"time_base": "1/1000",
|
||||
"start_pts": 0,
|
||||
"start_time": "0.000000",
|
||||
"disposition": {
|
||||
"default": 1,
|
||||
"dub": 0,
|
||||
"original": 0,
|
||||
"comment": 0,
|
||||
"lyrics": 0,
|
||||
"karaoke": 0,
|
||||
"forced": 0,
|
||||
"hearing_impaired": 0,
|
||||
"visual_impaired": 0,
|
||||
"clean_effects": 0,
|
||||
"attached_pic": 0,
|
||||
"timed_thumbnails": 0
|
||||
},
|
||||
"tags": {
|
||||
"language": "eng"
|
||||
}
|
||||
}
|
||||
],
|
||||
"chapters": [
|
||||
],
|
||||
"format": {
|
||||
"filename": "music_video.mkv",
|
||||
"nb_streams": 2,
|
||||
"nb_programs": 0,
|
||||
"format_name": "matroska,webm",
|
||||
"format_long_name": "Matroska / WebM",
|
||||
"start_time": "0.000000",
|
||||
"duration": "180.000000",
|
||||
"size": "500000000",
|
||||
"bit_rate": "22222222",
|
||||
"probe_score": 100,
|
||||
"tags": {
|
||||
"TITLE-eng": "The Title",
|
||||
"TITLESORT": "Title, The",
|
||||
"ARTIST": "The Artist",
|
||||
"ARTISTSORT": "Artist, The",
|
||||
"ALBUM": "Album",
|
||||
"DATE_RELEASED": "2021-01-01"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
{
|
||||
"streams": [
|
||||
{
|
||||
"index": 0,
|
||||
"codec_name": "flac",
|
||||
"codec_long_name": "FLAC (Free Lossless Audio Codec)",
|
||||
"codec_type": "audio",
|
||||
"codec_tag_string": "[0][0][0][0]",
|
||||
"codec_tag": "0x0000",
|
||||
"sample_fmt": "s16",
|
||||
"sample_rate": "44100",
|
||||
"channels": 2,
|
||||
"channel_layout": "stereo",
|
||||
"bits_per_sample": 0,
|
||||
"r_frame_rate": "0/0",
|
||||
"avg_frame_rate": "0/0",
|
||||
"time_base": "1/44100",
|
||||
"start_pts": 0,
|
||||
"start_time": "0.000000",
|
||||
"duration_ts": 16394616,
|
||||
"duration": "371.760000",
|
||||
"bits_per_raw_sample": "16",
|
||||
"disposition": {
|
||||
"default": 0,
|
||||
"dub": 0,
|
||||
"original": 0,
|
||||
"comment": 0,
|
||||
"lyrics": 0,
|
||||
"karaoke": 0,
|
||||
"forced": 0,
|
||||
"hearing_impaired": 0,
|
||||
"visual_impaired": 0,
|
||||
"clean_effects": 0,
|
||||
"attached_pic": 0,
|
||||
"timed_thumbnails": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"codec_name": "mjpeg",
|
||||
"codec_long_name": "Motion JPEG",
|
||||
"profile": "Baseline",
|
||||
"codec_type": "video",
|
||||
"codec_tag_string": "[0][0][0][0]",
|
||||
"codec_tag": "0x0000",
|
||||
"width": 500,
|
||||
"height": 498,
|
||||
"coded_width": 500,
|
||||
"coded_height": 498,
|
||||
"closed_captions": 0,
|
||||
"has_b_frames": 0,
|
||||
"sample_aspect_ratio": "1:1",
|
||||
"display_aspect_ratio": "250:249",
|
||||
"pix_fmt": "yuvj420p",
|
||||
"level": -99,
|
||||
"color_range": "pc",
|
||||
"color_space": "bt470bg",
|
||||
"chroma_location": "center",
|
||||
"refs": 1,
|
||||
"r_frame_rate": "90000/1",
|
||||
"avg_frame_rate": "0/0",
|
||||
"time_base": "1/90000",
|
||||
"start_pts": 0,
|
||||
"start_time": "0.000000",
|
||||
"duration_ts": 33458400,
|
||||
"duration": "371.760000",
|
||||
"bits_per_raw_sample": "8",
|
||||
"disposition": {
|
||||
"default": 0,
|
||||
"dub": 0,
|
||||
"original": 0,
|
||||
"comment": 0,
|
||||
"lyrics": 0,
|
||||
"karaoke": 0,
|
||||
"forced": 0,
|
||||
"hearing_impaired": 0,
|
||||
"visual_impaired": 0,
|
||||
"clean_effects": 0,
|
||||
"attached_pic": 1,
|
||||
"timed_thumbnails": 0
|
||||
},
|
||||
"tags": {
|
||||
"comment": "Cover (front)"
|
||||
}
|
||||
}
|
||||
],
|
||||
"format": {
|
||||
"filename": "02 Baker Street.flac",
|
||||
"nb_streams": 2,
|
||||
"nb_programs": 0,
|
||||
"format_name": "flac",
|
||||
"format_long_name": "raw FLAC",
|
||||
"start_time": "0.000000",
|
||||
"duration": "371.760000",
|
||||
"size": "37072649",
|
||||
"bit_rate": "797775",
|
||||
"probe_score": 100,
|
||||
"tags": {
|
||||
"MUSICBRAINZ_RELEASEGROUPID": "238c3fb4-5792-342b-b217-02f66298b424",
|
||||
"ORIGINALDATE": "1978",
|
||||
"ORIGINALYEAR": "1978",
|
||||
"RELEASETYPE": "album",
|
||||
"MUSICBRAINZ_ALBUMID": "30156786-e511-3106-ac95-66f0e880b24b",
|
||||
"ASIN": "B000007O5H",
|
||||
"MUSICBRAINZ_ALBUMARTISTID": "563201cb-721c-4cfb-acca-c1ba69e3d1fb",
|
||||
"album_artist": "Gerry Rafferty",
|
||||
"ALBUMARTISTSORT": "Rafferty, Gerry",
|
||||
"LABEL": "Liberty EMI Records UK",
|
||||
"CATALOGNUMBER": "CDP 7 46049 2",
|
||||
"DATE": "1989-07-26",
|
||||
"RELEASECOUNTRY": "GB",
|
||||
"BARCODE": "077774604925",
|
||||
"ALBUM": "City to City",
|
||||
"SCRIPT": "Latn",
|
||||
"RELEASESTATUS": "official",
|
||||
"TOTALDISCS": "1",
|
||||
"disc": "1",
|
||||
"MEDIA": "CD",
|
||||
"TOTALTRACKS": "10",
|
||||
"MUSICBRAINZ_TRACKID": "9235e22e-afbd-48f7-b329-21dae6da2810",
|
||||
"ISRC": "GBAYE1100924;GBAYE7800619",
|
||||
"PERFORMER": "Hugh Burns (electric guitar);Nigel Jenkins (electric guitar);Tommy Eyre (keyboard and Moog);Glen LeFleur (percussion);Raphael Ravenscroft (saxophone);Henry Spinetti (drums (drum set));Gary Taylor (bass);Gerry Rafferty (lead vocals)",
|
||||
"ARRANGER": "Graham Preskett",
|
||||
"MIXER": "Declan O’Doherty",
|
||||
"PRODUCER": "Hugh Murphy;Gerry Rafferty",
|
||||
"MUSICBRAINZ_WORKID": "a9eb3c45-784c-3c32-860c-4b406f03961b",
|
||||
"LANGUAGE": "eng",
|
||||
"WORK": "Baker Street",
|
||||
"COMPOSER": "Gerry Rafferty",
|
||||
"COMPOSERSORT": "Rafferty, Gerry",
|
||||
"LYRICIST": "Gerry Rafferty",
|
||||
"TITLE": "Baker Street",
|
||||
"MUSICBRAINZ_ARTISTID": "563201cb-721c-4cfb-acca-c1ba69e3d1fb",
|
||||
"ARTIST": "Gerry Rafferty",
|
||||
"ARTISTSORT": "Rafferty, Gerry",
|
||||
"ARTISTS": "Gerry Rafferty",
|
||||
"MUSICBRAINZ_RELEASETRACKID": "407cf7f7-440d-3e76-8b89-8686198868ea",
|
||||
"track": "2",
|
||||
"GENRE": "Electronic;Ambient;Pop;Jazz",
|
||||
"WEBSITE": "http://www.gerryrafferty.com/",
|
||||
"ACOUSTID_ID": "68f8d979-a659-4aa0-a216-eb3721a951eb",
|
||||
"MOOD": "Acoustic;Not aggressive;Not electronic;Not happy;Party;Relaxed;Not sad",
|
||||
"TRACKTOTAL": "10",
|
||||
"DISCTOTAL": "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
19
tests/Jellyfin.Model.Tests/Dlna/ContainerProfileTests.cs
Normal file
19
tests/Jellyfin.Model.Tests/Dlna/ContainerProfileTests.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Model.Tests.Dlna
|
||||
{
|
||||
public class ContainerProfileTests
|
||||
{
|
||||
private readonly ContainerProfile _emptyContainerProfile = new ContainerProfile();
|
||||
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
[InlineData("mp4")]
|
||||
public void ContainsContainer_EmptyContainerProfile_True(string? containers)
|
||||
{
|
||||
Assert.True(_emptyContainerProfile.ContainsContainer(containers));
|
||||
}
|
||||
}
|
||||
}
|
||||
156
tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs
Normal file
156
tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Model.Tests.Entities
|
||||
{
|
||||
public class MediaStreamTests
|
||||
{
|
||||
public static IEnumerable<object[]> Get_DisplayTitle_TestData()
|
||||
{
|
||||
return new List<object[]>
|
||||
{
|
||||
new object[]
|
||||
{
|
||||
new MediaStream
|
||||
{
|
||||
Type = MediaStreamType.Subtitle,
|
||||
Title = "English",
|
||||
Language = string.Empty,
|
||||
IsForced = false,
|
||||
IsDefault = false,
|
||||
Codec = "ASS"
|
||||
},
|
||||
"English - Und - ASS"
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new MediaStream
|
||||
{
|
||||
Type = MediaStreamType.Subtitle,
|
||||
Title = "English",
|
||||
Language = string.Empty,
|
||||
IsForced = false,
|
||||
IsDefault = false,
|
||||
Codec = string.Empty
|
||||
},
|
||||
"English - Und"
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new MediaStream
|
||||
{
|
||||
Type = MediaStreamType.Subtitle,
|
||||
Title = "English",
|
||||
Language = "EN",
|
||||
IsForced = false,
|
||||
IsDefault = false,
|
||||
Codec = string.Empty
|
||||
},
|
||||
"English"
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new MediaStream
|
||||
{
|
||||
Type = MediaStreamType.Subtitle,
|
||||
Title = "English",
|
||||
Language = "EN",
|
||||
IsForced = true,
|
||||
IsDefault = true,
|
||||
Codec = "SRT"
|
||||
},
|
||||
"English - Default - Forced - SRT"
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new MediaStream
|
||||
{
|
||||
Type = MediaStreamType.Subtitle,
|
||||
Title = null,
|
||||
Language = null,
|
||||
IsForced = false,
|
||||
IsDefault = false,
|
||||
Codec = null
|
||||
},
|
||||
"Und"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(Get_DisplayTitle_TestData))]
|
||||
public void Get_DisplayTitle_should_return_valid_title(MediaStream mediaStream, string expected)
|
||||
{
|
||||
Assert.Equal(expected, mediaStream.DisplayTitle);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, null, false, null)]
|
||||
[InlineData(null, 0, false, null)]
|
||||
[InlineData(0, null, false, null)]
|
||||
[InlineData(640, 480, false, "480p")]
|
||||
[InlineData(640, 480, true, "480i")]
|
||||
[InlineData(720, 576, false, "576p")]
|
||||
[InlineData(720, 576, true, "576i")]
|
||||
[InlineData(960, 540, false, "540p")]
|
||||
[InlineData(960, 540, true, "540i")]
|
||||
[InlineData(1280, 720, false, "720p")]
|
||||
[InlineData(1280, 720, true, "720i")]
|
||||
[InlineData(1920, 1080, false, "1080p")]
|
||||
[InlineData(1920, 1080, true, "1080i")]
|
||||
[InlineData(4096, 3072, false, "4K")]
|
||||
[InlineData(8192, 6144, false, "8K")]
|
||||
[InlineData(512, 384, false, "480p")]
|
||||
[InlineData(576, 336, false, "480p")]
|
||||
[InlineData(624, 352, false, "480p")]
|
||||
[InlineData(640, 352, false, "480p")]
|
||||
[InlineData(704, 396, false, "480p")]
|
||||
[InlineData(720, 404, false, "480p")]
|
||||
[InlineData(720, 480, false, "480p")]
|
||||
[InlineData(768, 576, false, "576p")]
|
||||
[InlineData(960, 720, false, "720p")]
|
||||
[InlineData(1280, 528, false, "720p")]
|
||||
[InlineData(1280, 532, false, "720p")]
|
||||
[InlineData(1280, 534, false, "720p")]
|
||||
[InlineData(1280, 536, false, "720p")]
|
||||
[InlineData(1280, 544, false, "720p")]
|
||||
[InlineData(1280, 690, false, "720p")]
|
||||
[InlineData(1280, 694, false, "720p")]
|
||||
[InlineData(1280, 696, false, "720p")]
|
||||
[InlineData(1280, 716, false, "720p")]
|
||||
[InlineData(1280, 718, false, "720p")]
|
||||
[InlineData(1912, 792, false, "1080p")]
|
||||
[InlineData(1916, 1076, false, "1080p")]
|
||||
[InlineData(1918, 1080, false, "1080p")]
|
||||
[InlineData(1920, 796, false, "1080p")]
|
||||
[InlineData(1920, 800, false, "1080p")]
|
||||
[InlineData(1920, 802, false, "1080p")]
|
||||
[InlineData(1920, 804, false, "1080p")]
|
||||
[InlineData(1920, 808, false, "1080p")]
|
||||
[InlineData(1920, 816, false, "1080p")]
|
||||
[InlineData(1920, 856, false, "1080p")]
|
||||
[InlineData(1920, 960, false, "1080p")]
|
||||
[InlineData(1920, 1024, false, "1080p")]
|
||||
[InlineData(1920, 1040, false, "1080p")]
|
||||
[InlineData(1920, 1072, false, "1080p")]
|
||||
[InlineData(1440, 1072, false, "1080p")]
|
||||
[InlineData(1440, 1080, false, "1080p")]
|
||||
[InlineData(3840, 1600, false, "4K")]
|
||||
[InlineData(3840, 1606, false, "4K")]
|
||||
[InlineData(3840, 1608, false, "4K")]
|
||||
[InlineData(3840, 2160, false, "4K")]
|
||||
[InlineData(7680, 4320, false, "8K")]
|
||||
public void GetResolutionText_Valid(int? width, int? height, bool interlaced, string expected)
|
||||
{
|
||||
var mediaStream = new MediaStream()
|
||||
{
|
||||
Width = width,
|
||||
Height = height,
|
||||
IsInterlaced = interlaced
|
||||
};
|
||||
|
||||
Assert.Equal(expected, mediaStream.GetResolutionText());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
using System;
|
||||
using FsCheck;
|
||||
using FsCheck.Xunit;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
using Xunit;
|
||||
|
||||
@@ -10,9 +13,20 @@ namespace Jellyfin.Model.Tests.Extensions
|
||||
[InlineData("banana", "Banana")]
|
||||
[InlineData("Banana", "Banana")]
|
||||
[InlineData("ä", "Ä")]
|
||||
[InlineData("\027", "\027")]
|
||||
public void StringHelper_ValidArgs_Success(string input, string expectedResult)
|
||||
{
|
||||
Assert.Equal(expectedResult, StringHelper.FirstToUpper(input));
|
||||
}
|
||||
|
||||
[Property]
|
||||
public Property FirstToUpper_RandomArg_Correct(NonEmptyString input)
|
||||
{
|
||||
var result = StringHelper.FirstToUpper(input.Item);
|
||||
|
||||
// We check IsLower instead of IsUpper because both return false for non-letters
|
||||
return (!char.IsLower(result[0])).Label("First char is uppercase")
|
||||
.And(input.Item.Length == 1 || result[1..].Equals(input.Item[1..], StringComparison.Ordinal)).Label("Remaining chars are unmodified");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,17 +3,15 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Nullable>enable</Nullable>
|
||||
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.0.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0" />
|
||||
<PackageReference Include="FsCheck.Xunit" Version="2.16.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
|
||||
@@ -8,17 +8,14 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Nullable>enable</Nullable>
|
||||
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.0.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -70,7 +70,8 @@ namespace Jellyfin.Naming.Tests.TV
|
||||
[InlineData("Log Horizon 2/[HorribleSubs] Log Horizon 2 - 03 [720p].mkv", 3)] // digit in series name
|
||||
[InlineData("Season 1/seriesname 05.mkv", 5)] // no hyphen between series name and episode number
|
||||
[InlineData("[BBT-RMX] Ranma ½ - 154 [50AC421A].mkv", 154)] // hyphens in the pre-name info, triple digit episode number
|
||||
// TODO: [InlineData("Case Closed (1996-2007)/Case Closed - 317.mkv", 317)] // triple digit episode number
|
||||
[InlineData("Season 2/Episode 21 - 94 Meetings.mp4", 21)] // Title starts with a number
|
||||
// [InlineData("Case Closed (1996-2007)/Case Closed - 317.mkv", 317)] // triple digit episode number
|
||||
// TODO: [InlineData("Season 2/16 12 Some Title.avi", 16)]
|
||||
// TODO: [InlineData("/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv/The.Legend.of.Condor.Heroes.2017.E07.V2.web-dl.1080p.h264.aac-hdctv.mkv", 7)]
|
||||
// TODO: [InlineData("Season 4/Uchuu.Senkan.Yamato.2199.E03.avi", 3)]
|
||||
|
||||
@@ -21,7 +21,8 @@ namespace Jellyfin.Naming.Tests.TV
|
||||
[InlineData("[Baz-Bar]Foo - [1080p][Multiple Subtitle]/[Baz-Bar] Foo - 05 [1080p][Multiple Subtitle].mkv", "Foo", null, 5)]
|
||||
[InlineData(@"/Foo/The.Series.Name.S01E04.WEBRip.x264-Baz[Bar]/the.series.name.s01e04.webrip.x264-Baz[Bar].mkv", "The.Series.Name", 1, 4)]
|
||||
[InlineData(@"Love.Death.and.Robots.S01.1080p.NF.WEB-DL.DDP5.1.x264-NTG/Love.Death.and.Robots.S01E01.Sonnies.Edge.1080p.NF.WEB-DL.DDP5.1.x264-NTG.mkv", "Love.Death.and.Robots", 1, 1)]
|
||||
// TODO: [InlineData("[Baz-Bar]Foo - 01 - 12[1080p][Multiple Subtitle]/[Baz-Bar] Foo - 05 [1080p][Multiple Subtitle].mkv", "Foo", null, 5)]
|
||||
[InlineData("[YuiSubs] Tensura Nikki - Tensei Shitara Slime Datta Ken/[YuiSubs] Tensura Nikki - Tensei Shitara Slime Datta Ken - 12 (NVENC H.265 1080p).mkv", "Tensura Nikki - Tensei Shitara Slime Datta Ken", null, 12)]
|
||||
[InlineData("[Baz-Bar]Foo - 01 - 12[1080p][Multiple Subtitle]/[Baz-Bar] Foo - 05 [1080p][Multiple Subtitle].mkv", "Foo", null, 5)]
|
||||
// TODO: [InlineData("E:\\Anime\\Yahari Ore no Seishun Love Comedy wa Machigatteiru\\Yahari Ore no Seishun Love Comedy wa Machigatteiru. Zoku\\Oregairu Zoku 11 - Hayama Hayato Always Renconds to Everyone's Expectations..mkv", "Yahari Ore no Seishun Love Comedy wa Machigatteiru", null, 11)]
|
||||
// TODO: [InlineData(@"/Library/Series/The Grand Tour (2016)/Season 1/S01E01 The Holy Trinity.mkv", "The Grand Tour", 1, 1)]
|
||||
public void TestSimple(string path, string seriesName, int? seasonNumber, int? episodeNumber)
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
{
|
||||
input = Path.GetFileName(input);
|
||||
|
||||
var result = new VideoResolver(_namingOptions).CleanDateTime(input);
|
||||
var result = VideoResolver.CleanDateTime(input, _namingOptions);
|
||||
|
||||
Assert.Equal(expectedName, result.Name, true);
|
||||
Assert.Equal(expectedYear, result.Year);
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
{
|
||||
public sealed class CleanStringTests
|
||||
{
|
||||
private readonly VideoResolver _videoResolver = new VideoResolver(new NamingOptions());
|
||||
private readonly NamingOptions _namingOptions = new NamingOptions();
|
||||
|
||||
[Theory]
|
||||
[InlineData("Super movie 480p.mp4", "Super movie")]
|
||||
@@ -26,7 +26,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
// FIXME: [InlineData("After The Sunset - [0004].mkv", "After The Sunset")]
|
||||
public void CleanStringTest_NeedsCleaning_Success(string input, string expectedName)
|
||||
{
|
||||
Assert.True(_videoResolver.TryCleanString(input, out ReadOnlySpan<char> newName));
|
||||
Assert.True(VideoResolver.TryCleanString(input, _namingOptions, out ReadOnlySpan<char> newName));
|
||||
// TODO: compare spans when XUnit supports it
|
||||
Assert.Equal(expectedName, newName.ToString());
|
||||
}
|
||||
@@ -41,7 +41,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
[InlineData("Run lola run (lola rennt) (2009).mp4")]
|
||||
public void CleanStringTest_DoesntNeedCleaning_False(string? input)
|
||||
{
|
||||
Assert.False(_videoResolver.TryCleanString(input, out ReadOnlySpan<char> newName));
|
||||
Assert.False(VideoResolver.TryCleanString(input, _namingOptions, out ReadOnlySpan<char> newName));
|
||||
Assert.True(newName.IsEmpty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,13 +104,6 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
Assert.Equal(rule, res.Rule);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestFlagsParser()
|
||||
{
|
||||
var flags = new FlagParser(_videoOptions).GetFlags(string.Empty);
|
||||
Assert.Empty(flags);
|
||||
}
|
||||
|
||||
private ExtraResolver GetExtraTypeParser(NamingOptions videoOptions)
|
||||
{
|
||||
return new ExtraResolver(videoOptions);
|
||||
|
||||
@@ -22,8 +22,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
[Fact]
|
||||
public void Test3DName()
|
||||
{
|
||||
var result =
|
||||
new VideoResolver(_namingOptions).ResolveFile(@"C:/Users/media/Desktop/Video Test/Movies/Oblivion/Oblivion.3d.hsbs.mkv");
|
||||
var result = VideoResolver.ResolveFile(@"C:/Users/media/Desktop/Video Test/Movies/Oblivion/Oblivion.3d.hsbs.mkv", _namingOptions);
|
||||
|
||||
Assert.Equal("hsbs", result?.Format3D);
|
||||
Assert.Equal("Oblivion", result?.Name);
|
||||
@@ -58,15 +57,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
|
||||
private void Test(string input, bool is3D, string? format3D)
|
||||
{
|
||||
var parser = new Format3DParser(_namingOptions);
|
||||
|
||||
var result = parser.Parse(input);
|
||||
var result = Format3DParser.Parse(input, _namingOptions);
|
||||
|
||||
Assert.Equal(is3D, result.Is3D);
|
||||
|
||||
if (format3D == null)
|
||||
{
|
||||
Assert.Null(result.Format3D);
|
||||
Assert.Null(result?.Format3D);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
{
|
||||
public class MultiVersionTests
|
||||
{
|
||||
private readonly VideoListResolver _videoListResolver = new VideoListResolver(new NamingOptions());
|
||||
private readonly NamingOptions _namingOptions = new NamingOptions();
|
||||
|
||||
[Fact]
|
||||
public void TestMultiEdition1()
|
||||
@@ -22,11 +22,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"/movies/X-Men Days of Future Past/X-Men Days of Future Past [hsbs].mkv"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Single(result[0].Extras);
|
||||
@@ -43,11 +45,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"/movies/X-Men Days of Future Past/X-Men Days of Future Past [banana].mp4"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Single(result[0].Extras);
|
||||
@@ -63,11 +67,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"/movies/The Phantom of the Opera (1925)/The Phantom of the Opera (1925) - 1929 version.mkv"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Single(result[0].AlternateVersions);
|
||||
@@ -87,11 +93,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"/movies/M/Movie 7.mkv"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(7, result.Count);
|
||||
Assert.Empty(result[0].Extras);
|
||||
@@ -113,11 +121,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"/movies/Movie/Movie-8.mkv"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Empty(result[0].Extras);
|
||||
@@ -140,11 +150,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"/movies/Mo/Movie 9.mkv"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(9, result.Count);
|
||||
Assert.Empty(result[0].Extras);
|
||||
@@ -163,11 +175,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"/movies/Movie/Movie 5.mkv"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(5, result.Count);
|
||||
Assert.Empty(result[0].Extras);
|
||||
@@ -188,11 +202,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"/movies/Iron Man/Iron Man (2011).mkv"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(5, result.Count);
|
||||
Assert.Empty(result[0].Extras);
|
||||
@@ -214,11 +230,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"/movies/Iron Man/Iron Man[test].mkv",
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Empty(result[0].Extras);
|
||||
@@ -243,11 +261,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"/movies/Iron Man/Iron Man [test].mkv"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Empty(result[0].Extras);
|
||||
@@ -266,11 +286,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"/movies/Iron Man/Iron Man - C (2007).mkv"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
}
|
||||
@@ -289,11 +311,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"/movies/Iron Man/Iron Man_3d.hsbs.mkv"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(7, result.Count);
|
||||
Assert.Empty(result[0].Extras);
|
||||
@@ -314,11 +338,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"/movies/Iron Man/Iron Man (2011).mkv"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(5, result.Count);
|
||||
Assert.Empty(result[0].Extras);
|
||||
@@ -334,11 +360,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"/movies/Blade Runner (1982)/Blade Runner (1982) [EE by ADM] [480p HEVC AAC,AAC,AAC].mkv"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Empty(result[0].Extras);
|
||||
@@ -354,11 +382,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) [2160p] Blu-ray.x265.AAC.mkv"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Empty(result[0].Extras);
|
||||
@@ -374,11 +404,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"/movies/John Wick - Kapitel 3 (2019) [imdbid=tt6146586]/John Wick - Kapitel 3 (2019) [imdbid=tt6146586] - Version 2.mkv"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Empty(result[0].Extras);
|
||||
@@ -394,11 +426,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"/movies/John Wick - Chapter 3 (2019)/John Wick - Chapter 3 (2019) [Version 2.mkv"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
}
|
||||
@@ -406,7 +440,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
[Fact]
|
||||
public void TestEmptyList()
|
||||
{
|
||||
var result = _videoListResolver.Resolve(new List<FileSystemMetadata>()).ToList();
|
||||
var result = VideoListResolver.Resolve(new List<FileSystemMetadata>(), _namingOptions).ToList();
|
||||
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
@@ -29,8 +29,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
[Fact]
|
||||
public void TestStubName()
|
||||
{
|
||||
var result =
|
||||
new VideoResolver(_namingOptions).ResolveFile(@"C:/Users/media/Desktop/Video Test/Movies/Oblivion/Oblivion.dvd.disc");
|
||||
var result = VideoResolver.ResolveFile(@"C:/Users/media/Desktop/Video Test/Movies/Oblivion/Oblivion.dvd.disc", _namingOptions);
|
||||
|
||||
Assert.Equal("Oblivion", result?.Name);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
{
|
||||
public class VideoListResolverTests
|
||||
{
|
||||
private readonly VideoListResolver _videoListResolver = new VideoListResolver(new NamingOptions());
|
||||
private readonly NamingOptions _namingOptions = new NamingOptions();
|
||||
|
||||
[Fact]
|
||||
public void TestStackAndExtras()
|
||||
@@ -40,11 +40,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"WillyWonka-trailer.mkv"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(5, result.Count);
|
||||
var batman = result.FirstOrDefault(x => string.Equals(x.Name, "Batman", StringComparison.Ordinal));
|
||||
@@ -67,11 +69,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"300.nfo"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
}
|
||||
@@ -85,11 +89,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"300 trailer.mkv"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
}
|
||||
@@ -103,11 +109,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"X-Men Days of Future Past-trailer.mp4"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
}
|
||||
@@ -122,11 +130,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"X-Men Days of Future Past-trailer2.mp4"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
}
|
||||
@@ -140,11 +150,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"Looper.2012.bluray.720p.x264.mkv"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
}
|
||||
@@ -162,11 +174,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"My video 5.mkv"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(5, result.Count);
|
||||
}
|
||||
@@ -180,11 +194,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"M:/Movies (DVD)/Movies (Musical)/Sound of Music (1965)/Sound of Music Disc 2"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = true,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = true,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
}
|
||||
@@ -199,11 +215,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"My movie #2.mp4"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = true,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = true,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
}
|
||||
@@ -218,11 +236,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"No (2012) part1-trailer.mp4"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
}
|
||||
@@ -237,11 +257,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"No (2012)-trailer.mp4"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
}
|
||||
@@ -257,11 +279,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"trailer.mp4"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
}
|
||||
@@ -277,11 +301,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"/MCFAMILY-PC/Private3$/Heterosexual/Breast In Class 2 Counterfeit Racks (2011)/Breast In Class 2 Disc 2 cd2.avi"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
}
|
||||
@@ -294,11 +320,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"/nas-markrobbo78/Videos/INDEX HTPC/Movies/Watched/3 - ACTION/Argo (2012)/movie.mkv"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
}
|
||||
@@ -311,11 +339,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"The Colony.mkv"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
}
|
||||
@@ -329,11 +359,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"Four Sisters and a Wedding - B.avi"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
}
|
||||
@@ -347,11 +379,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"Four Rooms - A.mp4"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
}
|
||||
@@ -365,11 +399,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"/Server/Despicable Me/movie-trailer.mkv"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
}
|
||||
@@ -385,11 +421,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"/Server/Despicable Me/Baywatch (2017) - Trailer.mkv"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(4, result.Count);
|
||||
}
|
||||
@@ -403,11 +441,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"/Movies/Despicable Me/trailers/trailer.mkv"
|
||||
};
|
||||
|
||||
var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList()).ToList();
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
{
|
||||
public class VideoResolverTests
|
||||
{
|
||||
private readonly VideoResolver _videoResolver = new VideoResolver(new NamingOptions());
|
||||
private static NamingOptions _namingOptions = new NamingOptions();
|
||||
|
||||
public static IEnumerable<object[]> ResolveFile_ValidFileNameTestData()
|
||||
{
|
||||
@@ -148,7 +148,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
yield return new object[]
|
||||
{
|
||||
new VideoFileInfo(
|
||||
path: @"/server/Movies/Rain Man 1988 REMASTERED 1080p BluRay x264 AAC - Ozlem/Rain Man 1988 REMASTERED 1080p BluRay x264 AAC - Ozlem.mp4",
|
||||
path: @"/server/Movies/Rain Man 1988 REMASTERED 1080p BluRay x264 AAC - JEFF/Rain Man 1988 REMASTERED 1080p BluRay x264 AAC - JEFF.mp4",
|
||||
container: "mp4",
|
||||
name: "Rain Man",
|
||||
year: 1988)
|
||||
@@ -159,27 +159,27 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
[MemberData(nameof(ResolveFile_ValidFileNameTestData))]
|
||||
public void ResolveFile_ValidFileName_Success(VideoFileInfo expectedResult)
|
||||
{
|
||||
var result = _videoResolver.ResolveFile(expectedResult.Path);
|
||||
var result = VideoResolver.ResolveFile(expectedResult.Path, _namingOptions);
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(result?.Path, expectedResult.Path);
|
||||
Assert.Equal(result?.Container, expectedResult.Container);
|
||||
Assert.Equal(result?.Name, expectedResult.Name);
|
||||
Assert.Equal(result?.Year, expectedResult.Year);
|
||||
Assert.Equal(result?.ExtraType, expectedResult.ExtraType);
|
||||
Assert.Equal(result?.Format3D, expectedResult.Format3D);
|
||||
Assert.Equal(result?.Is3D, expectedResult.Is3D);
|
||||
Assert.Equal(result?.IsStub, expectedResult.IsStub);
|
||||
Assert.Equal(result?.StubType, expectedResult.StubType);
|
||||
Assert.Equal(result?.IsDirectory, expectedResult.IsDirectory);
|
||||
Assert.Equal(result?.FileNameWithoutExtension, expectedResult.FileNameWithoutExtension);
|
||||
Assert.Equal(result?.ToString(), expectedResult.ToString());
|
||||
Assert.Equal(result!.Path, expectedResult.Path);
|
||||
Assert.Equal(result.Container, expectedResult.Container);
|
||||
Assert.Equal(result.Name, expectedResult.Name);
|
||||
Assert.Equal(result.Year, expectedResult.Year);
|
||||
Assert.Equal(result.ExtraType, expectedResult.ExtraType);
|
||||
Assert.Equal(result.Format3D, expectedResult.Format3D);
|
||||
Assert.Equal(result.Is3D, expectedResult.Is3D);
|
||||
Assert.Equal(result.IsStub, expectedResult.IsStub);
|
||||
Assert.Equal(result.StubType, expectedResult.StubType);
|
||||
Assert.Equal(result.IsDirectory, expectedResult.IsDirectory);
|
||||
Assert.Equal(result.FileNameWithoutExtension.ToString(), expectedResult.FileNameWithoutExtension.ToString());
|
||||
Assert.Equal(result.ToString(), expectedResult.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolveFile_EmptyPath()
|
||||
{
|
||||
var result = _videoResolver.ResolveFile(string.Empty);
|
||||
var result = VideoResolver.ResolveFile(string.Empty, _namingOptions);
|
||||
|
||||
Assert.Null(result);
|
||||
}
|
||||
@@ -194,12 +194,16 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
string.Empty
|
||||
};
|
||||
|
||||
var results = paths.Select(path => _videoResolver.ResolveDirectory(path)).ToList();
|
||||
var results = paths.Select(path => VideoResolver.ResolveDirectory(path, _namingOptions)).ToList();
|
||||
|
||||
Assert.Equal(3, results.Count);
|
||||
Assert.NotNull(results[0]);
|
||||
Assert.NotNull(results[1]);
|
||||
Assert.Null(results[2]);
|
||||
foreach (var result in results)
|
||||
{
|
||||
Assert.Null(result?.Container);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
53
tests/Jellyfin.Networking.Tests/IPHostTests.cs
Normal file
53
tests/Jellyfin.Networking.Tests/IPHostTests.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using FsCheck;
|
||||
using FsCheck.Xunit;
|
||||
using MediaBrowser.Common.Net;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Networking.Tests
|
||||
{
|
||||
public static class IPHostTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks IP address formats.
|
||||
/// </summary>
|
||||
/// <param name="address">IP Address.</param>
|
||||
[Theory]
|
||||
[InlineData("127.0.0.1")]
|
||||
[InlineData("127.0.0.1:123")]
|
||||
[InlineData("localhost")]
|
||||
[InlineData("localhost:1345")]
|
||||
[InlineData("www.google.co.uk")]
|
||||
[InlineData("fd23:184f:2029:0:3139:7386:67d7:d517")]
|
||||
[InlineData("fd23:184f:2029:0:3139:7386:67d7:d517/56")]
|
||||
[InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517]:124")]
|
||||
[InlineData("fe80::7add:12ff:febb:c67b%16")]
|
||||
[InlineData("[fe80::7add:12ff:febb:c67b%16]:123")]
|
||||
[InlineData("fe80::7add:12ff:febb:c67b%16:123")]
|
||||
[InlineData("[fe80::7add:12ff:febb:c67b%16]")]
|
||||
[InlineData("192.168.1.2/255.255.255.0")]
|
||||
[InlineData("192.168.1.2/24")]
|
||||
public static void TryParse_ValidHostStrings_True(string address)
|
||||
=> Assert.True(IPHost.TryParse(address, out _));
|
||||
|
||||
[Property]
|
||||
public static Property TryParse_IPv4Address_True(IPv4Address address)
|
||||
=> IPHost.TryParse(address.Item.ToString(), out _).ToProperty();
|
||||
|
||||
[Property]
|
||||
public static Property TryParse_IPv6Address_True(IPv6Address address)
|
||||
=> IPHost.TryParse(address.Item.ToString(), out _).ToProperty();
|
||||
|
||||
/// <summary>
|
||||
/// All should be invalid address strings.
|
||||
/// </summary>
|
||||
/// <param name="address">Invalid address strings.</param>
|
||||
[Theory]
|
||||
[InlineData("256.128.0.0.0.1")]
|
||||
[InlineData("127.0.0.1#")]
|
||||
[InlineData("localhost!")]
|
||||
[InlineData("fd23:184f:2029:0:3139:7386:67d7:d517:1231")]
|
||||
[InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517:1231]")]
|
||||
public static void TryParse_InvalidAddressString_False(string address)
|
||||
=> Assert.False(IPHost.TryParse(address, out _));
|
||||
}
|
||||
}
|
||||
49
tests/Jellyfin.Networking.Tests/IPNetAddressTests.cs
Normal file
49
tests/Jellyfin.Networking.Tests/IPNetAddressTests.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using FsCheck;
|
||||
using FsCheck.Xunit;
|
||||
using MediaBrowser.Common.Net;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Networking.Tests
|
||||
{
|
||||
public static class IPNetAddressTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks IP address formats.
|
||||
/// </summary>
|
||||
/// <param name="address">IP Address.</param>
|
||||
[Theory]
|
||||
[InlineData("127.0.0.1")]
|
||||
[InlineData("fd23:184f:2029:0:3139:7386:67d7:d517")]
|
||||
[InlineData("fd23:184f:2029:0:3139:7386:67d7:d517/56")]
|
||||
[InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517]")]
|
||||
[InlineData("fe80::7add:12ff:febb:c67b%16")]
|
||||
[InlineData("[fe80::7add:12ff:febb:c67b%16]:123")]
|
||||
[InlineData("fe80::7add:12ff:febb:c67b%16:123")]
|
||||
[InlineData("[fe80::7add:12ff:febb:c67b%16]")]
|
||||
[InlineData("192.168.1.2/255.255.255.0")]
|
||||
[InlineData("192.168.1.2/24")]
|
||||
public static void TryParse_ValidIPStrings_True(string address)
|
||||
=> Assert.True(IPNetAddress.TryParse(address, out _));
|
||||
|
||||
[Property]
|
||||
public static Property TryParse_IPv4Address_True(IPv4Address address)
|
||||
=> IPNetAddress.TryParse(address.Item.ToString(), out _).ToProperty();
|
||||
|
||||
[Property]
|
||||
public static Property TryParse_IPv6Address_True(IPv6Address address)
|
||||
=> IPNetAddress.TryParse(address.Item.ToString(), out _).ToProperty();
|
||||
|
||||
/// <summary>
|
||||
/// All should be invalid address strings.
|
||||
/// </summary>
|
||||
/// <param name="address">Invalid address strings.</param>
|
||||
[Theory]
|
||||
[InlineData("256.128.0.0.0.1")]
|
||||
[InlineData("127.0.0.1#")]
|
||||
[InlineData("localhost!")]
|
||||
[InlineData("fd23:184f:2029:0:3139:7386:67d7:d517:1231")]
|
||||
[InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517:1231]")]
|
||||
public static void TryParse_InvalidAddressString_False(string address)
|
||||
=> Assert.False(IPNetAddress.TryParse(address, out _));
|
||||
}
|
||||
}
|
||||
@@ -8,17 +8,15 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Nullable>enable</Nullable>
|
||||
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.0.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0" />
|
||||
<PackageReference Include="FsCheck.Xunit" Version="2.16.1" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -56,66 +56,6 @@ namespace Jellyfin.Networking.Tests
|
||||
Assert.Equal(nm.GetInternalBindAddresses().AsString(), value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks IP address formats.
|
||||
/// </summary>
|
||||
/// <param name="address">IP Address.</param>
|
||||
[Theory]
|
||||
[InlineData("127.0.0.1")]
|
||||
[InlineData("127.0.0.1:123")]
|
||||
[InlineData("localhost")]
|
||||
[InlineData("localhost:1345")]
|
||||
[InlineData("www.google.co.uk")]
|
||||
[InlineData("fd23:184f:2029:0:3139:7386:67d7:d517")]
|
||||
[InlineData("fd23:184f:2029:0:3139:7386:67d7:d517/56")]
|
||||
[InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517]:124")]
|
||||
[InlineData("fe80::7add:12ff:febb:c67b%16")]
|
||||
[InlineData("[fe80::7add:12ff:febb:c67b%16]:123")]
|
||||
[InlineData("fe80::7add:12ff:febb:c67b%16:123")]
|
||||
[InlineData("[fe80::7add:12ff:febb:c67b%16]")]
|
||||
[InlineData("192.168.1.2/255.255.255.0")]
|
||||
[InlineData("192.168.1.2/24")]
|
||||
public void ValidHostStrings(string address)
|
||||
{
|
||||
Assert.True(IPHost.TryParse(address, out _));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks IP address formats.
|
||||
/// </summary>
|
||||
/// <param name="address">IP Address.</param>
|
||||
[Theory]
|
||||
[InlineData("127.0.0.1")]
|
||||
[InlineData("fd23:184f:2029:0:3139:7386:67d7:d517")]
|
||||
[InlineData("fd23:184f:2029:0:3139:7386:67d7:d517/56")]
|
||||
[InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517]")]
|
||||
[InlineData("fe80::7add:12ff:febb:c67b%16")]
|
||||
[InlineData("[fe80::7add:12ff:febb:c67b%16]:123")]
|
||||
[InlineData("fe80::7add:12ff:febb:c67b%16:123")]
|
||||
[InlineData("[fe80::7add:12ff:febb:c67b%16]")]
|
||||
[InlineData("192.168.1.2/255.255.255.0")]
|
||||
[InlineData("192.168.1.2/24")]
|
||||
public void ValidIPStrings(string address)
|
||||
{
|
||||
Assert.True(IPNetAddress.TryParse(address, out _));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// All should be invalid address strings.
|
||||
/// </summary>
|
||||
/// <param name="address">Invalid address strings.</param>
|
||||
[Theory]
|
||||
[InlineData("256.128.0.0.0.1")]
|
||||
[InlineData("127.0.0.1#")]
|
||||
[InlineData("localhost!")]
|
||||
[InlineData("fd23:184f:2029:0:3139:7386:67d7:d517:1231")]
|
||||
[InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517:1231]")]
|
||||
public void InvalidAddressString(string address)
|
||||
{
|
||||
Assert.False(IPNetAddress.TryParse(address, out _));
|
||||
Assert.False(IPHost.TryParse(address, out _));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test collection parsing.
|
||||
/// </summary>
|
||||
@@ -384,6 +324,9 @@ namespace Jellyfin.Networking.Tests
|
||||
[InlineData("jellyfin.org", "eth16", false, "eth16")]
|
||||
// User on external network, no binding - so result is the 1st external.
|
||||
[InlineData("jellyfin.org", "", false, "eth11")]
|
||||
// Dns failure - should skip the test.
|
||||
// https://en.wikipedia.org/wiki/.test
|
||||
[InlineData("invalid.domain.test", "", false, "eth11")]
|
||||
// User assumed to be internal, no binding - so result is the 1st internal.
|
||||
[InlineData("", "", false, "eth16")]
|
||||
public void TestBindInterfaces(string source, string bindAddresses, bool ipv6enabled, string result)
|
||||
@@ -416,10 +359,13 @@ namespace Jellyfin.Networking.Tests
|
||||
|
||||
_ = nm.TryParseInterface(result, out Collection<IPObject>? resultObj);
|
||||
|
||||
if (resultObj != null)
|
||||
// Check to see if dns resolution is working. If not, skip test.
|
||||
_ = IPHost.TryParse(source, out var host);
|
||||
|
||||
if (resultObj != null && host?.HasAddress == true)
|
||||
{
|
||||
result = ((IPNetAddress)resultObj[0]).ToString(true);
|
||||
var intf = nm.GetBindInterface(source, out int? _);
|
||||
var intf = nm.GetBindInterface(source, out _);
|
||||
|
||||
Assert.Equal(intf, result);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../MediaBrowser.Providers/MediaBrowser.Providers.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,96 @@
|
||||
#pragma warning disable CA1002 // Do not expose generic lists
|
||||
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Globalization;
|
||||
using MediaBrowser.Providers.MediaInfo;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Providers.Tests.MediaInfo
|
||||
{
|
||||
public class SubtitleResolverTests
|
||||
{
|
||||
public static IEnumerable<object[]> AddExternalSubtitleStreams_GivenMixedFilenames_ReturnsValidSubtitles_TestData()
|
||||
{
|
||||
var index = 0;
|
||||
yield return new object[]
|
||||
{
|
||||
new List<MediaStream>(),
|
||||
"/video/My.Video.mkv",
|
||||
index,
|
||||
new[]
|
||||
{
|
||||
"/video/My.Video.mp3",
|
||||
"/video/My.Video.png",
|
||||
"/video/My.Video.srt",
|
||||
"/video/My.Video.txt",
|
||||
"/video/My.Video.vtt",
|
||||
"/video/My.Video.ass",
|
||||
"/video/My.Video.sub",
|
||||
"/video/My.Video.ssa",
|
||||
"/video/My.Video.smi",
|
||||
"/video/My.Video.sami",
|
||||
"/video/My.Video.en.srt",
|
||||
"/video/My.Video.default.en.srt",
|
||||
"/video/My.Video.default.forced.en.srt",
|
||||
"/video/My.Video.en.default.forced.srt",
|
||||
"/video/My.Video.With.Additional.Garbage.en.srt",
|
||||
"/video/My.Video With Additional Garbage.srt"
|
||||
},
|
||||
new[]
|
||||
{
|
||||
CreateMediaStream("/video/My.Video.srt", "srt", null, index++),
|
||||
CreateMediaStream("/video/My.Video.vtt", "vtt", null, index++),
|
||||
CreateMediaStream("/video/My.Video.ass", "ass", null, index++),
|
||||
CreateMediaStream("/video/My.Video.sub", "sub", null, index++),
|
||||
CreateMediaStream("/video/My.Video.ssa", "ssa", null, index++),
|
||||
CreateMediaStream("/video/My.Video.smi", "smi", null, index++),
|
||||
CreateMediaStream("/video/My.Video.sami", "sami", null, index++),
|
||||
CreateMediaStream("/video/My.Video.en.srt", "srt", "en", index++),
|
||||
CreateMediaStream("/video/My.Video.default.en.srt", "srt", "en", index++, isDefault: true),
|
||||
CreateMediaStream("/video/My.Video.default.forced.en.srt", "srt", "en", index++, isForced: true, isDefault: true),
|
||||
CreateMediaStream("/video/My.Video.en.default.forced.srt", "srt", "en", index++, isForced: true, isDefault: true),
|
||||
CreateMediaStream("/video/My.Video.With.Additional.Garbage.en.srt", "srt", "en", index),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(AddExternalSubtitleStreams_GivenMixedFilenames_ReturnsValidSubtitles_TestData))]
|
||||
public void AddExternalSubtitleStreams_GivenMixedFilenames_ReturnsValidSubtitles(List<MediaStream> streams, string videoPath, int startIndex, string[] files, MediaStream[] expectedResult)
|
||||
{
|
||||
new SubtitleResolver(Mock.Of<ILocalizationManager>()).AddExternalSubtitleStreams(streams, videoPath, startIndex, files);
|
||||
|
||||
Assert.Equal(expectedResult.Length, streams.Count);
|
||||
for (var i = 0; i < expectedResult.Length; i++)
|
||||
{
|
||||
var expected = expectedResult[i];
|
||||
var actual = streams[i];
|
||||
|
||||
Assert.Equal(expected.Index, actual.Index);
|
||||
Assert.Equal(expected.Type, actual.Type);
|
||||
Assert.Equal(expected.IsExternal, actual.IsExternal);
|
||||
Assert.Equal(expected.Path, actual.Path);
|
||||
Assert.Equal(expected.IsDefault, actual.IsDefault);
|
||||
Assert.Equal(expected.IsForced, actual.IsForced);
|
||||
Assert.Equal(expected.Language, actual.Language);
|
||||
}
|
||||
}
|
||||
|
||||
private static MediaStream CreateMediaStream(string path, string codec, string? language, int index, bool isForced = false, bool isDefault = false)
|
||||
{
|
||||
return new ()
|
||||
{
|
||||
Index = index,
|
||||
Codec = codec,
|
||||
Type = MediaStreamType.Subtitle,
|
||||
IsExternal = true,
|
||||
Path = path,
|
||||
IsDefault = isDefault,
|
||||
IsForced = isForced,
|
||||
Language = language
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using MediaBrowser.Common.Json.Converters;
|
||||
using MediaBrowser.Providers.Plugins.Omdb;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Common.Tests.Json
|
||||
namespace Jellyfin.Providers.Tests.Omdb
|
||||
{
|
||||
public class JsonOmdbConverterTests
|
||||
{
|
||||
27
tests/Jellyfin.Providers.Tests/Tmdb/TmdbUtilsTests.cs
Normal file
27
tests/Jellyfin.Providers.Tests/Tmdb/TmdbUtilsTests.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using MediaBrowser.Providers.Plugins.Tmdb;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Providers.Tests.Tmdb
|
||||
{
|
||||
public static class TmdbUtilsTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("de", "de")]
|
||||
[InlineData("En", "En")]
|
||||
[InlineData("de-de", "de-DE")]
|
||||
[InlineData("en-US", "en-US")]
|
||||
[InlineData("de-CH", "de")]
|
||||
public static void NormalizeLanguage_Valid_Success(string input, string expected)
|
||||
{
|
||||
Assert.Equal(expected, TmdbUtils.NormalizeLanguage(input));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, null)]
|
||||
[InlineData("", "")]
|
||||
public static void NormalizeLanguage_Invalid_Equal(string? input, string? expected)
|
||||
{
|
||||
Assert.Equal(expected, TmdbUtils.NormalizeLanguage(input!));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using AutoFixture;
|
||||
using AutoFixture.AutoMoq;
|
||||
using Emby.Server.Implementations.Data;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.Data
|
||||
{
|
||||
public class SqliteItemRepositoryTests
|
||||
{
|
||||
public const string VirtualMetaDataPath = "%MetadataPath%";
|
||||
public const string MetaDataPath = "/meta/data/path";
|
||||
|
||||
private readonly IFixture _fixture;
|
||||
private readonly SqliteItemRepository _sqliteItemRepository;
|
||||
|
||||
public SqliteItemRepositoryTests()
|
||||
{
|
||||
var appHost = new Mock<IServerApplicationHost>();
|
||||
appHost.Setup(x => x.ExpandVirtualPath(It.IsAny<string>()))
|
||||
.Returns((string x) => x.Replace(VirtualMetaDataPath, MetaDataPath, StringComparison.Ordinal));
|
||||
appHost.Setup(x => x.ReverseVirtualPath(It.IsAny<string>()))
|
||||
.Returns((string x) => x.Replace(MetaDataPath, VirtualMetaDataPath, StringComparison.Ordinal));
|
||||
|
||||
_fixture = new Fixture().Customize(new AutoMoqCustomization { ConfigureMembers = true });
|
||||
_fixture.Inject(appHost);
|
||||
_sqliteItemRepository = _fixture.Create<SqliteItemRepository>();
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> ItemImageInfoFromValueString_Valid_TestData()
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
"/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg*637452096478512963*Primary*1920*1080*WjQbtJtSO8nhNZ%L_Io#R/oaS6o}-;adXAoIn7j[%hW9s:WGw[nN",
|
||||
new ItemImageInfo
|
||||
{
|
||||
Path = "/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg",
|
||||
Type = ImageType.Primary,
|
||||
DateModified = new DateTime(637452096478512963, DateTimeKind.Utc),
|
||||
Width = 1920,
|
||||
Height = 1080,
|
||||
BlurHash = "WjQbtJtSO8nhNZ%L_Io#R*oaS6o}-;adXAoIn7j[%hW9s:WGw[nN"
|
||||
}
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
"https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg*0*Primary*0*0",
|
||||
new ItemImageInfo
|
||||
{
|
||||
Path = "https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg",
|
||||
Type = ImageType.Primary,
|
||||
}
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
"https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg*0*Primary",
|
||||
new ItemImageInfo
|
||||
{
|
||||
Path = "https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg",
|
||||
Type = ImageType.Primary,
|
||||
}
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
"https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg*0*Primary*600",
|
||||
new ItemImageInfo
|
||||
{
|
||||
Path = "https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg",
|
||||
Type = ImageType.Primary,
|
||||
}
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
"%MetadataPath%/library/68/68578562b96c80a7ebd530848801f645/poster.jpg*637264380567586027*Primary*600*336",
|
||||
new ItemImageInfo
|
||||
{
|
||||
Path = "/meta/data/path/library/68/68578562b96c80a7ebd530848801f645/poster.jpg",
|
||||
Type = ImageType.Primary,
|
||||
DateModified = new DateTime(637264380567586027, DateTimeKind.Utc),
|
||||
Width = 600,
|
||||
Height = 336
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ItemImageInfoFromValueString_Valid_TestData))]
|
||||
public void ItemImageInfoFromValueString_Valid_Success(string value, ItemImageInfo expected)
|
||||
{
|
||||
var result = _sqliteItemRepository.ItemImageInfoFromValueString(value);
|
||||
Assert.Equal(expected.Path, result.Path);
|
||||
Assert.Equal(expected.Type, result.Type);
|
||||
Assert.Equal(expected.DateModified, result.DateModified);
|
||||
Assert.Equal(expected.Width, result.Width);
|
||||
Assert.Equal(expected.Height, result.Height);
|
||||
Assert.Equal(expected.BlurHash, result.BlurHash);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("")]
|
||||
[InlineData("*")]
|
||||
[InlineData("https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg*0")]
|
||||
[InlineData("/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg*6374520964785129080*WjQbtJtSO8nhNZ%L_Io#R/oaS<o}-;adXAoIn7j[%hW9s:WGw[nN")] // Invalid modified date
|
||||
[InlineData("/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg*-637452096478512963*WjQbtJtSO8nhNZ%L_Io#R/oaS<o}-;adXAoIn7j[%hW9s:WGw[nN")] // Negative modified date
|
||||
[InlineData("/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg*637452096478512963*Invalid*1920*1080*WjQbtJtSO8nhNZ%L_Io#R/oaS6o}-;adXAoIn7j[%hW9s:WGw[nN")] // Invalid type
|
||||
public void ItemImageInfoFromValueString_Invalid_Null(string value)
|
||||
{
|
||||
Assert.Null(_sqliteItemRepository.ItemImageInfoFromValueString(value));
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> DeserializeImages_Valid_TestData()
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
"/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg*637452096478512963*Primary*1920*1080*WjQbtJtSO8nhNZ%L_Io#R/oaS6o}-;adXAoIn7j[%hW9s:WGw[nN",
|
||||
new ItemImageInfo[]
|
||||
{
|
||||
new ItemImageInfo()
|
||||
{
|
||||
Path = "/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg",
|
||||
Type = ImageType.Primary,
|
||||
DateModified = new DateTime(637452096478512963, DateTimeKind.Utc),
|
||||
Width = 1920,
|
||||
Height = 1080,
|
||||
BlurHash = "WjQbtJtSO8nhNZ%L_Io#R*oaS6o}-;adXAoIn7j[%hW9s:WGw[nN"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
"%MetadataPath%/library/2a/2a27372f1e9bc757b1db99721bbeae1e/poster.jpg*637261226720645297*Primary*0*0|%MetadataPath%/library/2a/2a27372f1e9bc757b1db99721bbeae1e/logo.png*637261226720805297*Logo*0*0|%MetadataPath%/library/2a/2a27372f1e9bc757b1db99721bbeae1e/landscape.jpg*637261226721285297*Thumb*0*0|%MetadataPath%/library/2a/2a27372f1e9bc757b1db99721bbeae1e/backdrop.jpg*637261226721685297*Backdrop*0*0",
|
||||
new ItemImageInfo[]
|
||||
{
|
||||
new ItemImageInfo()
|
||||
{
|
||||
Path = "/meta/data/path/library/2a/2a27372f1e9bc757b1db99721bbeae1e/poster.jpg",
|
||||
Type = ImageType.Primary,
|
||||
DateModified = new DateTime(637261226720645297, DateTimeKind.Utc),
|
||||
},
|
||||
new ItemImageInfo()
|
||||
{
|
||||
Path = "/meta/data/path/library/2a/2a27372f1e9bc757b1db99721bbeae1e/logo.png",
|
||||
Type = ImageType.Logo,
|
||||
DateModified = new DateTime(637261226720805297, DateTimeKind.Utc),
|
||||
},
|
||||
new ItemImageInfo()
|
||||
{
|
||||
Path = "/meta/data/path/library/2a/2a27372f1e9bc757b1db99721bbeae1e/landscape.jpg",
|
||||
Type = ImageType.Thumb,
|
||||
DateModified = new DateTime(637261226721285297, DateTimeKind.Utc),
|
||||
},
|
||||
new ItemImageInfo()
|
||||
{
|
||||
Path = "/meta/data/path/library/2a/2a27372f1e9bc757b1db99721bbeae1e/backdrop.jpg",
|
||||
Type = ImageType.Backdrop,
|
||||
DateModified = new DateTime(637261226721685297, DateTimeKind.Utc),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> DeserializeImages_ValidAndInvalid_TestData()
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
string.Empty,
|
||||
Array.Empty<ItemImageInfo>()
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
"/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg*637452096478512963*Primary*1920*1080*WjQbtJtSO8nhNZ%L_Io#R/oaS6o}-;adXAoIn7j[%hW9s:WGw[nN|test|1234||ss",
|
||||
new ItemImageInfo[]
|
||||
{
|
||||
new ()
|
||||
{
|
||||
Path = "/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg",
|
||||
Type = ImageType.Primary,
|
||||
DateModified = new DateTime(637452096478512963, DateTimeKind.Utc),
|
||||
Width = 1920,
|
||||
Height = 1080,
|
||||
BlurHash = "WjQbtJtSO8nhNZ%L_Io#R*oaS6o}-;adXAoIn7j[%hW9s:WGw[nN"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
"|",
|
||||
Array.Empty<ItemImageInfo>()
|
||||
};
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(DeserializeImages_Valid_TestData))]
|
||||
public void DeserializeImages_Valid_Success(string value, ItemImageInfo[] expected)
|
||||
{
|
||||
var result = _sqliteItemRepository.DeserializeImages(value);
|
||||
Assert.Equal(expected.Length, result.Length);
|
||||
for (int i = 0; i < expected.Length; i++)
|
||||
{
|
||||
Assert.Equal(expected[i].Path, result[i].Path);
|
||||
Assert.Equal(expected[i].Type, result[i].Type);
|
||||
Assert.Equal(expected[i].DateModified, result[i].DateModified);
|
||||
Assert.Equal(expected[i].Width, result[i].Width);
|
||||
Assert.Equal(expected[i].Height, result[i].Height);
|
||||
Assert.Equal(expected[i].BlurHash, result[i].BlurHash);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(DeserializeImages_ValidAndInvalid_TestData))]
|
||||
public void DeserializeImages_ValidAndInvalid_Success(string value, ItemImageInfo[] expected)
|
||||
{
|
||||
var result = _sqliteItemRepository.DeserializeImages(value);
|
||||
Assert.Equal(expected.Length, result.Length);
|
||||
for (int i = 0; i < expected.Length; i++)
|
||||
{
|
||||
Assert.Equal(expected[i].Path, result[i].Path);
|
||||
Assert.Equal(expected[i].Type, result[i].Type);
|
||||
Assert.Equal(expected[i].DateModified, result[i].DateModified);
|
||||
Assert.Equal(expected[i].Width, result[i].Width);
|
||||
Assert.Equal(expected[i].Height, result[i].Height);
|
||||
Assert.Equal(expected[i].BlurHash, result[i].BlurHash);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(DeserializeImages_Valid_TestData))]
|
||||
public void SerializeImages_Valid_Success(string expected, ItemImageInfo[] value)
|
||||
{
|
||||
Assert.Equal(expected, _sqliteItemRepository.SerializeImages(value));
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> DeserializeProviderIds_Valid_TestData()
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
"Imdb=tt0119567",
|
||||
new Dictionary<string, string>()
|
||||
{
|
||||
{ "Imdb", "tt0119567" },
|
||||
}
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
"Imdb=tt0119567|Tmdb=330|TmdbCollection=328",
|
||||
new Dictionary<string, string>()
|
||||
{
|
||||
{ "Imdb", "tt0119567" },
|
||||
{ "Tmdb", "330" },
|
||||
{ "TmdbCollection", "328" },
|
||||
}
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
"MusicBrainzAlbum=9d363e43-f24f-4b39-bc5a-7ef305c677c7|MusicBrainzReleaseGroup=63eba062-847c-3b73-8b0f-6baf27bba6fa|AudioDbArtist=111352|AudioDbAlbum=2116560|MusicBrainzAlbumArtist=20244d07-534f-4eff-b4d4-930878889970",
|
||||
new Dictionary<string, string>()
|
||||
{
|
||||
{ "MusicBrainzAlbum", "9d363e43-f24f-4b39-bc5a-7ef305c677c7" },
|
||||
{ "MusicBrainzReleaseGroup", "63eba062-847c-3b73-8b0f-6baf27bba6fa" },
|
||||
{ "AudioDbArtist", "111352" },
|
||||
{ "AudioDbAlbum", "2116560" },
|
||||
{ "MusicBrainzAlbumArtist", "20244d07-534f-4eff-b4d4-930878889970" },
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(DeserializeProviderIds_Valid_TestData))]
|
||||
public void DeserializeProviderIds_Valid_Success(string value, Dictionary<string, string> expected)
|
||||
{
|
||||
var result = new ProviderIdsExtensionsTestsObject();
|
||||
SqliteItemRepository.DeserializeProviderIds(value, result);
|
||||
Assert.Equal(expected, result.ProviderIds);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(DeserializeProviderIds_Valid_TestData))]
|
||||
public void SerializeProviderIds_Valid_Success(string expected, Dictionary<string, string> values)
|
||||
{
|
||||
Assert.Equal(expected, SqliteItemRepository.SerializeProviderIds(values));
|
||||
}
|
||||
|
||||
private class ProviderIdsExtensionsTestsObject : IHasProviderIds
|
||||
{
|
||||
public Dictionary<string, string> ProviderIds { get; set; } = new Dictionary<string, string>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using AutoFixture;
|
||||
using AutoFixture.AutoMoq;
|
||||
using Emby.Server.Implementations.IO;
|
||||
using MediaBrowser.Model.System;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.IO
|
||||
@@ -31,7 +31,7 @@ namespace Jellyfin.Server.Implementations.Tests.IO
|
||||
{
|
||||
var generatedPath = _sut.MakeAbsolutePath(folderPath, filePath);
|
||||
|
||||
if (MediaBrowser.Common.System.OperatingSystem.Id == OperatingSystemId.Windows)
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
var expectedWindowsPath = expectedAbsolutePath.Replace('/', '\\');
|
||||
Assert.Equal(expectedWindowsPath, generatedPath.Split(':')[1]);
|
||||
@@ -42,10 +42,20 @@ namespace Jellyfin.Server.Implementations.Tests.IO
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("ValidFileName", "ValidFileName")]
|
||||
[InlineData("AC/DC", "AC DC")]
|
||||
[InlineData("Invalid\0", "Invalid ")]
|
||||
[InlineData("AC/DC\0KD/A", "AC DC KD A")]
|
||||
public void GetValidFilename_ReturnsValidFilename(string filename, string expectedFileName)
|
||||
{
|
||||
Assert.Equal(expectedFileName, _sut.GetValidFilename(filename));
|
||||
}
|
||||
|
||||
[SkippableFact]
|
||||
public void GetFileInfo_DanglingSymlink_ExistsFalse()
|
||||
{
|
||||
Skip.If(RuntimeInformation.IsOSPlatform(OSPlatform.Windows));
|
||||
Skip.If(OperatingSystem.IsWindows());
|
||||
|
||||
string testFileDir = Path.Combine(Path.GetTempPath(), "jellyfin-test-data");
|
||||
string testFileName = Path.Combine(testFileDir, Path.GetRandomFileName() + "-danglingsym.link");
|
||||
|
||||
@@ -8,9 +8,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Nullable>enable</Nullable>
|
||||
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
<RootNamespace>Jellyfin.Server.Implementations.Tests</RootNamespace>
|
||||
</PropertyGroup>
|
||||
@@ -22,14 +19,14 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoFixture" Version="4.16.0" />
|
||||
<PackageReference Include="AutoFixture.AutoMoq" Version="4.16.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="AutoFixture" Version="4.17.0" />
|
||||
<PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
<PackageReference Include="Xunit.SkippableFact" Version="1.4.13" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.0.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
@@ -42,6 +39,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Emby.Server.Implementations\Emby.Server.Implementations.csproj" />
|
||||
<ProjectReference Include="..\..\Jellyfin.Server.Implementations\Jellyfin.Server.Implementations.csproj" />
|
||||
<ProjectReference Include="..\Jellyfin.Server.Integration.Tests\Jellyfin.Server.Integration.Tests.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -6,6 +6,7 @@ using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
@@ -28,7 +29,10 @@ namespace Jellyfin.Server.Implementations.Tests.Library
|
||||
{
|
||||
Parent = parent,
|
||||
CollectionType = CollectionType.TvShows,
|
||||
Path = "All My Children/Season 01/Extras/All My Children S01E01 - Behind The Scenes.mkv"
|
||||
FileInfo = new FileSystemMetadata()
|
||||
{
|
||||
FullName = "All My Children/Season 01/Extras/All My Children S01E01 - Behind The Scenes.mkv"
|
||||
}
|
||||
};
|
||||
|
||||
Assert.Null(episodeResolver.Resolve(itemResolveArgs));
|
||||
@@ -48,7 +52,10 @@ namespace Jellyfin.Server.Implementations.Tests.Library
|
||||
{
|
||||
Parent = series,
|
||||
CollectionType = CollectionType.TvShows,
|
||||
Path = "Extras/Extras S01E01.mkv"
|
||||
FileInfo = new FileSystemMetadata()
|
||||
{
|
||||
FullName = "Extras/Extras S01E01.mkv"
|
||||
}
|
||||
};
|
||||
Assert.NotNull(episodeResolver.Resolve(itemResolveArgs));
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace Jellyfin.Server.Implementations.Tests.Library
|
||||
[InlineData("C:\\Users\\jeff\\myfile.mkv", "C:\\Users/jeff", "/home/jeff/", "/home/jeff/myfile.mkv")]
|
||||
[InlineData("C:\\Users\\jeff\\myfile.mkv", "C:\\Users/jeff/", "/home/jeff/", "/home/jeff/myfile.mkv")]
|
||||
[InlineData("C:\\Users\\jeff\\myfile.mkv", "C:\\Users/jeff/", "/", "/myfile.mkv")]
|
||||
[InlineData("/o", "/o", "/s", "/s")] // regression test for #5977
|
||||
public void TryReplaceSubPath_ValidArgs_Correct(string path, string subPath, string newSubPath, string? expectedResult)
|
||||
{
|
||||
Assert.True(PathExtensions.TryReplaceSubPath(path, subPath, newSubPath, out var result));
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AutoFixture;
|
||||
@@ -15,8 +16,6 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
|
||||
{
|
||||
public class HdHomerunHostTests
|
||||
{
|
||||
private const string TestIp = "http://192.168.1.182";
|
||||
|
||||
private readonly Fixture _fixture;
|
||||
private readonly HdHomerunHost _hdHomerunHost;
|
||||
|
||||
@@ -30,7 +29,7 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
|
||||
{
|
||||
return Task.FromResult(new HttpResponseMessage()
|
||||
{
|
||||
Content = new StreamContent(File.OpenRead("Test Data/LiveTv/" + m.RequestUri?.Segments[^1]))
|
||||
Content = new StreamContent(File.OpenRead(Path.Combine("Test Data/LiveTv", m.RequestUri!.Host, m.RequestUri.Segments[^1])))
|
||||
});
|
||||
});
|
||||
|
||||
@@ -50,7 +49,7 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
|
||||
{
|
||||
var host = new TunerHostInfo()
|
||||
{
|
||||
Url = TestIp
|
||||
Url = "192.168.1.182"
|
||||
};
|
||||
|
||||
var modelInfo = await _hdHomerunHost.GetModelInfo(host, true, CancellationToken.None).ConfigureAwait(false);
|
||||
@@ -65,6 +64,26 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
|
||||
Assert.Equal("http://192.168.1.182:80/lineup.json", modelInfo.LineupURL);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetModelInfo_Legacy_Success()
|
||||
{
|
||||
var host = new TunerHostInfo()
|
||||
{
|
||||
Url = "10.10.10.100"
|
||||
};
|
||||
|
||||
var modelInfo = await _hdHomerunHost.GetModelInfo(host, true, CancellationToken.None).ConfigureAwait(false);
|
||||
Assert.Equal("HDHomeRun DUAL", modelInfo.FriendlyName);
|
||||
Assert.Equal("HDHR3-US", modelInfo.ModelNumber);
|
||||
Assert.Equal("hdhomerun3_atsc", modelInfo.FirmwareName);
|
||||
Assert.Equal("20200225", modelInfo.FirmwareVersion);
|
||||
Assert.Equal("10xxxxx5", modelInfo.DeviceID);
|
||||
Assert.Null(modelInfo.DeviceAuth);
|
||||
Assert.Equal(2, modelInfo.TunerCount);
|
||||
Assert.Equal("http://10.10.10.100:80", modelInfo.BaseURL);
|
||||
Assert.Null(modelInfo.LineupURL);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetModelInfo_EmptyUrl_ArgumentException()
|
||||
{
|
||||
@@ -81,7 +100,7 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
|
||||
{
|
||||
var host = new TunerHostInfo()
|
||||
{
|
||||
Url = TestIp
|
||||
Url = "192.168.1.182"
|
||||
};
|
||||
|
||||
var channels = await _hdHomerunHost.GetLineup(host, CancellationToken.None).ConfigureAwait(false);
|
||||
@@ -93,12 +112,24 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
|
||||
Assert.Equal("http://192.168.1.111:5004/auto/v4.1", channels[0].URL);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetLineup_Legacy_Success()
|
||||
{
|
||||
var host = new TunerHostInfo()
|
||||
{
|
||||
Url = "10.10.10.100"
|
||||
};
|
||||
|
||||
// Placeholder json is invalid, just need to make sure we can reach it
|
||||
await Assert.ThrowsAsync<JsonException>(() => _hdHomerunHost.GetLineup(host, CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetLineup_ImportFavoritesOnly_Success()
|
||||
{
|
||||
var host = new TunerHostInfo()
|
||||
{
|
||||
Url = TestIp,
|
||||
Url = "192.168.1.182",
|
||||
ImportFavoritesOnly = true
|
||||
};
|
||||
|
||||
@@ -114,9 +145,9 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
|
||||
[Fact]
|
||||
public async Task TryGetTunerHostInfo_Valid_Success()
|
||||
{
|
||||
var host = await _hdHomerunHost.TryGetTunerHostInfo(TestIp, CancellationToken.None).ConfigureAwait(false);
|
||||
var host = await _hdHomerunHost.TryGetTunerHostInfo("192.168.1.182", CancellationToken.None).ConfigureAwait(false);
|
||||
Assert.Equal(_hdHomerunHost.Type, host.Type);
|
||||
Assert.Equal(TestIp, host.Url);
|
||||
Assert.Equal("192.168.1.182", host.Url);
|
||||
Assert.Equal("HDHomeRun PRIME", host.FriendlyName);
|
||||
Assert.Equal("FFFFFFFF", host.DeviceId);
|
||||
Assert.Equal(3, host.TunerCount);
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Emby.Server.Implementations.LiveTv.EmbyTV;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.LiveTv
|
||||
{
|
||||
public static class RecordingHelperTests
|
||||
{
|
||||
public static IEnumerable<object[]> GetRecordingName_Success_TestData()
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
"The Incredibles 2020_04_20_21_06_00",
|
||||
new TimerInfo
|
||||
{
|
||||
Name = "The Incredibles",
|
||||
StartDate = new DateTime(2020, 4, 20, 21, 6, 0, DateTimeKind.Local),
|
||||
IsMovie = true
|
||||
}
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
"The Incredibles (2004)",
|
||||
new TimerInfo
|
||||
{
|
||||
Name = "The Incredibles",
|
||||
IsMovie = true,
|
||||
ProductionYear = 2004
|
||||
}
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
"The Big Bang Theory 2020_04_20_21_06_00",
|
||||
new TimerInfo
|
||||
{
|
||||
Name = "The Big Bang Theory",
|
||||
StartDate = new DateTime(2020, 4, 20, 21, 6, 0, DateTimeKind.Local),
|
||||
IsProgramSeries = true,
|
||||
}
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
"The Big Bang Theory S12E10",
|
||||
new TimerInfo
|
||||
{
|
||||
Name = "The Big Bang Theory",
|
||||
IsProgramSeries = true,
|
||||
SeasonNumber = 12,
|
||||
EpisodeNumber = 10
|
||||
}
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
"The Big Bang Theory S12E10 The VCR Illumination",
|
||||
new TimerInfo
|
||||
{
|
||||
Name = "The Big Bang Theory",
|
||||
IsProgramSeries = true,
|
||||
SeasonNumber = 12,
|
||||
EpisodeNumber = 10,
|
||||
EpisodeTitle = "The VCR Illumination"
|
||||
}
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
"The Big Bang Theory 2018-12-06",
|
||||
new TimerInfo
|
||||
{
|
||||
Name = "The Big Bang Theory",
|
||||
IsProgramSeries = true,
|
||||
OriginalAirDate = new DateTime(2018, 12, 6)
|
||||
}
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
"The Big Bang Theory 2018-12-06 - The VCR Illumination",
|
||||
new TimerInfo
|
||||
{
|
||||
Name = "The Big Bang Theory",
|
||||
IsProgramSeries = true,
|
||||
OriginalAirDate = new DateTime(2018, 12, 6),
|
||||
EpisodeTitle = "The VCR Illumination"
|
||||
}
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
"The Big Bang Theory 2018_12_06_21_06_00 - The VCR Illumination",
|
||||
new TimerInfo
|
||||
{
|
||||
Name = "The Big Bang Theory",
|
||||
StartDate = new DateTime(2018, 12, 6, 21, 6, 0, DateTimeKind.Local),
|
||||
IsProgramSeries = true,
|
||||
OriginalAirDate = new DateTime(2018, 12, 6),
|
||||
EpisodeTitle = "The VCR Illumination"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(GetRecordingName_Success_TestData))]
|
||||
public static void GetRecordingName_Success(string expected, TimerInfo timerInfo)
|
||||
{
|
||||
Assert.Equal(expected, RecordingHelper.GetRecordingName(timerInfo));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Server.Implementations.Localization;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.Localization
|
||||
{
|
||||
public class LocalizationManagerTests
|
||||
{
|
||||
[Fact]
|
||||
public void GetCountries_All_Success()
|
||||
{
|
||||
var localizationManager = Setup(new ServerConfiguration
|
||||
{
|
||||
UICulture = "de-DE"
|
||||
});
|
||||
var countries = localizationManager.GetCountries().ToList();
|
||||
|
||||
Assert.Equal(139, countries.Count);
|
||||
|
||||
var germany = countries.FirstOrDefault(x => x.Name.Equals("DE", StringComparison.Ordinal));
|
||||
Assert.NotNull(germany);
|
||||
Assert.Equal("Germany", germany!.DisplayName);
|
||||
Assert.Equal("DEU", germany.ThreeLetterISORegionName);
|
||||
Assert.Equal("DE", germany.TwoLetterISORegionName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetCultures_All_Success()
|
||||
{
|
||||
var localizationManager = Setup(new ServerConfiguration
|
||||
{
|
||||
UICulture = "de-DE"
|
||||
});
|
||||
await localizationManager.LoadAll();
|
||||
var cultures = localizationManager.GetCultures().ToList();
|
||||
|
||||
Assert.Equal(189, cultures.Count);
|
||||
|
||||
var germany = cultures.FirstOrDefault(x => x.TwoLetterISOLanguageName.Equals("de", StringComparison.Ordinal));
|
||||
Assert.NotNull(germany);
|
||||
Assert.Equal("ger", germany!.ThreeLetterISOLanguageName);
|
||||
Assert.Equal("German", germany.DisplayName);
|
||||
Assert.Equal("German", germany.Name);
|
||||
Assert.Contains("deu", germany.ThreeLetterISOLanguageNames);
|
||||
Assert.Contains("ger", germany.ThreeLetterISOLanguageNames);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("de")]
|
||||
[InlineData("ger")]
|
||||
[InlineData("german")]
|
||||
public async Task FindLanguageInfo_Valid_Success(string identifier)
|
||||
{
|
||||
var localizationManager = Setup(new ServerConfiguration
|
||||
{
|
||||
UICulture = "de-DE"
|
||||
});
|
||||
await localizationManager.LoadAll();
|
||||
|
||||
var germany = localizationManager.FindLanguageInfo(identifier);
|
||||
Assert.NotNull(germany);
|
||||
|
||||
Assert.Equal("ger", germany!.ThreeLetterISOLanguageName);
|
||||
Assert.Equal("German", germany.DisplayName);
|
||||
Assert.Equal("German", germany.Name);
|
||||
Assert.Contains("deu", germany.ThreeLetterISOLanguageNames);
|
||||
Assert.Contains("ger", germany.ThreeLetterISOLanguageNames);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetParentalRatings_Default_Success()
|
||||
{
|
||||
var localizationManager = Setup(new ServerConfiguration
|
||||
{
|
||||
UICulture = "de-DE"
|
||||
});
|
||||
await localizationManager.LoadAll();
|
||||
var ratings = localizationManager.GetParentalRatings().ToList();
|
||||
|
||||
Assert.Equal(23, ratings.Count);
|
||||
|
||||
var tvma = ratings.FirstOrDefault(x => x.Name.Equals("TV-MA", StringComparison.Ordinal));
|
||||
Assert.NotNull(tvma);
|
||||
Assert.Equal(9, tvma!.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetParentalRatings_ConfiguredCountryCode_Success()
|
||||
{
|
||||
var localizationManager = Setup(new ServerConfiguration()
|
||||
{
|
||||
MetadataCountryCode = "DE"
|
||||
});
|
||||
await localizationManager.LoadAll();
|
||||
var ratings = localizationManager.GetParentalRatings().ToList();
|
||||
|
||||
Assert.Equal(10, ratings.Count);
|
||||
|
||||
var fsk = ratings.FirstOrDefault(x => x.Name.Equals("FSK-12", StringComparison.Ordinal));
|
||||
Assert.NotNull(fsk);
|
||||
Assert.Equal(7, fsk!.Value);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("CA-R", "CA", 10)]
|
||||
[InlineData("FSK-16", "DE", 8)]
|
||||
[InlineData("FSK-18", "DE", 9)]
|
||||
[InlineData("FSK-18", "US", 9)]
|
||||
[InlineData("TV-MA", "US", 9)]
|
||||
[InlineData("XXX", "asdf", 100)]
|
||||
[InlineData("Germany: FSK-18", "DE", 9)]
|
||||
public async Task GetRatingLevel_GivenValidString_Success(string value, string countryCode, int expectedLevel)
|
||||
{
|
||||
var localizationManager = Setup(new ServerConfiguration()
|
||||
{
|
||||
MetadataCountryCode = countryCode
|
||||
});
|
||||
await localizationManager.LoadAll();
|
||||
var level = localizationManager.GetRatingLevel(value);
|
||||
Assert.NotNull(level);
|
||||
Assert.Equal(expectedLevel, level!);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetRatingLevel_GivenUnratedString_Success()
|
||||
{
|
||||
var localizationManager = Setup(new ServerConfiguration()
|
||||
{
|
||||
UICulture = "de-DE"
|
||||
});
|
||||
await localizationManager.LoadAll();
|
||||
Assert.Null(localizationManager.GetRatingLevel("n/a"));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Default", "Default")]
|
||||
[InlineData("HeaderLiveTV", "Live TV")]
|
||||
public void GetLocalizedString_Valid_Success(string key, string expected)
|
||||
{
|
||||
var localizationManager = Setup(new ServerConfiguration()
|
||||
{
|
||||
UICulture = "en-US"
|
||||
});
|
||||
|
||||
var translated = localizationManager.GetLocalizedString(key);
|
||||
Assert.NotNull(translated);
|
||||
Assert.Equal(expected, translated);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLocalizedString_Invalid_Success()
|
||||
{
|
||||
var localizationManager = Setup(new ServerConfiguration()
|
||||
{
|
||||
UICulture = "en-US"
|
||||
});
|
||||
|
||||
var key = "SuperInvalidTranslationKeyThatWillNeverBeAdded";
|
||||
|
||||
var translated = localizationManager.GetLocalizedString(key);
|
||||
Assert.NotNull(translated);
|
||||
Assert.Equal(key, translated);
|
||||
}
|
||||
|
||||
private LocalizationManager Setup(ServerConfiguration config)
|
||||
{
|
||||
var mockConfiguration = new Mock<IServerConfigurationManager>();
|
||||
mockConfiguration.SetupGet(x => x.Configuration).Returns(config);
|
||||
|
||||
return new LocalizationManager(mockConfiguration.Object, new NullLogger<LocalizationManager>());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using AutoFixture;
|
||||
using AutoFixture.AutoMoq;
|
||||
using Emby.Server.Implementations.QuickConnect;
|
||||
using MediaBrowser.Controller.Authentication;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.QuickConnect
|
||||
{
|
||||
public class QuickConnectManagerTests
|
||||
{
|
||||
private static readonly AuthorizationInfo _quickConnectAuthInfo = new AuthorizationInfo
|
||||
{
|
||||
Device = "Device",
|
||||
DeviceId = "DeviceId",
|
||||
Client = "Client",
|
||||
Version = "1.0.0"
|
||||
};
|
||||
|
||||
private readonly Fixture _fixture;
|
||||
private readonly ServerConfiguration _config;
|
||||
private readonly QuickConnectManager _quickConnectManager;
|
||||
|
||||
public QuickConnectManagerTests()
|
||||
{
|
||||
_config = new ServerConfiguration();
|
||||
var configManager = new Mock<IServerConfigurationManager>();
|
||||
configManager.Setup(x => x.Configuration).Returns(_config);
|
||||
|
||||
_fixture = new Fixture();
|
||||
_fixture.Customize(new AutoMoqCustomization
|
||||
{
|
||||
ConfigureMembers = true
|
||||
}).Inject(configManager.Object);
|
||||
|
||||
// User object contains circular references.
|
||||
_fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList()
|
||||
.ForEach(b => _fixture.Behaviors.Remove(b));
|
||||
_fixture.Behaviors.Add(new OmitOnRecursionBehavior());
|
||||
|
||||
_quickConnectManager = _fixture.Create<QuickConnectManager>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsEnabled_QuickConnectUnavailable_False()
|
||||
=> Assert.False(_quickConnectManager.IsEnabled);
|
||||
|
||||
[Fact]
|
||||
public void TryConnect_QuickConnectUnavailable_ThrowsAuthenticationException()
|
||||
=> Assert.Throws<AuthenticationException>(() => _quickConnectManager.TryConnect(_quickConnectAuthInfo));
|
||||
|
||||
[Fact]
|
||||
public void CheckRequestStatus_QuickConnectUnavailable_ThrowsAuthenticationException()
|
||||
=> Assert.Throws<AuthenticationException>(() => _quickConnectManager.CheckRequestStatus(string.Empty));
|
||||
|
||||
[Fact]
|
||||
public void AuthorizeRequest_QuickConnectUnavailable_ThrowsAuthenticationException()
|
||||
=> Assert.ThrowsAsync<AuthenticationException>(() => _quickConnectManager.AuthorizeRequest(Guid.Empty, string.Empty));
|
||||
|
||||
[Fact]
|
||||
public void IsEnabled_QuickConnectAvailable_True()
|
||||
{
|
||||
_config.QuickConnectAvailable = true;
|
||||
Assert.True(_quickConnectManager.IsEnabled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckRequestStatus_QuickConnectAvailable_Success()
|
||||
{
|
||||
_config.QuickConnectAvailable = true;
|
||||
var res1 = _quickConnectManager.TryConnect(_quickConnectAuthInfo);
|
||||
var res2 = _quickConnectManager.CheckRequestStatus(res1.Secret);
|
||||
Assert.Equal(res1, res2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AuthorizeRequest_QuickConnectAvailable_Success()
|
||||
{
|
||||
_config.QuickConnectAvailable = true;
|
||||
var res = _quickConnectManager.TryConnect(_quickConnectAuthInfo);
|
||||
Assert.True(await _quickConnectManager.AuthorizeRequest(Guid.Empty, res.Code));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Emby.Server.Implementations.Sorting;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.Sorting
|
||||
{
|
||||
public class AiredEpisodeOrderComparerTests
|
||||
{
|
||||
[Theory]
|
||||
[ClassData(typeof(EpisodeBadData))]
|
||||
public void Compare_GivenNull_ThrowsArgumentNullException(BaseItem x, BaseItem y)
|
||||
{
|
||||
var cmp = new AiredEpisodeOrderComparer();
|
||||
Assert.Throws<ArgumentNullException>(() => cmp.Compare(x, y));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ClassData(typeof(EpisodeTestData))]
|
||||
public void AiredEpisodeOrderCompareTest(BaseItem x, BaseItem y, int expected)
|
||||
{
|
||||
var cmp = new AiredEpisodeOrderComparer();
|
||||
|
||||
Assert.Equal(expected, cmp.Compare(x, y));
|
||||
Assert.Equal(-expected, cmp.Compare(y, x));
|
||||
}
|
||||
|
||||
private class EpisodeBadData : IEnumerable<object?[]>
|
||||
{
|
||||
public IEnumerator<object?[]> GetEnumerator()
|
||||
{
|
||||
yield return new object?[] { null, new Episode() };
|
||||
yield return new object?[] { new Episode(), null };
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
|
||||
private class EpisodeTestData : IEnumerable<object?[]>
|
||||
{
|
||||
public IEnumerator<object?[]> GetEnumerator()
|
||||
{
|
||||
yield return new object?[]
|
||||
{
|
||||
new Movie(),
|
||||
new Movie(),
|
||||
0
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
new Movie(),
|
||||
new Episode(),
|
||||
1
|
||||
};
|
||||
// Good cases
|
||||
yield return new object?[]
|
||||
{
|
||||
new Episode(),
|
||||
new Episode(),
|
||||
0
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
|
||||
0
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 2 },
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
|
||||
1
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
new Episode { ParentIndexNumber = 2, IndexNumber = 1 },
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
|
||||
1
|
||||
};
|
||||
// Good Specials
|
||||
yield return new object?[]
|
||||
{
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1 },
|
||||
0
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 2 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1 },
|
||||
1
|
||||
};
|
||||
|
||||
// Specials to Episodes
|
||||
yield return new object?[]
|
||||
{
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1 },
|
||||
1
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 2 },
|
||||
1
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 2 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1 },
|
||||
1
|
||||
};
|
||||
|
||||
yield return new object?[]
|
||||
{
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 2 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1 },
|
||||
1
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 2 },
|
||||
1
|
||||
};
|
||||
|
||||
yield return new object?[]
|
||||
{
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1 },
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
|
||||
1
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
new Episode { ParentIndexNumber = 3, IndexNumber = 1 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1 },
|
||||
1
|
||||
};
|
||||
|
||||
yield return new object?[]
|
||||
{
|
||||
new Episode { ParentIndexNumber = 3, IndexNumber = 1 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 },
|
||||
1
|
||||
};
|
||||
|
||||
yield return new object?[]
|
||||
{
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1 },
|
||||
1
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 2 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 },
|
||||
1
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
new Episode { ParentIndexNumber = 1 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 },
|
||||
0
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 3 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 },
|
||||
1
|
||||
};
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"FriendlyName":"HDHomeRun DUAL","ModelNumber":"HDHR3-US","Legacy":1,"FirmwareName":"hdhomerun3_atsc","FirmwareVersion":"20200225","DeviceID":"10xxxxx5","TunerCount":2,"BaseURL":"http://10.10.10.100:80"}
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Jellyfin.Data.Enums;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.TypedBaseItem
|
||||
{
|
||||
public class BaseItemKindTests
|
||||
{
|
||||
public static TheoryData<Type> BaseItemKind_TestData()
|
||||
{
|
||||
var data = new TheoryData<Type>();
|
||||
|
||||
var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
foreach (var assembly in loadedAssemblies)
|
||||
{
|
||||
if (IsProjectAssemblyName(assembly.FullName))
|
||||
{
|
||||
var baseItemTypes = assembly.GetTypes()
|
||||
.Where(targetType => targetType.IsClass
|
||||
&& !targetType.IsAbstract
|
||||
&& targetType.IsSubclassOf(typeof(MediaBrowser.Controller.Entities.BaseItem)));
|
||||
foreach (var baseItemType in baseItemTypes)
|
||||
{
|
||||
data.Add(baseItemType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(BaseItemKind_TestData))]
|
||||
public void EnumParse_GivenValidBaseItemType_ReturnsEnumValue(Type baseItemDescendantType)
|
||||
{
|
||||
var enumValue = Enum.Parse<BaseItemKind>(baseItemDescendantType.Name);
|
||||
Assert.True(Enum.IsDefined(typeof(BaseItemKind), enumValue));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(BaseItemKind_TestData))]
|
||||
public void GetBaseItemKind_WhenCalledAfterDefaultCtor_DoesNotThrow(Type baseItemDescendantType)
|
||||
{
|
||||
var defaultConstructor = baseItemDescendantType.GetConstructor(Type.EmptyTypes);
|
||||
var instance = (MediaBrowser.Controller.Entities.BaseItem)defaultConstructor!.Invoke(null);
|
||||
var exception = Record.Exception(() => instance.GetBaseItemKind());
|
||||
Assert.Null(exception);
|
||||
}
|
||||
|
||||
private static bool IsProjectAssemblyName(string? name)
|
||||
{
|
||||
if (name == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return name.StartsWith("Jellyfin", StringComparison.OrdinalIgnoreCase)
|
||||
|| name.StartsWith("Emby", StringComparison.OrdinalIgnoreCase)
|
||||
|| name.StartsWith("MediaBrowser", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -46,12 +47,36 @@ namespace Jellyfin.Server.Implementations.Tests.Updates
|
||||
[Fact]
|
||||
public async Task GetPackages_Valid_Success()
|
||||
{
|
||||
IList<PackageInfo> packages = await _installationManager.GetPackages(
|
||||
PackageInfo[] packages = await _installationManager.GetPackages(
|
||||
"Jellyfin Stable",
|
||||
"https://repo.jellyfin.org/releases/plugin/manifest-stable.json",
|
||||
false);
|
||||
|
||||
Assert.Equal(25, packages.Count);
|
||||
Assert.Equal(25, packages.Length);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FilterPackages_NameOnly_Success()
|
||||
{
|
||||
PackageInfo[] packages = await _installationManager.GetPackages(
|
||||
"Jellyfin Stable",
|
||||
"https://repo.jellyfin.org/releases/plugin/manifest-stable.json",
|
||||
false);
|
||||
|
||||
packages = _installationManager.FilterPackages(packages, "Anime").ToArray();
|
||||
Assert.Single(packages);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FilterPackages_GuidOnly_Success()
|
||||
{
|
||||
PackageInfo[] packages = await _installationManager.GetPackages(
|
||||
"Jellyfin Stable",
|
||||
"https://repo.jellyfin.org/releases/plugin/manifest-stable.json",
|
||||
false);
|
||||
|
||||
packages = _installationManager.FilterPackages(packages, id: new Guid("a4df60c5-6ab4-412a-8f79-2cab93fb2bc5")).ToArray();
|
||||
Assert.Single(packages);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Api.Models.StartupDtos;
|
||||
using Jellyfin.Api.Models.UserDtos;
|
||||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Integration.Tests
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using Jellyfin.Api;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Jellyfin.Server.Integration.Tests.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Base controller for testing infrastructure.
|
||||
/// Automatically ignored in generated openapi spec.
|
||||
/// </summary>
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
public class BaseJellyfinTestController : BaseJellyfinApiController
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Api.Models;
|
||||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Integration.Tests.Controllers
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Jellyfin.Server.Integration.Tests.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Controller for testing the encoded url.
|
||||
/// </summary>
|
||||
public class EncoderController : BaseJellyfinTestController
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests the url decoding.
|
||||
/// </summary>
|
||||
/// <param name="params">Parameters to echo back in the response.</param>
|
||||
/// <returns>An <see cref="OkResult"/>.</returns>
|
||||
/// <response code="200">Information retrieved.</response>
|
||||
[HttpGet("UrlDecode")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public ContentResult TestUrlDecoding([FromQuery] Dictionary<string, string>? @params = null)
|
||||
{
|
||||
return new ContentResult()
|
||||
{
|
||||
Content = (@params != null && @params.Count > 0)
|
||||
? string.Join("&", @params.Select(x => x.Key + "=" + x.Value))
|
||||
: string.Empty,
|
||||
ContentType = "text/plain; charset=utf-8",
|
||||
StatusCode = 200
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the url decoding.
|
||||
/// </summary>
|
||||
/// <param name="params">Parameters to echo back in the response.</param>
|
||||
/// <returns>An <see cref="OkResult"/>.</returns>
|
||||
/// <response code="200">Information retrieved.</response>
|
||||
[HttpGet("UrlArrayDecode")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public ContentResult TestUrlArrayDecoding([FromQuery] Dictionary<string, string[]>? @params = null)
|
||||
{
|
||||
return new ContentResult()
|
||||
{
|
||||
Content = (@params != null && @params.Count > 0)
|
||||
? string.Join("&", @params.Select(x => x.Key + "=" + string.Join(',', x.Value)))
|
||||
: string.Empty,
|
||||
ContentType = "text/plain; charset=utf-8",
|
||||
StatusCode = 200
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Net.Mime;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Integration.Tests.Controllers
|
||||
{
|
||||
public sealed class MediaInfoControllerTests : IClassFixture<JellyfinApplicationFactory>
|
||||
{
|
||||
private readonly JellyfinApplicationFactory _factory;
|
||||
private static string? _accessToken;
|
||||
|
||||
public MediaInfoControllerTests(JellyfinApplicationFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BitrateTest_Default_Ok()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
var response = await client.GetAsync("Playback/BitrateTest").ConfigureAwait(false);
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Equal(MediaTypeNames.Application.Octet, response.Content.Headers.ContentType?.MediaType);
|
||||
Assert.NotNull(response.Content.Headers.ContentLength);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(102400)]
|
||||
public async Task BitrateTest_WithValidParam_Ok(int size)
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
var response = await client.GetAsync("Playback/BitrateTest?size=" + size.ToString(CultureInfo.InvariantCulture)).ConfigureAwait(false);
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Equal(MediaTypeNames.Application.Octet, response.Content.Headers.ContentType?.MediaType);
|
||||
Assert.NotNull(response.Content.Headers.ContentLength);
|
||||
Assert.InRange(response.Content.Headers.ContentLength!.Value, size, long.MaxValue);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0)] // Zero
|
||||
[InlineData(-102400)] // Negative value
|
||||
[InlineData(1000000000)] // Too large
|
||||
public async Task BitrateTest_InvalidValue_BadRequest(int size)
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
var response = await client.GetAsync("Playback/BitrateTest?size=" + size.ToString(CultureInfo.InvariantCulture)).ConfigureAwait(false);
|
||||
|
||||
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Mime;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Api.Models.LibraryStructureDto;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Integration.Tests.Controllers
|
||||
{
|
||||
public sealed class MediaStructureControllerTests : IClassFixture<JellyfinApplicationFactory>
|
||||
{
|
||||
private readonly JellyfinApplicationFactory _factory;
|
||||
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
|
||||
private static string? _accessToken;
|
||||
|
||||
public MediaStructureControllerTests(JellyfinApplicationFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RenameVirtualFolder_WhiteSpaceName_ReturnsBadRequest()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
using var postContent = new ByteArrayContent(Array.Empty<byte>());
|
||||
var response = await client.PostAsync("Library/VirtualFolders/Name?name=+&newName=test", postContent).ConfigureAwait(false);
|
||||
|
||||
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RenameVirtualFolder_WhiteSpaceNewName_ReturnsBadRequest()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
using var postContent = new ByteArrayContent(Array.Empty<byte>());
|
||||
var response = await client.PostAsync("Library/VirtualFolders/Name?name=test&newName=+", postContent).ConfigureAwait(false);
|
||||
|
||||
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RenameVirtualFolder_NameDoesntExist_ReturnsNotFound()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
using var postContent = new ByteArrayContent(Array.Empty<byte>());
|
||||
var response = await client.PostAsync("Library/VirtualFolders/Name?name=doesnt+exist&newName=test", postContent).ConfigureAwait(false);
|
||||
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddMediaPath_PathDoesntExist_ReturnsNotFound()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
var data = new MediaPathDto()
|
||||
{
|
||||
Name = "Test",
|
||||
Path = "/this/path/doesnt/exist"
|
||||
};
|
||||
|
||||
using var postContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(data, _jsonOptions));
|
||||
postContent.Headers.ContentType = MediaTypeHeaderValue.Parse(MediaTypeNames.Application.Json);
|
||||
var response = await client.PostAsync("Library/VirtualFolders/Paths", postContent).ConfigureAwait(false);
|
||||
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateMediaPath_WhiteSpaceName_ReturnsBadRequest()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
var data = new UpdateMediaPathRequestDto()
|
||||
{
|
||||
Name = " ",
|
||||
PathInfo = new MediaPathInfo("test")
|
||||
};
|
||||
|
||||
using var postContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(data, _jsonOptions));
|
||||
postContent.Headers.ContentType = MediaTypeHeaderValue.Parse(MediaTypeNames.Application.Json);
|
||||
var response = await client.PostAsync("Library/VirtualFolders/Paths/Update", postContent).ConfigureAwait(false);
|
||||
|
||||
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveMediaPath_WhiteSpaceName_ReturnsBadRequest()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
var response = await client.DeleteAsync("Library/VirtualFolders/Paths?name=+").ConfigureAwait(false);
|
||||
|
||||
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveMediaPath_PathDoesntExist_ReturnsNotFound()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
var response = await client.DeleteAsync("Library/VirtualFolders/Paths?name=none&path=%2Fthis%2Fpath%2Fdoesnt%2Fexist").ConfigureAwait(false);
|
||||
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ using System.Net.Mime;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Api.Models.StartupDtos;
|
||||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using Xunit;
|
||||
using Xunit.Priority;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ using System.Net.Mime;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Api.Models.UserDtos;
|
||||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using Xunit;
|
||||
using Xunit.Priority;
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Integration.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the test for encoded querystrings in the url.
|
||||
/// </summary>
|
||||
public class EncodedQueryStringTest : IClassFixture<JellyfinApplicationFactory>
|
||||
{
|
||||
private readonly JellyfinApplicationFactory _factory;
|
||||
|
||||
public EncodedQueryStringTest(JellyfinApplicationFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("a=1&b=2&c=3", "a=1&b=2&c=3")] // won't be processed as there is more than 1.
|
||||
[InlineData("a=1", "a=1")] // won't be processed as it has a value
|
||||
[InlineData("a%3D1%26b%3D2%26c%3D3", "a=1&b=2&c=3")] // will be processed.
|
||||
[InlineData("a=b&a=c", "a=b")]
|
||||
[InlineData("a%3Db%26a%3Dc", "a=b")]
|
||||
public async Task Ensure_Decoding_Of_Urls_Is_Working(string sourceUrl, string unencodedUrl)
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
|
||||
var response = await client.GetAsync("Encoder/UrlDecode?" + sourceUrl).ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
string reply = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
Assert.Equal(unencodedUrl, reply);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("a=b&a=c", "a=b,c")]
|
||||
[InlineData("a%3Db%26a%3Dc", "a=b,c")]
|
||||
public async Task Ensure_Array_Decoding_Of_Urls_Is_Working(string sourceUrl, string unencodedUrl)
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
|
||||
var response = await client.GetAsync("Encoder/UrlArrayDecode?" + sourceUrl).ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
string reply = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
Assert.Equal(unencodedUrl, reply);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,26 +2,30 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Nullable>enable</Nullable>
|
||||
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoFixture" Version="4.16.0" />
|
||||
<PackageReference Include="AutoFixture.AutoMoq" Version="4.16.0" />
|
||||
<PackageReference Include="AutoFixture.Xunit2" Version="4.16.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.5" />
|
||||
<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="5.0.9" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
<PackageReference Include="Xunit.Priority" Version="1.1.6" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.0.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Don't run tests in parallel -->
|
||||
<None Update="xunit.runner.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
|
||||
|
||||
@@ -44,10 +44,7 @@ namespace Jellyfin.Server.Integration.Tests
|
||||
protected override void ConfigureWebHost(IWebHostBuilder builder)
|
||||
{
|
||||
// Specify the startup command line options
|
||||
var commandLineOpts = new StartupOptions
|
||||
{
|
||||
NoWebClient = true
|
||||
};
|
||||
var commandLineOpts = new StartupOptions();
|
||||
|
||||
// Use a temporary directory for the application paths
|
||||
var webHostPathRoot = Path.Combine(_testPathRoot, "test-host-" + Path.GetFileNameWithoutExtension(Path.GetRandomFileName()));
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Integration.Tests.Middleware
|
||||
{
|
||||
public sealed class RobotsRedirectionMiddlewareTests : IClassFixture<JellyfinApplicationFactory>
|
||||
{
|
||||
private readonly JellyfinApplicationFactory _factory;
|
||||
|
||||
public RobotsRedirectionMiddlewareTests(JellyfinApplicationFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RobotsDotTxtRedirects()
|
||||
{
|
||||
var client = _factory.CreateClient(
|
||||
new WebApplicationFactoryClientOptions()
|
||||
{
|
||||
AllowAutoRedirect = false
|
||||
});
|
||||
|
||||
var response = await client.GetAsync("robots.txt").ConfigureAwait(false);
|
||||
|
||||
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
|
||||
Assert.Equal("web/robots.txt", response.Headers.Location?.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"parallelizeAssembly": false,
|
||||
"parallelizeTestCollections": false
|
||||
}
|
||||
@@ -3,22 +3,19 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Nullable>enable</Nullable>
|
||||
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoFixture" Version="4.16.0" />
|
||||
<PackageReference Include="AutoFixture.AutoMoq" Version="4.16.0" />
|
||||
<PackageReference Include="AutoFixture.Xunit2" Version="4.16.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.5" />
|
||||
<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="5.0.9" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.0.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using Jellyfin.Networking.Configuration;
|
||||
using Jellyfin.Networking.Manager;
|
||||
using Jellyfin.Server.Extensions;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
@@ -13,20 +18,63 @@ namespace Jellyfin.Server.Tests
|
||||
{
|
||||
public class ParseNetworkTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Order of the result has always got to be hosts, then networks.
|
||||
/// </summary>
|
||||
/// <param name="ip4">IP4 enabled.</param>
|
||||
/// <param name="ip6">IP6 enabled.</param>
|
||||
/// <param name="hostList">List to parse.</param>
|
||||
/// <param name="match">What it should match.</param>
|
||||
public static TheoryData<bool, bool, string[], IPAddress[], IPNetwork[]> TestNetworks_TestData()
|
||||
{
|
||||
var data = new TheoryData<bool, bool, string[], IPAddress[], IPNetwork[]>();
|
||||
data.Add(
|
||||
true,
|
||||
true,
|
||||
new string[] { "192.168.t", "127.0.0.1", "1234.1232.12.1234" },
|
||||
new IPAddress[] { IPAddress.Loopback.MapToIPv6() },
|
||||
Array.Empty<IPNetwork>());
|
||||
|
||||
data.Add(
|
||||
true,
|
||||
false,
|
||||
new string[] { "192.168.x", "127.0.0.1", "1234.1232.12.1234" },
|
||||
new IPAddress[] { IPAddress.Loopback },
|
||||
Array.Empty<IPNetwork>());
|
||||
|
||||
data.Add(
|
||||
true,
|
||||
true,
|
||||
new string[] { "::1" },
|
||||
Array.Empty<IPAddress>(),
|
||||
new IPNetwork[] { new IPNetwork(IPAddress.IPv6Loopback, 128) });
|
||||
|
||||
data.Add(
|
||||
false,
|
||||
false,
|
||||
new string[] { "localhost" },
|
||||
Array.Empty<IPAddress>(),
|
||||
Array.Empty<IPNetwork>());
|
||||
|
||||
data.Add(
|
||||
true,
|
||||
false,
|
||||
new string[] { "localhost" },
|
||||
new IPAddress[] { IPAddress.Loopback },
|
||||
Array.Empty<IPNetwork>());
|
||||
|
||||
data.Add(
|
||||
false,
|
||||
true,
|
||||
new string[] { "localhost" },
|
||||
Array.Empty<IPAddress>(),
|
||||
new IPNetwork[] { new IPNetwork(IPAddress.IPv6Loopback, 128) });
|
||||
|
||||
data.Add(
|
||||
true,
|
||||
true,
|
||||
new string[] { "localhost" },
|
||||
new IPAddress[] { IPAddress.Loopback.MapToIPv6() },
|
||||
new IPNetwork[] { new IPNetwork(IPAddress.IPv6Loopback, 128) });
|
||||
return data;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
// [InlineData(true, true, "192.168.0.0/16,www.yahoo.co.uk", "::ffff:212.82.100.150,::ffff:192.168.0.0/16")] <- fails on Max. www.yahoo.co.uk resolves to a different ip address.
|
||||
// [InlineData(true, false, "192.168.0.0/16,www.yahoo.co.uk", "212.82.100.150,192.168.0.0/16")]
|
||||
[InlineData(true, true, "192.168.t,127.0.0.1,1234.1232.12.1234", "::ffff:127.0.0.1")]
|
||||
[InlineData(true, false, "192.168.x,127.0.0.1,1234.1232.12.1234", "127.0.0.1")]
|
||||
[InlineData(true, true, "::1", "::1/128")]
|
||||
public void TestNetworks(bool ip4, bool ip6, string hostList, string match)
|
||||
[MemberData(nameof(TestNetworks_TestData))]
|
||||
public void TestNetworks(bool ip4, bool ip6, string[] hostList, IPAddress[] knownProxies, IPNetwork[] knownNetworks)
|
||||
{
|
||||
using var nm = CreateNetworkManager();
|
||||
|
||||
@@ -36,31 +84,25 @@ namespace Jellyfin.Server.Tests
|
||||
EnableIPV6 = ip6
|
||||
};
|
||||
|
||||
var result = match + ",";
|
||||
ForwardedHeadersOptions options = new ForwardedHeadersOptions();
|
||||
|
||||
// Need this here as ::1 and 127.0.0.1 are in them by default.
|
||||
options.KnownProxies.Clear();
|
||||
options.KnownNetworks.Clear();
|
||||
|
||||
ApiServiceCollectionExtensions.AddProxyAddresses(settings, hostList.Split(','), options);
|
||||
ApiServiceCollectionExtensions.AddProxyAddresses(settings, hostList, options);
|
||||
|
||||
var sb = new StringBuilder();
|
||||
foreach (var item in options.KnownProxies)
|
||||
Assert.Equal(knownProxies.Length, options.KnownProxies.Count);
|
||||
foreach (var item in knownProxies)
|
||||
{
|
||||
sb.Append(item)
|
||||
.Append(',');
|
||||
Assert.True(options.KnownProxies.Contains(item));
|
||||
}
|
||||
|
||||
foreach (var item in options.KnownNetworks)
|
||||
Assert.Equal(knownNetworks.Length, options.KnownNetworks.Count);
|
||||
foreach (var item in knownNetworks)
|
||||
{
|
||||
sb.Append(item.Prefix)
|
||||
.Append('/')
|
||||
.Append(item.PrefixLength.ToString(CultureInfo.InvariantCulture))
|
||||
.Append(',');
|
||||
Assert.NotNull(options.KnownNetworks.FirstOrDefault(x => x.Prefix.Equals(item.Prefix) && x.PrefixLength == item.PrefixLength));
|
||||
}
|
||||
|
||||
Assert.Equal(sb.ToString(), result);
|
||||
}
|
||||
|
||||
private static IConfigurationManager GetMockConfig(NetworkConfiguration conf)
|
||||
|
||||
31
tests/Jellyfin.Server.Tests/UrlDecodeQueryFeatureTests.cs
Normal file
31
tests/Jellyfin.Server.Tests/UrlDecodeQueryFeatureTests.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Jellyfin.Server.Middleware;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Tests
|
||||
{
|
||||
public static class UrlDecodeQueryFeatureTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("e0a72cb2a2c7", "e0a72cb2a2c7")] // isn't encoded
|
||||
[InlineData("random+test", "random test")] // encoded
|
||||
[InlineData("random%20test", "random test")] // encoded
|
||||
[InlineData("++", " ")] // encoded
|
||||
public static void EmptyValueTest(string query, string key)
|
||||
{
|
||||
var dict = new Dictionary<string, StringValues>
|
||||
{
|
||||
{ query, StringValues.Empty }
|
||||
};
|
||||
var test = new UrlDecodeQueryFeature(new QueryFeature(new QueryCollection(dict)));
|
||||
Assert.Single(test.Query);
|
||||
var (k, v) = test.Query.First();
|
||||
Assert.Equal(key, k);
|
||||
Assert.Empty(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Nullable>enable</Nullable>
|
||||
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -16,11 +13,11 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.0.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.System;
|
||||
using MediaBrowser.XbmcMetadata.Savers;
|
||||
using Xunit;
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Location
|
||||
var path2 = "/media/movies/Avengers Endgame/movie.nfo";
|
||||
|
||||
// uses ContainingFolderPath which uses Operating system specific paths
|
||||
if (MediaBrowser.Common.System.OperatingSystem.Id == OperatingSystemId.Windows)
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
movie.Path = movie.Path.Replace('/', '\\');
|
||||
path1 = path1.Replace('/', '\\');
|
||||
@@ -49,7 +49,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Location
|
||||
var path2 = "/media/movies/Avengers Endgame/VIDEO_TS/VIDEO_TS.nfo";
|
||||
|
||||
// uses ContainingFolderPath which uses Operating system specific paths
|
||||
if (MediaBrowser.Common.System.OperatingSystem.Id == OperatingSystemId.Windows)
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
movie.Path = movie.Path.Replace('/', '\\');
|
||||
path1 = path1.Replace('/', '\\');
|
||||
|
||||
@@ -14,8 +14,6 @@ using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
#pragma warning disable CA5369
|
||||
|
||||
namespace Jellyfin.XbmcMetadata.Tests.Parsers
|
||||
{
|
||||
public class EpisodeNfoProviderTests
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
|
||||
_localImageFileMetadata = new FileSystemMetadata()
|
||||
{
|
||||
Exists = true,
|
||||
FullName = MediaBrowser.Common.System.OperatingSystem.Id == OperatingSystemId.Windows ?
|
||||
FullName = OperatingSystem.IsWindows() ?
|
||||
"C:\\media\\movies\\Justice League (2017).jpg"
|
||||
: "/media/movies/Justice League (2017).jpg"
|
||||
};
|
||||
@@ -156,7 +156,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
|
||||
Assert.Equal("Justice League Collection", item.CollectionName);
|
||||
|
||||
// Images
|
||||
Assert.Equal(6, result.RemoteImages.Count);
|
||||
Assert.Equal(7, result.RemoteImages.Count);
|
||||
|
||||
var posters = result.RemoteImages.Where(x => x.type == ImageType.Primary).ToList();
|
||||
Assert.Single(posters);
|
||||
@@ -182,6 +182,10 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
|
||||
Assert.Single(discArt);
|
||||
Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-5a3af26360617.png", discArt[0].url);
|
||||
|
||||
var backdrop = result.RemoteImages.Where(x => x.type == ImageType.Backdrop).ToList();
|
||||
Assert.Single(backdrop);
|
||||
Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5793f518c6d6e.jpg", backdrop[0].url);
|
||||
|
||||
// Local Image - contains only one item depending on operating system
|
||||
Assert.Single(result.Images);
|
||||
Assert.Equal(_localImageFileMetadata.Name, result.Images[0].FileInfo.Name);
|
||||
@@ -203,6 +207,20 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
|
||||
Assert.Equal(id, item.ProviderIds[provider]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_GivenFileWithFanartTag_Success()
|
||||
{
|
||||
var result = new MetadataResult<Video>()
|
||||
{
|
||||
Item = new Movie()
|
||||
};
|
||||
|
||||
_parser.Fetch(result, "Test Data/Fanart.nfo", CancellationToken.None);
|
||||
|
||||
Assert.Single(result.RemoteImages.Where(x => x.type == ImageType.Backdrop));
|
||||
Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5a5332c7b5e77.jpg", result.RemoteImages.First(x => x.type == ImageType.Backdrop).url);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_RadarrUrlFile_Success()
|
||||
{
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#pragma warning disable CA5369
|
||||
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#pragma warning disable CA5369
|
||||
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
|
||||
33
tests/Jellyfin.XbmcMetadata.Tests/Test Data/Fanart.nfo
Normal file
33
tests/Jellyfin.XbmcMetadata.Tests/Test Data/Fanart.nfo
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<movie>
|
||||
<thumb aspect="clearlogo" preview="https://assets.fanart.tv/preview/movies/141052/hdmovielogo/justice-league-5865bf95cbadb.png">https://assets.fanart.tv/fanart/movies/141052/hdmovielogo/justice-league-5865bf95cbadb.png</thumb>
|
||||
<thumb aspect="clearlogo" preview="https://assets.fanart.tv/preview/movies/141052/hdmovielogo/justice-league-585e9ca3bcf6a.png">https://assets.fanart.tv/fanart/movies/141052/hdmovielogo/justice-league-585e9ca3bcf6a.png</thumb>
|
||||
<thumb aspect="clearlogo" preview="https://assets.fanart.tv/preview/movies/141052/hdmovielogo/justice-league-57b476a831d74.png">https://assets.fanart.tv/fanart/movies/141052/hdmovielogo/justice-league-57b476a831d74.png</thumb>
|
||||
<thumb aspect="clearlogo" preview="https://assets.fanart.tv/preview/movies/141052/hdmovielogo/justice-league-57947e28cf10b.png">https://assets.fanart.tv/fanart/movies/141052/hdmovielogo/justice-league-57947e28cf10b.png</thumb>
|
||||
<thumb aspect="clearlogo" preview="https://assets.fanart.tv/preview/movies/141052/hdmovielogo/justice-league-5863d5c0cf0c9.png">https://assets.fanart.tv/fanart/movies/141052/hdmovielogo/justice-league-5863d5c0cf0c9.png</thumb>
|
||||
<thumb aspect="clearlogo" preview="https://assets.fanart.tv/preview/movies/141052/hdmovielogo/justice-league-5a801747e5545.png">https://assets.fanart.tv/fanart/movies/141052/hdmovielogo/justice-league-5a801747e5545.png</thumb>
|
||||
<thumb aspect="clearlogo" preview="https://assets.fanart.tv/preview/movies/141052/hdmovielogo/justice-league-5cd75683df92b.png">https://assets.fanart.tv/fanart/movies/141052/hdmovielogo/justice-league-5cd75683df92b.png</thumb>
|
||||
<thumb aspect="banner" preview="https://assets.fanart.tv/preview/movies/141052/moviebanner/justice-league-586017e95adbd.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebanner/justice-league-586017e95adbd.jpg</thumb>
|
||||
<thumb aspect="banner" preview="https://assets.fanart.tv/preview/movies/141052/moviebanner/justice-league-5934d45bc6592.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebanner/justice-league-5934d45bc6592.jpg</thumb>
|
||||
<thumb aspect="banner" preview="https://assets.fanart.tv/preview/movies/141052/moviebanner/justice-league-5aa9289a379fa.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebanner/justice-league-5aa9289a379fa.jpg</thumb>
|
||||
<thumb aspect="landscape" preview="https://assets.fanart.tv/preview/movies/141052/moviethumb/justice-league-585fb155c3743.jpg">https://assets.fanart.tv/fanart/movies/141052/moviethumb/justice-league-585fb155c3743.jpg</thumb>
|
||||
<thumb aspect="landscape" preview="https://assets.fanart.tv/preview/movies/141052/moviethumb/justice-league-585edbda91d82.jpg">https://assets.fanart.tv/fanart/movies/141052/moviethumb/justice-league-585edbda91d82.jpg</thumb>
|
||||
<thumb aspect="landscape" preview="https://assets.fanart.tv/preview/movies/141052/moviethumb/justice-league-5b86588882c12.jpg">https://assets.fanart.tv/fanart/movies/141052/moviethumb/justice-league-5b86588882c12.jpg</thumb>
|
||||
<thumb aspect="landscape" preview="https://assets.fanart.tv/preview/movies/141052/moviethumb/justice-league-5bbb9babe600c.jpg">https://assets.fanart.tv/fanart/movies/141052/moviethumb/justice-league-5bbb9babe600c.jpg</thumb>
|
||||
<thumb aspect="clearart" preview="https://assets.fanart.tv/preview/movies/141052/hdmovieclearart/justice-league-5865c23193041.png">https://assets.fanart.tv/fanart/movies/141052/hdmovieclearart/justice-league-5865c23193041.png</thumb>
|
||||
<thumb aspect="discart" preview="https://assets.fanart.tv/preview/movies/141052/moviedisc/justice-league-5a3af26360617.png">https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-5a3af26360617.png</thumb>
|
||||
<thumb aspect="discart" preview="https://assets.fanart.tv/preview/movies/141052/moviedisc/justice-league-58690967b9765.png">https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-58690967b9765.png</thumb>
|
||||
<thumb aspect="discart" preview="https://assets.fanart.tv/preview/movies/141052/moviedisc/justice-league-5a953ca4db6a6.png">https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-5a953ca4db6a6.png</thumb>
|
||||
<thumb aspect="discart" preview="https://assets.fanart.tv/preview/movies/141052/moviedisc/justice-league-5a0b913c233be.png">https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-5a0b913c233be.png</thumb>
|
||||
<thumb aspect="discart" preview="https://assets.fanart.tv/preview/movies/141052/moviedisc/justice-league-5a87e0cdb1209.png">https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-5a87e0cdb1209.png</thumb>
|
||||
<thumb aspect="discart" preview="https://assets.fanart.tv/preview/movies/141052/moviedisc/justice-league-59dc595362ef1.png">https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-59dc595362ef1.png</thumb>
|
||||
<fanart>
|
||||
<thumb preview="https://assets.fanart.tv/preview/movies/141052/moviebackground/justice-league-5a5332c7b5e77.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5a5332c7b5e77.jpg</thumb>
|
||||
<thumb preview="https://assets.fanart.tv/preview/movies/141052/moviebackground/justice-league-5a53cf2dac1c8.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5a53cf2dac1c8.jpg</thumb>
|
||||
<thumb preview="https://assets.fanart.tv/preview/movies/141052/moviebackground/justice-league-5976ba93eb5d3.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5976ba93eb5d3.jpg</thumb>
|
||||
<thumb preview="https://assets.fanart.tv/preview/movies/141052/moviebackground/justice-league-58fa1f1932897.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-58fa1f1932897.jpg</thumb>
|
||||
<thumb preview="https://assets.fanart.tv/preview/movies/141052/moviebackground/justice-league-5a14f5fd8dd16.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5a14f5fd8dd16.jpg</thumb>
|
||||
<thumb preview="https://assets.fanart.tv/preview/movies/141052/moviebackground/justice-league-5a119394ea362.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5a119394ea362.jpg</thumb>
|
||||
</fanart>
|
||||
<thumb aspect="fanart">This-should-not-be-saved-as-a-fanart-image.jpg</thumb>
|
||||
</movie>
|
||||
@@ -82,8 +82,8 @@
|
||||
<thumb aspect="discart" preview="https://assets.fanart.tv/preview/movies/141052/moviedisc/justice-league-5a0b913c233be.png">https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-5a0b913c233be.png</thumb>
|
||||
<thumb aspect="discart" preview="https://assets.fanart.tv/preview/movies/141052/moviedisc/justice-league-5a87e0cdb1209.png">https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-5a87e0cdb1209.png</thumb>
|
||||
<thumb aspect="discart" preview="https://assets.fanart.tv/preview/movies/141052/moviedisc/justice-league-59dc595362ef1.png">https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-59dc595362ef1.png</thumb>
|
||||
<thumb aspect="fanart">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5793f518c6d6e.jpg</thumb>
|
||||
<fanart>
|
||||
<thumb preview="https://assets.fanart.tv/preview/movies/141052/moviebackground/justice-league-5793f518c6d6e.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5793f518c6d6e.jpg</thumb>
|
||||
<thumb preview="https://assets.fanart.tv/preview/movies/141052/moviebackground/justice-league-5a5332c7b5e77.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5a5332c7b5e77.jpg</thumb>
|
||||
<thumb preview="https://assets.fanart.tv/preview/movies/141052/moviebackground/justice-league-5a53cf2dac1c8.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5a53cf2dac1c8.jpg</thumb>
|
||||
<thumb preview="https://assets.fanart.tv/preview/movies/141052/moviebackground/justice-league-5976ba93eb5d3.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5976ba93eb5d3.jpg</thumb>
|
||||
|
||||
Reference in New Issue
Block a user