mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-01-27 13:38:20 +00:00
Merge branch 'master' into keyframe_extraction_v1
# Conflicts: # Jellyfin.Api/Controllers/DynamicHlsController.cs # MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs # MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
This commit is contained in:
@@ -132,6 +132,8 @@ namespace Jellyfin.Api.Tests.Auth
|
||||
authorizationInfo.User.AddDefaultPreferences();
|
||||
authorizationInfo.User.SetPermission(PermissionKind.IsAdministrator, isAdmin);
|
||||
authorizationInfo.IsApiKey = false;
|
||||
authorizationInfo.HasToken = true;
|
||||
authorizationInfo.Token = "fake-token";
|
||||
|
||||
_jellyfinAuthServiceMock.Setup(
|
||||
a => a.Authenticate(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Jellyfin.Api.Controllers;
|
||||
using Xunit;
|
||||
|
||||
@@ -19,33 +18,28 @@ namespace Jellyfin.Api.Tests.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> GetSegmentLengths_Success_TestData()
|
||||
public static TheoryData<long, int, double[]> GetSegmentLengths_Success_TestData()
|
||||
{
|
||||
yield return new object[] { 0, 6, Array.Empty<double>() };
|
||||
yield return new object[]
|
||||
{
|
||||
var data = new TheoryData<long, int, double[]>();
|
||||
data.Add(0, 6, Array.Empty<double>());
|
||||
data.Add(
|
||||
TimeSpan.FromSeconds(3).Ticks,
|
||||
6,
|
||||
new double[] { 3 }
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
new double[] { 3 });
|
||||
data.Add(
|
||||
TimeSpan.FromSeconds(6).Ticks,
|
||||
6,
|
||||
new double[] { 6 }
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
new double[] { 6 });
|
||||
data.Add(
|
||||
TimeSpan.FromSeconds(3.3333333).Ticks,
|
||||
6,
|
||||
new double[] { 3.3333333 }
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
new double[] { 3.3333333 });
|
||||
data.Add(
|
||||
TimeSpan.FromSeconds(9.3333333).Ticks,
|
||||
6,
|
||||
new double[] { 6, 3.3333333 }
|
||||
};
|
||||
new double[] { 6, 3.3333333 });
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,16 +15,16 @@ namespace Jellyfin.Api.Tests.Helpers
|
||||
Assert.Equal(expected, RequestHelpers.GetOrderBy(sortBy, requestedSortOrder));
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> GetOrderBy_Success_TestData()
|
||||
public static TheoryData<IReadOnlyList<string>, IReadOnlyList<SortOrder>, (string, SortOrder)[]> GetOrderBy_Success_TestData()
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
var data = new TheoryData<IReadOnlyList<string>, IReadOnlyList<SortOrder>, (string, SortOrder)[]>();
|
||||
|
||||
data.Add(
|
||||
Array.Empty<string>(),
|
||||
Array.Empty<SortOrder>(),
|
||||
Array.Empty<(string, SortOrder)>()
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
Array.Empty<(string, SortOrder)>());
|
||||
|
||||
data.Add(
|
||||
new string[]
|
||||
{
|
||||
"IsFavoriteOrLiked",
|
||||
@@ -35,10 +35,9 @@ namespace Jellyfin.Api.Tests.Helpers
|
||||
{
|
||||
("IsFavoriteOrLiked", SortOrder.Ascending),
|
||||
("Random", SortOrder.Ascending),
|
||||
}
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
});
|
||||
|
||||
data.Add(
|
||||
new string[]
|
||||
{
|
||||
"SortName",
|
||||
@@ -52,38 +51,9 @@ namespace Jellyfin.Api.Tests.Helpers
|
||||
{
|
||||
("SortName", SortOrder.Descending),
|
||||
("ProductionYear", SortOrder.Descending),
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
[Fact]
|
||||
public static void GetItemTypeStrings_Empty_Empty()
|
||||
{
|
||||
Assert.Empty(RequestHelpers.GetItemTypeStrings(Array.Empty<BaseItemKind>()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void GetItemTypeStrings_Valid_Success()
|
||||
{
|
||||
BaseItemKind[] input =
|
||||
{
|
||||
BaseItemKind.AggregateFolder,
|
||||
BaseItemKind.Audio,
|
||||
BaseItemKind.BasePluginFolder,
|
||||
BaseItemKind.CollectionFolder
|
||||
};
|
||||
|
||||
string[] expected =
|
||||
{
|
||||
"AggregateFolder",
|
||||
"Audio",
|
||||
"BasePluginFolder",
|
||||
"CollectionFolder"
|
||||
};
|
||||
|
||||
var res = RequestHelpers.GetItemTypeStrings(input);
|
||||
|
||||
Assert.Equal(expected, res);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
<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.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0" />
|
||||
@@ -27,7 +27,7 @@
|
||||
<!-- 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="StyleCop.Analyzers" Version="1.2.0-beta.376" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0" />
|
||||
@@ -22,7 +22,7 @@
|
||||
<!-- 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="StyleCop.Analyzers" Version="1.2.0-beta.376" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
88
tests/Jellyfin.Controller.Tests/BaseItemManagerTests.cs
Normal file
88
tests/Jellyfin.Controller.Tests/BaseItemManagerTests.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using MediaBrowser.Controller.BaseItemManager;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Controller.Tests
|
||||
{
|
||||
public class BaseItemManagerTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(typeof(Book), "LibraryEnabled", true)]
|
||||
[InlineData(typeof(Book), "LibraryDisabled", false)]
|
||||
[InlineData(typeof(MusicArtist), "Enabled", true)]
|
||||
[InlineData(typeof(MusicArtist), "ServerDisabled", false)]
|
||||
public void IsMetadataFetcherEnabled_ChecksOptions_ReturnsExpected(Type itemType, string fetcherName, bool expected)
|
||||
{
|
||||
BaseItem item = (BaseItem)Activator.CreateInstance(itemType)!;
|
||||
|
||||
var libraryOptions = new LibraryOptions
|
||||
{
|
||||
TypeOptions = new[]
|
||||
{
|
||||
new TypeOptions
|
||||
{
|
||||
Type = "Book",
|
||||
MetadataFetchers = new[] { "LibraryEnabled" }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var serverConfiguration = new ServerConfiguration();
|
||||
foreach (var typeConfig in serverConfiguration.MetadataOptions)
|
||||
{
|
||||
typeConfig.DisabledMetadataFetchers = new[] { "ServerDisabled" };
|
||||
}
|
||||
|
||||
var serverConfigurationManager = new Mock<IServerConfigurationManager>();
|
||||
serverConfigurationManager.Setup(scm => scm.Configuration)
|
||||
.Returns(serverConfiguration);
|
||||
|
||||
var baseItemManager = new BaseItemManager(serverConfigurationManager.Object);
|
||||
var actual = baseItemManager.IsMetadataFetcherEnabled(item, libraryOptions, fetcherName);
|
||||
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(typeof(Book), "LibraryEnabled", true)]
|
||||
[InlineData(typeof(Book), "LibraryDisabled", false)]
|
||||
[InlineData(typeof(MusicArtist), "Enabled", true)]
|
||||
[InlineData(typeof(MusicArtist), "ServerDisabled", false)]
|
||||
public void IsImageFetcherEnabled_ChecksOptions_ReturnsExpected(Type itemType, string fetcherName, bool expected)
|
||||
{
|
||||
BaseItem item = (BaseItem)Activator.CreateInstance(itemType)!;
|
||||
|
||||
var libraryOptions = new LibraryOptions
|
||||
{
|
||||
TypeOptions = new[]
|
||||
{
|
||||
new TypeOptions
|
||||
{
|
||||
Type = "Book",
|
||||
ImageFetchers = new[] { "LibraryEnabled" }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var serverConfiguration = new ServerConfiguration();
|
||||
foreach (var typeConfig in serverConfiguration.MetadataOptions)
|
||||
{
|
||||
typeConfig.DisabledImageFetchers = new[] { "ServerDisabled" };
|
||||
}
|
||||
|
||||
var serverConfigurationManager = new Mock<IServerConfigurationManager>();
|
||||
serverConfigurationManager.Setup(scm => scm.Configuration)
|
||||
.Returns(serverConfiguration);
|
||||
|
||||
var baseItemManager = new BaseItemManager(serverConfigurationManager.Object);
|
||||
var actual = baseItemManager.IsImageFetcherEnabled(item, libraryOptions, fetcherName);
|
||||
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,22 +13,22 @@ namespace Jellyfin.Controller.Tests
|
||||
|
||||
private static readonly FileSystemMetadata[] _lowerCaseFileSystemMetadata =
|
||||
{
|
||||
new ()
|
||||
new()
|
||||
{
|
||||
FullName = LowerCasePath + "/Artwork",
|
||||
IsDirectory = true
|
||||
},
|
||||
new ()
|
||||
new()
|
||||
{
|
||||
FullName = LowerCasePath + "/Some Other Folder",
|
||||
IsDirectory = true
|
||||
},
|
||||
new ()
|
||||
new()
|
||||
{
|
||||
FullName = LowerCasePath + "/Song 2.mp3",
|
||||
IsDirectory = false
|
||||
},
|
||||
new ()
|
||||
new()
|
||||
{
|
||||
FullName = LowerCasePath + "/Song 3.mp3",
|
||||
IsDirectory = false
|
||||
@@ -37,12 +37,12 @@ namespace Jellyfin.Controller.Tests
|
||||
|
||||
private static readonly FileSystemMetadata[] _upperCaseFileSystemMetadata =
|
||||
{
|
||||
new ()
|
||||
new()
|
||||
{
|
||||
FullName = UpperCasePath + "/Lyrics",
|
||||
IsDirectory = true
|
||||
},
|
||||
new ()
|
||||
new()
|
||||
{
|
||||
FullName = UpperCasePath + "/Song 1.mp3",
|
||||
IsDirectory = false
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
@@ -22,7 +22,7 @@
|
||||
<!-- 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="StyleCop.Analyzers" Version="1.2.0-beta.376" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Jellyfin.Dlna.Tests
|
||||
ModelDescription = "LG WebOSTV DMRplus",
|
||||
ModelName = "LG TV",
|
||||
ModelNumber = "1.0",
|
||||
Identification = new ()
|
||||
Identification = new()
|
||||
{
|
||||
FriendlyName = "My Device",
|
||||
Manufacturer = "LG Electronics",
|
||||
@@ -92,7 +92,7 @@ namespace Jellyfin.Dlna.Tests
|
||||
ModelDescription = "LG WebOSTV DMRplus",
|
||||
ModelName = "LG TV",
|
||||
ModelNumber = "1.0",
|
||||
Identification = new ()
|
||||
Identification = new()
|
||||
{
|
||||
FriendlyName = "My Device",
|
||||
Manufacturer = "LG Electronics",
|
||||
@@ -120,7 +120,7 @@ namespace Jellyfin.Dlna.Tests
|
||||
{
|
||||
Name = "Test Profile",
|
||||
FriendlyName = "My .*",
|
||||
Identification = new ()
|
||||
Identification = new()
|
||||
};
|
||||
|
||||
var deviceMatch = GetManager().IsMatch(device.ToDeviceIdentification(), profile.Identification);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
@@ -17,7 +17,7 @@
|
||||
<!-- 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="StyleCop.Analyzers" Version="1.2.0-beta.376" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -6,10 +6,17 @@ namespace Jellyfin.Extensions.Tests
|
||||
{
|
||||
public static class CopyToExtensionsTests
|
||||
{
|
||||
public static IEnumerable<object[]> CopyTo_Valid_Correct_TestData()
|
||||
public static TheoryData<IReadOnlyList<int>, IList<int>, int, IList<int>> CopyTo_Valid_Correct_TestData()
|
||||
{
|
||||
yield return new object[] { new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0, 0, 0, 0, 0, 0 }, 0, new[] { 0, 1, 2, 3, 4, 5 } };
|
||||
yield return new object[] { new[] { 0, 1, 2 }, new[] { 5, 4, 3, 2, 1, 0 }, 2, new[] { 5, 4, 0, 1, 2, 0 } };
|
||||
var data = new TheoryData<IReadOnlyList<int>, IList<int>, int, IList<int>>();
|
||||
|
||||
data.Add(
|
||||
new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0, 0, 0, 0, 0, 0 }, 0, new[] { 0, 1, 2, 3, 4, 5 });
|
||||
|
||||
data.Add(
|
||||
new[] { 0, 1, 2 }, new[] { 5, 4, 3, 2, 1, 0 }, 2, new[] { 5, 4, 0, 1, 2, 0 } );
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -20,13 +27,26 @@ namespace Jellyfin.Extensions.Tests
|
||||
Assert.Equal(expected, destination);
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> CopyTo_Invalid_ThrowsArgumentOutOfRangeException_TestData()
|
||||
public static TheoryData<IReadOnlyList<int>, IList<int>, int> CopyTo_Invalid_ThrowsArgumentOutOfRangeException_TestData()
|
||||
{
|
||||
yield return new object[] { new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0, 0, 0, 0, 0, 0 }, -1 };
|
||||
yield return new object[] { new[] { 0, 1, 2 }, new[] { 5, 4, 3, 2, 1, 0 }, 6 };
|
||||
yield return new object[] { new[] { 0, 1, 2 }, Array.Empty<int>(), 0 };
|
||||
yield return new object[] { new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0 }, 0 };
|
||||
yield return new object[] { new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0, 0, 0, 0, 0, 0 }, 1 };
|
||||
var data = new TheoryData<IReadOnlyList<int>, IList<int>, int>();
|
||||
|
||||
data.Add(
|
||||
new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0, 0, 0, 0, 0, 0 }, -1 );
|
||||
|
||||
data.Add(
|
||||
new[] { 0, 1, 2 }, new[] { 5, 4, 3, 2, 1, 0 }, 6 );
|
||||
|
||||
data.Add(
|
||||
new[] { 0, 1, 2 }, Array.Empty<int>(), 0 );
|
||||
|
||||
data.Add(
|
||||
new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0 }, 0 );
|
||||
|
||||
data.Add(
|
||||
new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0, 0, 0, 0, 0, 0 }, 1 );
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.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>
|
||||
@@ -23,7 +23,7 @@
|
||||
<!-- 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="StyleCop.Analyzers" Version="1.2.0-beta.376" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Jellyfin.Extensions.Tests.Json.Converters
|
||||
{
|
||||
public class JsonStringConverterTests
|
||||
{
|
||||
private readonly JsonSerializerOptions _jsonSerializerOptions = new ()
|
||||
private readonly JsonSerializerOptions _jsonSerializerOptions = new()
|
||||
{
|
||||
Converters =
|
||||
{
|
||||
|
||||
@@ -5,13 +5,11 @@ namespace Jellyfin.Extensions.Tests
|
||||
{
|
||||
public static class ShuffleExtensionsTests
|
||||
{
|
||||
private static readonly Random _rng = new Random();
|
||||
|
||||
[Fact]
|
||||
public static void Shuffle_Valid_Correct()
|
||||
{
|
||||
byte[] original = new byte[1 << 6];
|
||||
_rng.NextBytes(original);
|
||||
Random.Shared.NextBytes(original);
|
||||
byte[] shuffled = (byte[])original.Clone();
|
||||
shuffled.Shuffle();
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.MediaEncoding.Encoder;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Xunit;
|
||||
@@ -34,23 +32,21 @@ namespace Jellyfin.MediaEncoding.Tests
|
||||
Assert.Equal(valid, _encoderValidator.ValidateVersionInternal(versionOutput));
|
||||
}
|
||||
|
||||
private class GetFFmpegVersionTestData : IEnumerable<object?[]>
|
||||
private class GetFFmpegVersionTestData : TheoryData<string, Version?>
|
||||
{
|
||||
public IEnumerator<object?[]> GetEnumerator()
|
||||
public GetFFmpegVersionTestData()
|
||||
{
|
||||
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) };
|
||||
yield return new object?[] { EncoderValidatorTestsData.FFmpegV42Output, new Version(4, 2) };
|
||||
yield return new object?[] { EncoderValidatorTestsData.FFmpegV414Output, new Version(4, 1, 4) };
|
||||
yield return new object?[] { EncoderValidatorTestsData.FFmpegV404Output, new Version(4, 0, 4) };
|
||||
yield return new object?[] { EncoderValidatorTestsData.FFmpegGitUnknownOutput2, new Version(4, 0) };
|
||||
yield return new object?[] { EncoderValidatorTestsData.FFmpegGitUnknownOutput, null };
|
||||
Add(EncoderValidatorTestsData.FFmpegV44Output, new Version(4, 4));
|
||||
Add(EncoderValidatorTestsData.FFmpegV432Output, new Version(4, 3, 2));
|
||||
Add(EncoderValidatorTestsData.FFmpegV431Output, new Version(4, 3, 1));
|
||||
Add(EncoderValidatorTestsData.FFmpegV43Output, new Version(4, 3));
|
||||
Add(EncoderValidatorTestsData.FFmpegV421Output, new Version(4, 2, 1));
|
||||
Add(EncoderValidatorTestsData.FFmpegV42Output, new Version(4, 2));
|
||||
Add(EncoderValidatorTestsData.FFmpegV414Output, new Version(4, 1, 4));
|
||||
Add(EncoderValidatorTestsData.FFmpegV404Output, new Version(4, 0, 4));
|
||||
Add(EncoderValidatorTestsData.FFmpegGitUnknownOutput2, new Version(4, 0));
|
||||
Add(EncoderValidatorTestsData.FFmpegGitUnknownOutput, null);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" />
|
||||
<PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
@@ -31,7 +31,7 @@
|
||||
<!-- 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="StyleCop.Analyzers" Version="1.2.0-beta.376" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -5,8 +5,10 @@ using System.Text.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.MediaEncoding.Probing;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Globalization;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.MediaEncoding.Tests.Probing
|
||||
@@ -16,6 +18,19 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
|
||||
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
|
||||
private readonly ProbeResultNormalizer _probeResultNormalizer = new ProbeResultNormalizer(new NullLogger<EncoderValidatorTests>(), null);
|
||||
|
||||
[Theory]
|
||||
[InlineData("2997/125", 23.976f)]
|
||||
[InlineData("1/50", 0.02f)]
|
||||
[InlineData("25/1", 25f)]
|
||||
[InlineData("120/1", 120f)]
|
||||
[InlineData("1704753000/71073479", 23.98578237601117f)]
|
||||
[InlineData("0/0", null)]
|
||||
[InlineData("1/1000", 0.001f)]
|
||||
[InlineData("1/90000", 1.1111111E-05f)]
|
||||
[InlineData("1/48000", 2.0833333E-05f)]
|
||||
public void GetFrameRate_Success(string value, float? expected)
|
||||
=> Assert.Equal(expected, ProbeResultNormalizer.GetFrameRate(value));
|
||||
|
||||
[Fact]
|
||||
public void GetMediaInfo_MetaData_Success()
|
||||
{
|
||||
@@ -55,6 +70,72 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
|
||||
Assert.Equal("Just color bars", res.Overview);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetMediaInfo_Mp4MetaData_Success()
|
||||
{
|
||||
var bytes = File.ReadAllBytes("Test Data/Probing/video_mp4_metadata.json");
|
||||
var internalMediaInfoResult = JsonSerializer.Deserialize<InternalMediaInfoResult>(bytes, _jsonOptions);
|
||||
|
||||
// subtitle handling requires a localization object, set a mock to return the input string
|
||||
var mockLocalization = new Mock<ILocalizationManager>();
|
||||
mockLocalization.Setup(x => x.GetLocalizedString(It.IsAny<string>())).Returns<string>(x => x);
|
||||
ProbeResultNormalizer localizedProbeResultNormalizer = new ProbeResultNormalizer(new NullLogger<EncoderValidatorTests>(), mockLocalization.Object);
|
||||
|
||||
MediaInfo res = localizedProbeResultNormalizer.GetMediaInfo(internalMediaInfoResult, VideoType.VideoFile, false, "Test Data/Probing/video_mp4_metadata.mkv", MediaProtocol.File);
|
||||
|
||||
// [Video, Audio (Main), Audio (Commentary), Subtitle (Main, Spanish), Subtitle (Main, English), Subtitle (Commentary)
|
||||
Assert.Equal(6, res.MediaStreams.Count);
|
||||
|
||||
Assert.NotNull(res.VideoStream);
|
||||
Assert.Equal(res.MediaStreams[0], res.VideoStream);
|
||||
Assert.Equal(0, res.VideoStream.Index);
|
||||
Assert.Equal("h264", res.VideoStream.Codec);
|
||||
Assert.Equal("High", res.VideoStream.Profile);
|
||||
Assert.Equal(MediaStreamType.Video, res.VideoStream.Type);
|
||||
Assert.Equal(358, res.VideoStream.Height);
|
||||
Assert.Equal(720, res.VideoStream.Width);
|
||||
Assert.Equal("2.40:1", res.VideoStream.AspectRatio);
|
||||
Assert.Equal("yuv420p", res.VideoStream.PixelFormat);
|
||||
Assert.Equal(31d, res.VideoStream.Level);
|
||||
Assert.Equal(1, res.VideoStream.RefFrames);
|
||||
Assert.True(res.VideoStream.IsAVC);
|
||||
Assert.Equal(120f, res.VideoStream.RealFrameRate);
|
||||
Assert.Equal("1/90000", res.VideoStream.TimeBase);
|
||||
Assert.Equal(1147365, res.VideoStream.BitRate);
|
||||
Assert.Equal(8, res.VideoStream.BitDepth);
|
||||
Assert.True(res.VideoStream.IsDefault);
|
||||
Assert.Equal("und", res.VideoStream.Language);
|
||||
|
||||
Assert.Equal(MediaStreamType.Audio, res.MediaStreams[1].Type);
|
||||
Assert.Equal("aac", res.MediaStreams[1].Codec);
|
||||
Assert.Equal(7, res.MediaStreams[1].Channels);
|
||||
Assert.True(res.MediaStreams[1].IsDefault);
|
||||
Assert.Equal("eng", res.MediaStreams[1].Language);
|
||||
Assert.Equal("Surround 6.1", res.MediaStreams[1].Title);
|
||||
|
||||
Assert.Equal(MediaStreamType.Audio, res.MediaStreams[2].Type);
|
||||
Assert.Equal("aac", res.MediaStreams[2].Codec);
|
||||
Assert.Equal(2, res.MediaStreams[2].Channels);
|
||||
Assert.False(res.MediaStreams[2].IsDefault);
|
||||
Assert.Equal("eng", res.MediaStreams[2].Language);
|
||||
Assert.Equal("Commentary", res.MediaStreams[2].Title);
|
||||
|
||||
Assert.Equal("spa", res.MediaStreams[3].Language);
|
||||
Assert.Equal(MediaStreamType.Subtitle, res.MediaStreams[3].Type);
|
||||
Assert.Equal("DVDSUB", res.MediaStreams[3].Codec);
|
||||
Assert.Null(res.MediaStreams[3].Title);
|
||||
|
||||
Assert.Equal("eng", res.MediaStreams[4].Language);
|
||||
Assert.Equal(MediaStreamType.Subtitle, res.MediaStreams[4].Type);
|
||||
Assert.Equal("mov_text", res.MediaStreams[4].Codec);
|
||||
Assert.Null(res.MediaStreams[4].Title);
|
||||
|
||||
Assert.Equal("eng", res.MediaStreams[5].Language);
|
||||
Assert.Equal(MediaStreamType.Subtitle, res.MediaStreams[5].Type);
|
||||
Assert.Equal("mov_text", res.MediaStreams[5].Codec);
|
||||
Assert.Equal("Commentary", res.MediaStreams[5].Title);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetMediaInfo_MusicVideo_Success()
|
||||
{
|
||||
|
||||
@@ -38,10 +38,11 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> Parse_MultipleDialogues_TestData()
|
||||
public static TheoryData<string, IReadOnlyList<SubtitleTrackEvent>> Parse_MultipleDialogues_TestData()
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
var data = new TheoryData<string, IReadOnlyList<SubtitleTrackEvent>>();
|
||||
|
||||
data.Add(
|
||||
@"[Events]
|
||||
Format: Layer, Start, End, Text
|
||||
Dialogue: ,0:00:01.18,0:00:01.85,dialogue1
|
||||
@@ -65,8 +66,9 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests
|
||||
StartPositionTicks = 31800000,
|
||||
EndPositionTicks = 38500000
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -1,23 +1,11 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AutoFixture;
|
||||
using AutoFixture.AutoMoq;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.MediaEncoding.Subtitles;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.MediaEncoding.Subtitles.Tests
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
{
|
||||
"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_tag_string": "avc1",
|
||||
"codec_tag": "0x31637661",
|
||||
"width": 720,
|
||||
"height": 358,
|
||||
"coded_width": 720,
|
||||
"coded_height": 358,
|
||||
"closed_captions": 0,
|
||||
"has_b_frames": 2,
|
||||
"sample_aspect_ratio": "32:27",
|
||||
"display_aspect_ratio": "1280:537",
|
||||
"pix_fmt": "yuv420p",
|
||||
"level": 31,
|
||||
"color_range": "tv",
|
||||
"color_space": "smpte170m",
|
||||
"color_transfer": "bt709",
|
||||
"color_primaries": "smpte170m",
|
||||
"chroma_location": "left",
|
||||
"refs": 1,
|
||||
"is_avc": "true",
|
||||
"nal_length_size": "4",
|
||||
"r_frame_rate": "120/1",
|
||||
"avg_frame_rate": "1704753000/71073479",
|
||||
"time_base": "1/90000",
|
||||
"start_pts": 0,
|
||||
"start_time": "0.000000",
|
||||
"duration_ts": 1421469580,
|
||||
"duration": "15794.106444",
|
||||
"bit_rate": "1147365",
|
||||
"bits_per_raw_sample": "8",
|
||||
"nb_frames": "378834",
|
||||
"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": {
|
||||
"creation_time": "2021-09-13T22:42:42.000000Z",
|
||||
"language": "und",
|
||||
"handler_name": "VideoHandler",
|
||||
"vendor_id": "[0][0][0][0]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"codec_name": "aac",
|
||||
"codec_long_name": "AAC (Advanced Audio Coding)",
|
||||
"profile": "LC",
|
||||
"codec_type": "audio",
|
||||
"codec_tag_string": "mp4a",
|
||||
"codec_tag": "0x6134706d",
|
||||
"sample_fmt": "fltp",
|
||||
"sample_rate": "48000",
|
||||
"channels": 7,
|
||||
"bits_per_sample": 0,
|
||||
"r_frame_rate": "0/0",
|
||||
"avg_frame_rate": "0/0",
|
||||
"time_base": "1/48000",
|
||||
"start_pts": 0,
|
||||
"start_time": "0.000000",
|
||||
"duration_ts": 758115312,
|
||||
"duration": "15794.069000",
|
||||
"bit_rate": "224197",
|
||||
"nb_frames": "740348",
|
||||
"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": {
|
||||
"creation_time": "2021-09-13T22:42:42.000000Z",
|
||||
"language": "eng",
|
||||
"handler_name": "Surround 6.1",
|
||||
"vendor_id": "[0][0][0][0]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"codec_name": "aac",
|
||||
"codec_long_name": "AAC (Advanced Audio Coding)",
|
||||
"profile": "LC",
|
||||
"codec_type": "audio",
|
||||
"codec_tag_string": "mp4a",
|
||||
"codec_tag": "0x6134706d",
|
||||
"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/48000",
|
||||
"start_pts": 0,
|
||||
"start_time": "0.000000",
|
||||
"duration_ts": 758114304,
|
||||
"duration": "15794.048000",
|
||||
"bit_rate": "160519",
|
||||
"nb_frames": "740347",
|
||||
"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
|
||||
},
|
||||
"tags": {
|
||||
"creation_time": "2021-09-13T22:42:42.000000Z",
|
||||
"language": "eng",
|
||||
"handler_name": "Commentary",
|
||||
"vendor_id": "[0][0][0][0]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"codec_name": "dvd_subtitle",
|
||||
"codec_long_name": "DVD subtitles",
|
||||
"codec_type": "subtitle",
|
||||
"codec_tag_string": "mp4s",
|
||||
"codec_tag": "0x7334706d",
|
||||
"width": 720,
|
||||
"height": 480,
|
||||
"r_frame_rate": "0/0",
|
||||
"avg_frame_rate": "0/0",
|
||||
"time_base": "1/90000",
|
||||
"start_pts": 0,
|
||||
"start_time": "0.000000",
|
||||
"duration_ts": 1300301588,
|
||||
"duration": "14447.795422",
|
||||
"bit_rate": "2653",
|
||||
"nb_frames": "3545",
|
||||
"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
|
||||
},
|
||||
"tags": {
|
||||
"creation_time": "2021-09-13T22:42:42.000000Z",
|
||||
"language": "spa",
|
||||
"handler_name": "SubtitleHandler"
|
||||
}
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"codec_name": "mov_text",
|
||||
"codec_long_name": "MOV text",
|
||||
"codec_type": "subtitle",
|
||||
"codec_tag_string": "tx3g",
|
||||
"codec_tag": "0x67337874",
|
||||
"width": 853,
|
||||
"height": 51,
|
||||
"r_frame_rate": "0/0",
|
||||
"avg_frame_rate": "0/0",
|
||||
"time_base": "1/90000",
|
||||
"start_pts": 0,
|
||||
"start_time": "0.000000",
|
||||
"duration_ts": 1401339330,
|
||||
"duration": "15570.437000",
|
||||
"bit_rate": "88",
|
||||
"nb_frames": "5079",
|
||||
"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": {
|
||||
"creation_time": "2021-09-13T22:42:42.000000Z",
|
||||
"language": "eng",
|
||||
"handler_name": "SubtitleHandler"
|
||||
}
|
||||
},
|
||||
{
|
||||
"index": 5,
|
||||
"codec_name": "mov_text",
|
||||
"codec_long_name": "MOV text",
|
||||
"codec_type": "subtitle",
|
||||
"codec_tag_string": "tx3g",
|
||||
"codec_tag": "0x67337874",
|
||||
"width": 853,
|
||||
"height": 51,
|
||||
"r_frame_rate": "0/0",
|
||||
"avg_frame_rate": "0/0",
|
||||
"time_base": "1/90000",
|
||||
"start_pts": 0,
|
||||
"start_time": "0.000000",
|
||||
"duration_ts": 1370580300,
|
||||
"duration": "15228.670000",
|
||||
"bit_rate": "18",
|
||||
"nb_frames": "1563",
|
||||
"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
|
||||
},
|
||||
"tags": {
|
||||
"creation_time": "2021-09-13T22:42:42.000000Z",
|
||||
"language": "eng",
|
||||
"handler_name": "Commentary"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Common.Cryptography;
|
||||
using MediaBrowser.Model.Cryptography;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Common.Tests.Cryptography
|
||||
namespace Jellyfin.Model.Tests.Cryptography
|
||||
{
|
||||
public static class PasswordHashTests
|
||||
{
|
||||
@@ -19,18 +19,16 @@ namespace Jellyfin.Common.Tests.Cryptography
|
||||
Assert.Throws<ArgumentException>(() => new PasswordHash(string.Empty, Array.Empty<byte>()));
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> Parse_Valid_TestData()
|
||||
public static TheoryData<string, PasswordHash> Parse_Valid_TestData()
|
||||
{
|
||||
var data = new TheoryData<string, PasswordHash>();
|
||||
// Id
|
||||
yield return new object[]
|
||||
{
|
||||
data.Add(
|
||||
"$PBKDF2",
|
||||
new PasswordHash("PBKDF2", Array.Empty<byte>())
|
||||
};
|
||||
new PasswordHash("PBKDF2", Array.Empty<byte>()));
|
||||
|
||||
// Id + parameter
|
||||
yield return new object[]
|
||||
{
|
||||
data.Add(
|
||||
"$PBKDF2$iterations=1000",
|
||||
new PasswordHash(
|
||||
"PBKDF2",
|
||||
@@ -39,12 +37,10 @@ namespace Jellyfin.Common.Tests.Cryptography
|
||||
new Dictionary<string, string>()
|
||||
{
|
||||
{ "iterations", "1000" },
|
||||
})
|
||||
};
|
||||
}));
|
||||
|
||||
// Id + parameters
|
||||
yield return new object[]
|
||||
{
|
||||
data.Add(
|
||||
"$PBKDF2$iterations=1000,m=120",
|
||||
new PasswordHash(
|
||||
"PBKDF2",
|
||||
@@ -54,34 +50,28 @@ namespace Jellyfin.Common.Tests.Cryptography
|
||||
{
|
||||
{ "iterations", "1000" },
|
||||
{ "m", "120" }
|
||||
})
|
||||
};
|
||||
}));
|
||||
|
||||
// Id + hash
|
||||
yield return new object[]
|
||||
{
|
||||
data.Add(
|
||||
"$PBKDF2$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D",
|
||||
new PasswordHash(
|
||||
"PBKDF2",
|
||||
Convert.FromHexString("62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D"),
|
||||
Array.Empty<byte>(),
|
||||
new Dictionary<string, string>())
|
||||
};
|
||||
new Dictionary<string, string>()));
|
||||
|
||||
// Id + salt + hash
|
||||
yield return new object[]
|
||||
{
|
||||
data.Add(
|
||||
"$PBKDF2$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D",
|
||||
new PasswordHash(
|
||||
"PBKDF2",
|
||||
Convert.FromHexString("62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D"),
|
||||
Convert.FromHexString("69F420"),
|
||||
new Dictionary<string, string>())
|
||||
};
|
||||
new Dictionary<string, string>()));
|
||||
|
||||
// Id + parameter + hash
|
||||
yield return new object[]
|
||||
{
|
||||
data.Add(
|
||||
"$PBKDF2$iterations=1000$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D",
|
||||
new PasswordHash(
|
||||
"PBKDF2",
|
||||
@@ -90,12 +80,9 @@ namespace Jellyfin.Common.Tests.Cryptography
|
||||
new Dictionary<string, string>()
|
||||
{
|
||||
{ "iterations", "1000" }
|
||||
})
|
||||
};
|
||||
|
||||
}));
|
||||
// Id + parameters + hash
|
||||
yield return new object[]
|
||||
{
|
||||
data.Add(
|
||||
"$PBKDF2$iterations=1000,m=120$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D",
|
||||
new PasswordHash(
|
||||
"PBKDF2",
|
||||
@@ -105,12 +92,9 @@ namespace Jellyfin.Common.Tests.Cryptography
|
||||
{
|
||||
{ "iterations", "1000" },
|
||||
{ "m", "120" }
|
||||
})
|
||||
};
|
||||
|
||||
}));
|
||||
// Id + parameters + salt + hash
|
||||
yield return new object[]
|
||||
{
|
||||
data.Add(
|
||||
"$PBKDF2$iterations=1000,m=120$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D",
|
||||
new PasswordHash(
|
||||
"PBKDF2",
|
||||
@@ -120,8 +104,8 @@ namespace Jellyfin.Common.Tests.Cryptography
|
||||
{
|
||||
{ "iterations", "1000" },
|
||||
{ "m", "120" }
|
||||
})
|
||||
};
|
||||
}));
|
||||
return data;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using Xunit;
|
||||
|
||||
@@ -6,12 +5,11 @@ namespace Jellyfin.Model.Tests.Entities
|
||||
{
|
||||
public class MediaStreamTests
|
||||
{
|
||||
public static IEnumerable<object[]> Get_DisplayTitle_TestData()
|
||||
public static TheoryData<MediaStream, string> Get_DisplayTitle_TestData()
|
||||
{
|
||||
return new List<object[]>
|
||||
{
|
||||
new object[]
|
||||
{
|
||||
var data = new TheoryData<MediaStream, string>();
|
||||
|
||||
data.Add(
|
||||
new MediaStream
|
||||
{
|
||||
Type = MediaStreamType.Subtitle,
|
||||
@@ -21,61 +19,57 @@ namespace Jellyfin.Model.Tests.Entities
|
||||
IsDefault = false,
|
||||
Codec = "ASS"
|
||||
},
|
||||
"English - Und - ASS"
|
||||
},
|
||||
new object[]
|
||||
"English - Und - ASS");
|
||||
|
||||
data.Add(
|
||||
new MediaStream
|
||||
{
|
||||
new MediaStream
|
||||
{
|
||||
Type = MediaStreamType.Subtitle,
|
||||
Title = "English",
|
||||
Language = string.Empty,
|
||||
IsForced = false,
|
||||
IsDefault = false,
|
||||
Codec = string.Empty
|
||||
},
|
||||
"English - Und"
|
||||
Type = MediaStreamType.Subtitle,
|
||||
Title = "English",
|
||||
Language = string.Empty,
|
||||
IsForced = false,
|
||||
IsDefault = false,
|
||||
Codec = string.Empty
|
||||
},
|
||||
new object[]
|
||||
"English - Und");
|
||||
|
||||
data.Add(
|
||||
new MediaStream
|
||||
{
|
||||
new MediaStream
|
||||
{
|
||||
Type = MediaStreamType.Subtitle,
|
||||
Title = "English",
|
||||
Language = "EN",
|
||||
IsForced = false,
|
||||
IsDefault = false,
|
||||
Codec = string.Empty
|
||||
},
|
||||
"English"
|
||||
Type = MediaStreamType.Subtitle,
|
||||
Title = "English",
|
||||
Language = "EN",
|
||||
IsForced = false,
|
||||
IsDefault = false,
|
||||
Codec = string.Empty
|
||||
},
|
||||
new object[]
|
||||
"English");
|
||||
|
||||
data.Add(
|
||||
new MediaStream
|
||||
{
|
||||
new MediaStream
|
||||
{
|
||||
Type = MediaStreamType.Subtitle,
|
||||
Title = "English",
|
||||
Language = "EN",
|
||||
IsForced = true,
|
||||
IsDefault = true,
|
||||
Codec = "SRT"
|
||||
},
|
||||
"English - Default - Forced - SRT"
|
||||
Type = MediaStreamType.Subtitle,
|
||||
Title = "English",
|
||||
Language = "EN",
|
||||
IsForced = true,
|
||||
IsDefault = true,
|
||||
Codec = "SRT"
|
||||
},
|
||||
new object[]
|
||||
"English - Default - Forced - SRT");
|
||||
|
||||
data.Add(
|
||||
new MediaStream
|
||||
{
|
||||
new MediaStream
|
||||
{
|
||||
Type = MediaStreamType.Subtitle,
|
||||
Title = null,
|
||||
Language = null,
|
||||
IsForced = false,
|
||||
IsDefault = false,
|
||||
Codec = null
|
||||
},
|
||||
"Und"
|
||||
}
|
||||
};
|
||||
Type = MediaStreamType.Subtitle,
|
||||
Title = null,
|
||||
Language = null,
|
||||
IsForced = false,
|
||||
IsDefault = false,
|
||||
Codec = null
|
||||
},
|
||||
"Und");
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0" />
|
||||
@@ -17,7 +17,7 @@
|
||||
<!-- 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="StyleCop.Analyzers" Version="1.2.0-beta.376" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
160
tests/Jellyfin.Model.Tests/Net/MimeTypesTests.cs
Normal file
160
tests/Jellyfin.Model.Tests/Net/MimeTypesTests.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using MediaBrowser.Model.Net;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Model.Tests.Net
|
||||
{
|
||||
public class MimeTypesTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(".dll", "application/octet-stream")]
|
||||
[InlineData(".log", "text/plain")]
|
||||
[InlineData(".srt", "application/x-subrip")]
|
||||
[InlineData(".html", "text/html; charset=UTF-8")]
|
||||
[InlineData(".htm", "text/html; charset=UTF-8")]
|
||||
[InlineData(".7z", "application/x-7z-compressed")]
|
||||
[InlineData(".azw", "application/vnd.amazon.ebook")]
|
||||
[InlineData(".azw3", "application/vnd.amazon.ebook")]
|
||||
[InlineData(".eot", "application/vnd.ms-fontobject")]
|
||||
[InlineData(".epub", "application/epub+zip")]
|
||||
[InlineData(".json", "application/json")]
|
||||
[InlineData(".mobi", "application/x-mobipocket-ebook")]
|
||||
[InlineData(".opf", "application/oebps-package+xml")]
|
||||
[InlineData(".pdf", "application/pdf")]
|
||||
[InlineData(".rar", "application/vnd.rar")]
|
||||
[InlineData(".ttml", "application/ttml+xml")]
|
||||
[InlineData(".wasm", "application/wasm")]
|
||||
[InlineData(".xml", "application/xml")]
|
||||
[InlineData(".zip", "application/zip")]
|
||||
[InlineData(".bmp", "image/bmp")]
|
||||
[InlineData(".gif", "image/gif")]
|
||||
[InlineData(".ico", "image/vnd.microsoft.icon")]
|
||||
[InlineData(".jpg", "image/jpeg")]
|
||||
[InlineData(".jpeg", "image/jpeg")]
|
||||
[InlineData(".png", "image/png")]
|
||||
[InlineData(".svg", "image/svg+xml")]
|
||||
[InlineData(".svgz", "image/svg+xml")]
|
||||
[InlineData(".tbn", "image/jpeg")]
|
||||
[InlineData(".tif", "image/tiff")]
|
||||
[InlineData(".tiff", "image/tiff")]
|
||||
[InlineData(".webp", "image/webp")]
|
||||
[InlineData(".ttf", "font/ttf")]
|
||||
[InlineData(".woff", "font/woff")]
|
||||
[InlineData(".woff2", "font/woff2")]
|
||||
[InlineData(".ass", "text/x-ssa")]
|
||||
[InlineData(".ssa", "text/x-ssa")]
|
||||
[InlineData(".css", "text/css")]
|
||||
[InlineData(".csv", "text/csv")]
|
||||
[InlineData(".edl", "text/plain")]
|
||||
[InlineData(".txt", "text/plain")]
|
||||
[InlineData(".vtt", "text/vtt")]
|
||||
[InlineData(".3gp", "video/3gpp")]
|
||||
[InlineData(".3g2", "video/3gpp2")]
|
||||
[InlineData(".asf", "video/x-ms-asf")]
|
||||
[InlineData(".avi", "video/x-msvideo")]
|
||||
[InlineData(".flv", "video/x-flv")]
|
||||
[InlineData(".mp4", "video/mp4")]
|
||||
[InlineData(".m4v", "video/x-m4v")]
|
||||
[InlineData(".mpegts", "video/mp2t")]
|
||||
[InlineData(".mpg", "video/mpeg")]
|
||||
[InlineData(".mkv", "video/x-matroska")]
|
||||
[InlineData(".mov", "video/quicktime")]
|
||||
[InlineData(".ogv", "video/ogg")]
|
||||
[InlineData(".ts", "video/mp2t")]
|
||||
[InlineData(".webm", "video/webm")]
|
||||
[InlineData(".wmv", "video/x-ms-wmv")]
|
||||
[InlineData(".aac", "audio/aac")]
|
||||
[InlineData(".ac3", "audio/ac3")]
|
||||
[InlineData(".ape", "audio/x-ape")]
|
||||
[InlineData(".dsf", "audio/dsf")]
|
||||
[InlineData(".dsp", "audio/dsp")]
|
||||
[InlineData(".flac", "audio/flac")]
|
||||
[InlineData(".m4a", "audio/mp4")]
|
||||
[InlineData(".m4b", "audio/m4b")]
|
||||
[InlineData(".mid", "audio/midi")]
|
||||
[InlineData(".midi", "audio/midi")]
|
||||
[InlineData(".mp3", "audio/mpeg")]
|
||||
[InlineData(".oga", "audio/ogg")]
|
||||
[InlineData(".ogg", "audio/ogg")]
|
||||
[InlineData(".opus", "audio/ogg")]
|
||||
[InlineData(".vorbis", "audio/vorbis")]
|
||||
[InlineData(".wav", "audio/wav")]
|
||||
[InlineData(".webma", "audio/webm")]
|
||||
[InlineData(".wma", "audio/x-ms-wma")]
|
||||
[InlineData(".wv", "audio/x-wavpack")]
|
||||
[InlineData(".xsp", "audio/xsp")]
|
||||
public void GetMimeType_Valid_ReturnsCorrectResult(string input, string expectedResult)
|
||||
{
|
||||
Assert.Equal(expectedResult, MimeTypes.GetMimeType(input, null));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("application/epub+zip", ".epub")]
|
||||
[InlineData("application/json", ".json")]
|
||||
[InlineData("application/oebps-package+xml", ".opf")]
|
||||
[InlineData("application/pdf", ".pdf")]
|
||||
[InlineData("application/ttml+xml", ".ttml")]
|
||||
[InlineData("application/vnd.amazon.ebook", ".azw")]
|
||||
[InlineData("application/vnd.ms-fontobject", ".eot")]
|
||||
[InlineData("application/vnd.rar", ".rar")]
|
||||
[InlineData("application/wasm", ".wasm")]
|
||||
[InlineData("application/x-7z-compressed", ".7z")]
|
||||
[InlineData("application/x-cbz", ".cbz")]
|
||||
[InlineData("application/x-javascript", ".js")]
|
||||
[InlineData("application/x-mobipocket-ebook", ".mobi")]
|
||||
[InlineData("application/x-mpegURL", ".m3u8")]
|
||||
[InlineData("application/x-subrip", ".srt")]
|
||||
[InlineData("application/xml", ".xml")]
|
||||
[InlineData("application/zip", ".zip")]
|
||||
[InlineData("audio/aac", ".aac")]
|
||||
[InlineData("audio/ac3", ".ac3")]
|
||||
[InlineData("audio/dsf", ".dsf")]
|
||||
[InlineData("audio/dsp", ".dsp")]
|
||||
[InlineData("audio/flac", ".flac")]
|
||||
[InlineData("audio/m4b", ".m4b")]
|
||||
[InlineData("audio/mp4", ".m4a")]
|
||||
[InlineData("audio/vorbis", ".vorbis")]
|
||||
[InlineData("audio/wav", ".wav")]
|
||||
[InlineData("audio/x-aac", ".aac")]
|
||||
[InlineData("audio/x-ape", ".ape")]
|
||||
[InlineData("audio/x-ms-wma", ".wma")]
|
||||
[InlineData("audio/x-wavpack", ".wv")]
|
||||
[InlineData("audio/xsp", ".xsp")]
|
||||
[InlineData("font/ttf", ".ttf")]
|
||||
[InlineData("font/woff", ".woff")]
|
||||
[InlineData("font/woff2", ".woff2")]
|
||||
[InlineData("image/bmp", ".bmp")]
|
||||
[InlineData("image/gif", ".gif")]
|
||||
[InlineData("image/jpg", ".jpg")]
|
||||
[InlineData("image/jpeg", ".jpg")]
|
||||
[InlineData("image/png", ".png")]
|
||||
[InlineData("image/svg+xml", ".svg")]
|
||||
[InlineData("image/tiff", ".tif")]
|
||||
[InlineData("image/vnd.microsoft.icon", ".ico")]
|
||||
[InlineData("image/webp", ".webp")]
|
||||
[InlineData("image/x-png", ".png")]
|
||||
[InlineData("text/css", ".css")]
|
||||
[InlineData("text/csv", ".csv")]
|
||||
[InlineData("text/plain", ".txt")]
|
||||
[InlineData("text/rtf", ".rtf")]
|
||||
[InlineData("text/vtt", ".vtt")]
|
||||
[InlineData("text/x-ssa", ".ssa")]
|
||||
[InlineData("video/3gpp", ".3gp")]
|
||||
[InlineData("video/3gpp2", ".3g2")]
|
||||
[InlineData("video/mp2t", ".ts")]
|
||||
[InlineData("video/mp4", ".mp4")]
|
||||
[InlineData("video/ogg", ".ogv")]
|
||||
[InlineData("video/quicktime", ".mov")]
|
||||
[InlineData("video/vnd.mpeg.dash.mpd", ".mpd")]
|
||||
[InlineData("video/webm", ".webm")]
|
||||
[InlineData("video/x-flv", ".flv")]
|
||||
[InlineData("video/x-m4v", ".m4v")]
|
||||
[InlineData("video/x-matroska", ".mkv")]
|
||||
[InlineData("video/x-ms-asf", ".asf")]
|
||||
[InlineData("video/x-ms-wmv", ".wmv")]
|
||||
[InlineData("video/x-msvideo", ".avi")]
|
||||
public void ToExtension_Valid_ReturnsCorrectResult(string input, string expectedResult)
|
||||
{
|
||||
Assert.Equal(expectedResult, MimeTypes.ToExtension(input));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Collections.Generic;
|
||||
using Emby.Naming.AudioBook;
|
||||
using Emby.Naming.Common;
|
||||
using Xunit;
|
||||
@@ -9,29 +8,29 @@ namespace Jellyfin.Naming.Tests.AudioBook
|
||||
{
|
||||
private readonly NamingOptions _namingOptions = new NamingOptions();
|
||||
|
||||
public static IEnumerable<object[]> Resolve_ValidFileNameTestData()
|
||||
public static TheoryData<AudioBookFileInfo> Resolve_ValidFileNameTestData()
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
var data = new TheoryData<AudioBookFileInfo>();
|
||||
|
||||
data.Add(
|
||||
new AudioBookFileInfo(
|
||||
@"/server/AudioBooks/Larry Potter/Larry Potter.mp3",
|
||||
"mp3")
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
"mp3"));
|
||||
|
||||
data.Add(
|
||||
new AudioBookFileInfo(
|
||||
@"/server/AudioBooks/Berry Potter/Chapter 1 .ogg",
|
||||
"ogg",
|
||||
chapterNumber: 1)
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
chapterNumber: 1));
|
||||
|
||||
data.Add(
|
||||
new AudioBookFileInfo(
|
||||
@"/server/AudioBooks/Nerry Potter/Part 3 - Chapter 2.mp3",
|
||||
"mp3",
|
||||
chapterNumber: 2,
|
||||
partNumber: 3)
|
||||
};
|
||||
partNumber: 3));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
||||
@@ -10,7 +10,6 @@ namespace Jellyfin.Naming.Tests.Common
|
||||
{
|
||||
var options = new NamingOptions();
|
||||
|
||||
Assert.NotEmpty(options.VideoFileStackingRegexes);
|
||||
Assert.NotEmpty(options.CleanDateTimeRegexes);
|
||||
Assert.NotEmpty(options.CleanStringRegexes);
|
||||
Assert.NotEmpty(options.EpisodeWithoutSeasonRegexes);
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0" />
|
||||
@@ -25,7 +25,7 @@
|
||||
<!-- 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="StyleCop.Analyzers" Version="1.2.0-beta.376" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace Jellyfin.Naming.Tests.TV
|
||||
{
|
||||
public class AbsoluteEpisodeNumberTests
|
||||
{
|
||||
private readonly EpisodeResolver _resolver = new EpisodeResolver(new NamingOptions());
|
||||
|
||||
[Theory]
|
||||
[InlineData("The Simpsons/12.avi", 12)]
|
||||
[InlineData("The Simpsons/The Simpsons 12.avi", 12)]
|
||||
@@ -16,10 +18,7 @@ namespace Jellyfin.Naming.Tests.TV
|
||||
[InlineData("The Simpsons/The Simpsons 101.avi", 101)]
|
||||
public void GetEpisodeNumberFromFileTest(string path, int episodeNumber)
|
||||
{
|
||||
var options = new NamingOptions();
|
||||
|
||||
var result = new EpisodeResolver(options)
|
||||
.Resolve(path, false, null, null, true);
|
||||
var result = _resolver.Resolve(path, false, null, null, true);
|
||||
|
||||
Assert.Equal(episodeNumber, result?.EpisodeNumber);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace Jellyfin.Naming.Tests.TV
|
||||
{
|
||||
public class DailyEpisodeTests
|
||||
{
|
||||
private readonly EpisodeResolver _resolver = new EpisodeResolver(new NamingOptions());
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"/server/anything_1996.11.14.mp4", "anything", 1996, 11, 14)]
|
||||
[InlineData(@"/server/anything_1996-11-14.mp4", "anything", 1996, 11, 14)]
|
||||
@@ -16,10 +18,7 @@ namespace Jellyfin.Naming.Tests.TV
|
||||
// TODO: [InlineData(@"/server/Last Man Standing_KTLADT_2018_05_25_01_28_00.wtv", "Last Man Standing", 2018, 05, 25)]
|
||||
public void Test(string path, string seriesName, int? year, int? month, int? day)
|
||||
{
|
||||
var options = new NamingOptions();
|
||||
|
||||
var result = new EpisodeResolver(options)
|
||||
.Resolve(path, false);
|
||||
var result = _resolver.Resolve(path, false);
|
||||
|
||||
Assert.Null(result?.SeasonNumber);
|
||||
Assert.Null(result?.EpisodeNumber);
|
||||
|
||||
@@ -71,9 +71,9 @@ namespace Jellyfin.Naming.Tests.TV
|
||||
[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
|
||||
[InlineData("Season 2/Episode 21 - 94 Meetings.mp4", 21)] // Title starts with a number
|
||||
[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)]
|
||||
// [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)]
|
||||
// TODO: [InlineData("Season 2/7 12 Angry Men.avi", 7)]
|
||||
// TODO: [InlineData("Season 02/02x03x04x15 - Ep Name.mp4", 2)]
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace Jellyfin.Naming.Tests.TV
|
||||
{
|
||||
public class EpisodeNumberWithoutSeasonTests
|
||||
{
|
||||
private readonly EpisodeResolver _resolver = new EpisodeResolver(new NamingOptions());
|
||||
|
||||
[Theory]
|
||||
[InlineData(8, @"The Simpsons/The Simpsons.S25E08.Steal this episode.mp4")]
|
||||
[InlineData(2, @"The Simpsons/The Simpsons - 02 - Ep Name.avi")]
|
||||
@@ -24,10 +26,7 @@ namespace Jellyfin.Naming.Tests.TV
|
||||
// TODO: [InlineData(13, @"Case Closed (1996-2007)/Case Closed - 13.mkv")]
|
||||
public void GetEpisodeNumberFromFileTest(int episodeNumber, string path)
|
||||
{
|
||||
var options = new NamingOptions();
|
||||
|
||||
var result = new EpisodeResolver(options)
|
||||
.Resolve(path, false);
|
||||
var result = _resolver.Resolve(path, false);
|
||||
|
||||
Assert.Equal(episodeNumber, result?.EpisodeNumber);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace Jellyfin.Naming.Tests.TV
|
||||
{
|
||||
public class EpisodePathParserTest
|
||||
{
|
||||
private readonly NamingOptions _namingOptions = new NamingOptions();
|
||||
|
||||
[Theory]
|
||||
[InlineData("/media/Foo/Foo-S01E01", true, "Foo", 1, 1)]
|
||||
[InlineData("/media/Foo - S04E011", true, "Foo", 4, 11)]
|
||||
@@ -36,8 +38,7 @@ namespace Jellyfin.Naming.Tests.TV
|
||||
// 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", "The Legend of Condor Heroes 2017", 1, 7)]
|
||||
public void ParseEpisodesCorrectly(string path, bool isDirectory, string name, int season, int episode)
|
||||
{
|
||||
NamingOptions o = new NamingOptions();
|
||||
EpisodePathParser p = new EpisodePathParser(o);
|
||||
EpisodePathParser p = new EpisodePathParser(_namingOptions);
|
||||
var res = p.Parse(path, isDirectory);
|
||||
|
||||
Assert.True(res.Success);
|
||||
@@ -50,8 +51,7 @@ namespace Jellyfin.Naming.Tests.TV
|
||||
[InlineData("/test/01-03.avi", true, true)]
|
||||
public void EpisodePathParserTest_DifferentExpressionsParameters(string path, bool? isNamed, bool? isOptimistic)
|
||||
{
|
||||
NamingOptions o = new NamingOptions();
|
||||
EpisodePathParser p = new EpisodePathParser(o);
|
||||
EpisodePathParser p = new EpisodePathParser(_namingOptions);
|
||||
var res = p.Parse(path, false, isNamed, isOptimistic);
|
||||
|
||||
Assert.True(res.Success);
|
||||
@@ -60,8 +60,7 @@ namespace Jellyfin.Naming.Tests.TV
|
||||
[Fact]
|
||||
public void EpisodePathParserTest_FalsePositivePixelRate()
|
||||
{
|
||||
NamingOptions o = new NamingOptions();
|
||||
EpisodePathParser p = new EpisodePathParser(o);
|
||||
EpisodePathParser p = new EpisodePathParser(_namingOptions);
|
||||
var res = p.Parse("Series Special (1920x1080).mkv", false);
|
||||
|
||||
Assert.False(res.Success);
|
||||
@@ -70,14 +69,14 @@ namespace Jellyfin.Naming.Tests.TV
|
||||
[Fact]
|
||||
public void EpisodeResolverTest_WrongExtension()
|
||||
{
|
||||
var res = new EpisodeResolver(new NamingOptions()).Resolve("test.mp3", false);
|
||||
var res = new EpisodeResolver(_namingOptions).Resolve("test.mp3", false);
|
||||
Assert.Null(res);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EpisodeResolverTest_WrongExtensionStub()
|
||||
{
|
||||
var res = new EpisodeResolver(new NamingOptions()).Resolve("dvd.disc", false);
|
||||
var res = new EpisodeResolver(_namingOptions).Resolve("dvd.disc", false);
|
||||
Assert.NotNull(res);
|
||||
Assert.True(res!.IsStub);
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
using Emby.Naming.Common;
|
||||
using Emby.Naming.TV;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Naming.Tests.TV
|
||||
{
|
||||
public class EpisodeWithoutSeasonTests
|
||||
{
|
||||
// TODO: [Theory]
|
||||
// TODO: [InlineData(@"/server/anything_ep02.mp4", "anything", null, 2)]
|
||||
// TODO: [InlineData(@"/server/anything_ep_02.mp4", "anything", null, 2)]
|
||||
// TODO: [InlineData(@"/server/anything_part.II.mp4", "anything", null, null)]
|
||||
// TODO: [InlineData(@"/server/anything_pt.II.mp4", "anything", null, null)]
|
||||
// TODO: [InlineData(@"/server/anything_pt_II.mp4", "anything", null, null)]
|
||||
public void Test(string path, string seriesName, int? seasonNumber, int? episodeNumber)
|
||||
{
|
||||
var options = new NamingOptions();
|
||||
|
||||
var result = new EpisodeResolver(options)
|
||||
.Resolve(path, false);
|
||||
|
||||
Assert.Equal(seasonNumber, result?.SeasonNumber);
|
||||
Assert.Equal(episodeNumber, result?.EpisodeNumber);
|
||||
Assert.Equal(seriesName, result?.SeriesName, ignoreCase: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ namespace Jellyfin.Naming.Tests.TV
|
||||
{
|
||||
public class MultiEpisodeTests
|
||||
{
|
||||
private readonly EpisodePathParser _episodePathParser = new EpisodePathParser(new NamingOptions());
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"Season 1/4x01 – 20 Hours in America (1).mkv", null)]
|
||||
[InlineData(@"Season 1/01x02 blah.avi", null)]
|
||||
@@ -69,10 +71,7 @@ namespace Jellyfin.Naming.Tests.TV
|
||||
[InlineData(@"Season 1/MOONLIGHTING_s01e01-e04", 4)]
|
||||
public void TestGetEndingEpisodeNumberFromFile(string filename, int? endingEpisodeNumber)
|
||||
{
|
||||
var options = new NamingOptions();
|
||||
|
||||
var result = new EpisodePathParser(options)
|
||||
.Parse(filename, false);
|
||||
var result = _episodePathParser.Parse(filename, false);
|
||||
|
||||
Assert.Equal(result.EndingEpisodeNumber, endingEpisodeNumber);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Jellyfin.Naming.Tests.TV
|
||||
{
|
||||
public class SeasonNumberTests
|
||||
{
|
||||
private readonly NamingOptions _namingOptions = new NamingOptions();
|
||||
private readonly EpisodeResolver _resolver = new EpisodeResolver(new NamingOptions());
|
||||
|
||||
[Theory]
|
||||
[InlineData("The Daily Show/The Daily Show 25x22 - [WEBDL-720p][AAC 2.0][x264] Noah Baumbach-TBS.mkv", 25)]
|
||||
@@ -56,8 +56,7 @@ namespace Jellyfin.Naming.Tests.TV
|
||||
// TODO: [InlineData(@"Seinfeld/Seinfeld 0807 The Checks.avi", 8)]
|
||||
public void GetSeasonNumberFromEpisodeFileTest(string path, int? expected)
|
||||
{
|
||||
var result = new EpisodeResolver(_namingOptions)
|
||||
.Resolve(path, false);
|
||||
var result = _resolver.Resolve(path, false);
|
||||
|
||||
Assert.Equal(expected, result?.SeasonNumber);
|
||||
}
|
||||
|
||||
29
tests/Jellyfin.Naming.Tests/TV/SeriesPathParserTest.cs
Normal file
29
tests/Jellyfin.Naming.Tests/TV/SeriesPathParserTest.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Emby.Naming.Common;
|
||||
using Emby.Naming.TV;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Naming.Tests.TV
|
||||
{
|
||||
public class SeriesPathParserTest
|
||||
{
|
||||
private readonly NamingOptions _namingOptions = new NamingOptions();
|
||||
|
||||
[Theory]
|
||||
[InlineData("The.Show.S01", "The.Show")]
|
||||
[InlineData("/The.Show.S01", "The.Show")]
|
||||
[InlineData("/some/place/The.Show.S01", "The.Show")]
|
||||
[InlineData("/something/The.Show.S01", "The.Show")]
|
||||
[InlineData("The Show Season 10", "The Show")]
|
||||
[InlineData("The Show S01E01", "The Show")]
|
||||
[InlineData("The Show S01E01 Episode", "The Show")]
|
||||
[InlineData("/something/The Show/Season 1", "The Show")]
|
||||
[InlineData("/something/The Show/S01", "The Show")]
|
||||
public void SeriesPathParserParseTest(string path, string name)
|
||||
{
|
||||
var res = SeriesPathParser.Parse(_namingOptions, path);
|
||||
|
||||
Assert.Equal(name, res.SeriesName);
|
||||
Assert.True(res.Success);
|
||||
}
|
||||
}
|
||||
}
|
||||
29
tests/Jellyfin.Naming.Tests/TV/SeriesResolverTests.cs
Normal file
29
tests/Jellyfin.Naming.Tests/TV/SeriesResolverTests.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Emby.Naming.Common;
|
||||
using Emby.Naming.TV;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Naming.Tests.TV
|
||||
{
|
||||
public class SeriesResolverTests
|
||||
{
|
||||
private readonly NamingOptions _namingOptions = new NamingOptions();
|
||||
|
||||
[Theory]
|
||||
[InlineData("The.Show.S01", "The Show")]
|
||||
[InlineData("The.Show.S01.COMPLETE", "The Show")]
|
||||
[InlineData("S.H.O.W.S01", "S.H.O.W")]
|
||||
[InlineData("The.Show.P.I.S01", "The Show P.I")]
|
||||
[InlineData("The_Show_Season_1", "The Show")]
|
||||
[InlineData("/something/The_Show/Season 10", "The Show")]
|
||||
[InlineData("The Show", "The Show")]
|
||||
[InlineData("/some/path/The Show", "The Show")]
|
||||
[InlineData("/some/path/The Show s02e10 720p hdtv", "The Show")]
|
||||
[InlineData("/some/path/The Show s02e10 the episode 720p hdtv", "The Show")]
|
||||
public void SeriesResolverResolveTest(string path, string name)
|
||||
{
|
||||
var res = SeriesResolver.Resolve(_namingOptions, path);
|
||||
|
||||
Assert.Equal(name, res.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,8 @@ namespace Jellyfin.Naming.Tests.TV
|
||||
{
|
||||
public class SimpleEpisodeTests
|
||||
{
|
||||
private readonly EpisodeResolver _resolver = new EpisodeResolver(new NamingOptions());
|
||||
|
||||
[Theory]
|
||||
[InlineData("/server/anything_s01e02.mp4", "anything", 1, 2)]
|
||||
[InlineData("/server/anything_s1e2.mp4", "anything", 1, 2)]
|
||||
@@ -23,39 +25,25 @@ namespace Jellyfin.Naming.Tests.TV
|
||||
[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)]
|
||||
[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)]
|
||||
[InlineData("Series/4-12 - The Woman.mp4", "", 4, 12, 12)]
|
||||
// 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)
|
||||
public void TestSimple(string path, string seriesName, int? seasonNumber, int? episodeNumber, int? episodeEndNumber = null)
|
||||
{
|
||||
Test(path, seriesName, seasonNumber, episodeNumber, null);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Series/4-12 - The Woman.mp4", "", 4, 12, 12)]
|
||||
public void TestWithPossibleEpisodeEnd(string path, string seriesName, int? seasonNumber, int? episodeNumber, int? episodeEndNumber)
|
||||
{
|
||||
Test(path, seriesName, seasonNumber, episodeNumber, episodeEndNumber);
|
||||
}
|
||||
|
||||
private void Test(string path, string seriesName, int? seasonNumber, int? episodeNumber, int? episodeEndNumber)
|
||||
{
|
||||
var options = new NamingOptions();
|
||||
|
||||
var result = new EpisodeResolver(options)
|
||||
.Resolve(path, false);
|
||||
var result = _resolver.Resolve(path, false);
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(seasonNumber, result?.SeasonNumber);
|
||||
Assert.Equal(episodeNumber, result?.EpisodeNumber);
|
||||
Assert.Equal(seriesName, result?.SeriesName, true);
|
||||
Assert.Equal(path, result?.Path);
|
||||
Assert.Equal(seasonNumber, result!.SeasonNumber);
|
||||
Assert.Equal(episodeNumber, result!.EpisodeNumber);
|
||||
Assert.Equal(seriesName, result!.SeriesName, true);
|
||||
Assert.Equal(path, result!.Path);
|
||||
Assert.Equal(Path.GetExtension(path).Substring(1), result?.Container);
|
||||
Assert.Null(result?.Format3D);
|
||||
Assert.False(result?.Is3D);
|
||||
Assert.False(result?.IsStub);
|
||||
Assert.Null(result?.StubType);
|
||||
Assert.Equal(episodeEndNumber, result?.EndingEpisodeNumber);
|
||||
Assert.False(result?.IsByDate);
|
||||
Assert.Null(result!.Format3D);
|
||||
Assert.False(result!.Is3D);
|
||||
Assert.False(result!.IsStub);
|
||||
Assert.Null(result!.StubType);
|
||||
Assert.Equal(episodeEndNumber, result!.EndingEpisodeNumber);
|
||||
Assert.False(result!.IsByDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using Emby.Naming.Common;
|
||||
using Emby.Naming.Video;
|
||||
using Xunit;
|
||||
@@ -23,12 +22,17 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
[InlineData("Crouching.Tiger.Hidden.Dragon.BDrip.mkv", "Crouching.Tiger.Hidden.Dragon")]
|
||||
[InlineData("Crouching.Tiger.Hidden.Dragon.BDrip-HDC.mkv", "Crouching.Tiger.Hidden.Dragon")]
|
||||
[InlineData("Crouching.Tiger.Hidden.Dragon.4K.UltraHD.HDR.BDrip-HDC.mkv", "Crouching.Tiger.Hidden.Dragon")]
|
||||
[InlineData("[HorribleSubs] Made in Abyss - 13 [720p].mkv", "Made in Abyss")]
|
||||
[InlineData("[Tsundere] Kore wa Zombie Desu ka of the Dead [BDRip h264 1920x1080 FLAC]", "Kore wa Zombie Desu ka of the Dead")]
|
||||
[InlineData("[Erai-raws] Jujutsu Kaisen - 03 [720p][Multiple Subtitle].mkv", "Jujutsu Kaisen")]
|
||||
[InlineData("[OCN] 애타는 로맨스 720p-NEXT", "애타는 로맨스")]
|
||||
[InlineData("[tvN] 혼술남녀.E01-E16.720p-NEXT", "혼술남녀")]
|
||||
[InlineData("[tvN] 연애말고 결혼 E01~E16 END HDTV.H264.720p-WITH", "연애말고 결혼")]
|
||||
// FIXME: [InlineData("After The Sunset - [0004].mkv", "After The Sunset")]
|
||||
public void CleanStringTest_NeedsCleaning_Success(string input, string expectedName)
|
||||
{
|
||||
Assert.True(VideoResolver.TryCleanString(input, _namingOptions, out ReadOnlySpan<char> newName));
|
||||
// TODO: compare spans when XUnit supports it
|
||||
Assert.Equal(expectedName, newName.ToString());
|
||||
Assert.True(VideoResolver.TryCleanString(input, _namingOptions, out var newName));
|
||||
Assert.Equal(expectedName, newName);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -41,8 +45,8 @@ 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, _namingOptions, out ReadOnlySpan<char> newName));
|
||||
Assert.True(newName.IsEmpty);
|
||||
Assert.False(VideoResolver.TryCleanString(input, _namingOptions, out var newName));
|
||||
Assert.True(string.IsNullOrEmpty(newName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,30 +18,31 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
[Fact]
|
||||
public void TestKodiExtras()
|
||||
{
|
||||
Test("trailer.mp4", ExtraType.Trailer, _videoOptions);
|
||||
Test("300-trailer.mp4", ExtraType.Trailer, _videoOptions);
|
||||
Test("trailer.mp4", ExtraType.Trailer);
|
||||
Test("300-trailer.mp4", ExtraType.Trailer);
|
||||
|
||||
Test("theme.mp3", ExtraType.ThemeSong, _videoOptions);
|
||||
Test("theme.mp3", ExtraType.ThemeSong);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestExpandedExtras()
|
||||
{
|
||||
Test("trailer.mp4", ExtraType.Trailer, _videoOptions);
|
||||
Test("trailer.mp3", null, _videoOptions);
|
||||
Test("300-trailer.mp4", ExtraType.Trailer, _videoOptions);
|
||||
Test("trailer.mp4", ExtraType.Trailer);
|
||||
Test("trailer.mp3", null);
|
||||
Test("300-trailer.mp4", ExtraType.Trailer);
|
||||
Test("stuff trailerthings.mkv", null);
|
||||
|
||||
Test("theme.mp3", ExtraType.ThemeSong, _videoOptions);
|
||||
Test("theme.mkv", null, _videoOptions);
|
||||
Test("theme.mp3", ExtraType.ThemeSong);
|
||||
Test("theme.mkv", null);
|
||||
|
||||
Test("300-scene.mp4", ExtraType.Scene, _videoOptions);
|
||||
Test("300-scene2.mp4", ExtraType.Scene, _videoOptions);
|
||||
Test("300-clip.mp4", ExtraType.Clip, _videoOptions);
|
||||
Test("300-scene.mp4", ExtraType.Scene);
|
||||
Test("300-scene2.mp4", ExtraType.Scene);
|
||||
Test("300-clip.mp4", ExtraType.Clip);
|
||||
|
||||
Test("300-deleted.mp4", ExtraType.DeletedScene, _videoOptions);
|
||||
Test("300-deletedscene.mp4", ExtraType.DeletedScene, _videoOptions);
|
||||
Test("300-interview.mp4", ExtraType.Interview, _videoOptions);
|
||||
Test("300-behindthescenes.mp4", ExtraType.BehindTheScenes, _videoOptions);
|
||||
Test("300-deleted.mp4", ExtraType.DeletedScene);
|
||||
Test("300-deletedscene.mp4", ExtraType.DeletedScene);
|
||||
Test("300-interview.mp4", ExtraType.Interview);
|
||||
Test("300-behindthescenes.mp4", ExtraType.BehindTheScenes);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -52,12 +53,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
[InlineData(ExtraType.Sample, "samples")]
|
||||
[InlineData(ExtraType.Clip, "shorts")]
|
||||
[InlineData(ExtraType.Clip, "featurettes")]
|
||||
[InlineData(ExtraType.ThemeVideo, "backdrops")]
|
||||
[InlineData(ExtraType.Unknown, "extras")]
|
||||
public void TestDirectories(ExtraType type, string dirName)
|
||||
{
|
||||
Test(dirName + "/300.mp4", type, _videoOptions);
|
||||
Test("300/" + dirName + "/something.mkv", type, _videoOptions);
|
||||
Test("/data/something/Movies/300/" + dirName + "/whoknows.mp4", type, _videoOptions);
|
||||
Test(dirName + "/300.mp4", type);
|
||||
Test("300/" + dirName + "/something.mkv", type);
|
||||
Test("/data/something/Movies/300/" + dirName + "/whoknows.mp4", type);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -66,32 +68,23 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
[InlineData("The Big Short")]
|
||||
public void TestNonExtraDirectories(string dirName)
|
||||
{
|
||||
Test(dirName + "/300.mp4", null, _videoOptions);
|
||||
Test("300/" + dirName + "/something.mkv", null, _videoOptions);
|
||||
Test("/data/something/Movies/300/" + dirName + "/whoknows.mp4", null, _videoOptions);
|
||||
Test("/data/something/Movies/" + dirName + "/" + dirName + ".mp4", null, _videoOptions);
|
||||
Test(dirName + "/300.mp4", null);
|
||||
Test("300/" + dirName + "/something.mkv", null);
|
||||
Test("/data/something/Movies/300/" + dirName + "/whoknows.mp4", null);
|
||||
Test("/data/something/Movies/" + dirName + "/" + dirName + ".mp4", null);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestSample()
|
||||
{
|
||||
Test("300-sample.mp4", ExtraType.Sample, _videoOptions);
|
||||
Test("300-sample.mp4", ExtraType.Sample);
|
||||
}
|
||||
|
||||
private void Test(string input, ExtraType? expectedType, NamingOptions videoOptions)
|
||||
private void Test(string input, ExtraType? expectedType)
|
||||
{
|
||||
var parser = GetExtraTypeParser(videoOptions);
|
||||
var extraType = ExtraRuleResolver.GetExtraInfo(input, _videoOptions).ExtraType;
|
||||
|
||||
var extraType = parser.GetExtraInfo(input).ExtraType;
|
||||
|
||||
if (expectedType == null)
|
||||
{
|
||||
Assert.Null(extraType);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Equal(expectedType, extraType);
|
||||
}
|
||||
Assert.Equal(expectedType, extraType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -99,14 +92,9 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
{
|
||||
var rule = new ExtraRule(ExtraType.Unknown, ExtraRuleType.Regex, @"([eE]x(tra)?\.\w+)", MediaType.Video);
|
||||
var options = new NamingOptions { VideoExtraRules = new[] { rule } };
|
||||
var res = GetExtraTypeParser(options).GetExtraInfo("extra.mp4");
|
||||
var res = ExtraRuleResolver.GetExtraInfo("extra.mp4", options);
|
||||
|
||||
Assert.Equal(rule, res.Rule);
|
||||
}
|
||||
|
||||
private ExtraResolver GetExtraTypeParser(NamingOptions videoOptions)
|
||||
{
|
||||
return new ExtraResolver(videoOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,15 +23,11 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Single(result[0].Extras);
|
||||
Assert.Single(result.Where(v => v.ExtraType == null));
|
||||
Assert.Single(result.Where(v => v.ExtraType != null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -46,15 +42,11 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Single(result[0].Extras);
|
||||
Assert.Single(result.Where(v => v.ExtraType == null));
|
||||
Assert.Single(result.Where(v => v.ExtraType != null));
|
||||
Assert.Equal(2, result[0].AlternateVersions.Count);
|
||||
}
|
||||
|
||||
@@ -68,11 +60,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
@@ -94,15 +82,10 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(7, result.Count);
|
||||
Assert.Empty(result[0].Extras);
|
||||
Assert.Empty(result[0].AlternateVersions);
|
||||
}
|
||||
|
||||
@@ -122,15 +105,10 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Empty(result[0].Extras);
|
||||
Assert.Equal(7, result[0].AlternateVersions.Count);
|
||||
}
|
||||
|
||||
@@ -151,15 +129,10 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(9, result.Count);
|
||||
Assert.Empty(result[0].Extras);
|
||||
Assert.Empty(result[0].AlternateVersions);
|
||||
}
|
||||
|
||||
@@ -176,15 +149,10 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(5, result.Count);
|
||||
Assert.Empty(result[0].Extras);
|
||||
Assert.Empty(result[0].AlternateVersions);
|
||||
}
|
||||
|
||||
@@ -203,15 +171,10 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(5, result.Count);
|
||||
Assert.Empty(result[0].Extras);
|
||||
Assert.Empty(result[0].AlternateVersions);
|
||||
}
|
||||
|
||||
@@ -231,15 +194,10 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Empty(result[0].Extras);
|
||||
Assert.Equal(7, result[0].AlternateVersions.Count);
|
||||
Assert.False(result[0].AlternateVersions[2].Is3D);
|
||||
Assert.True(result[0].AlternateVersions[3].Is3D);
|
||||
@@ -262,15 +220,10 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Empty(result[0].Extras);
|
||||
Assert.Equal(7, result[0].AlternateVersions.Count);
|
||||
Assert.False(result[0].AlternateVersions[3].Is3D);
|
||||
Assert.True(result[0].AlternateVersions[4].Is3D);
|
||||
@@ -287,11 +240,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
@@ -312,15 +261,10 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(7, result.Count);
|
||||
Assert.Empty(result[0].Extras);
|
||||
Assert.Empty(result[0].AlternateVersions);
|
||||
}
|
||||
|
||||
@@ -339,15 +283,10 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(5, result.Count);
|
||||
Assert.Empty(result[0].Extras);
|
||||
Assert.Empty(result[0].AlternateVersions);
|
||||
}
|
||||
|
||||
@@ -361,15 +300,10 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Empty(result[0].Extras);
|
||||
Assert.Single(result[0].AlternateVersions);
|
||||
}
|
||||
|
||||
@@ -383,15 +317,10 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Empty(result[0].Extras);
|
||||
Assert.Single(result[0].AlternateVersions);
|
||||
}
|
||||
|
||||
@@ -405,15 +334,10 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Empty(result[0].Extras);
|
||||
Assert.Single(result[0].AlternateVersions);
|
||||
}
|
||||
|
||||
@@ -427,11 +351,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
@@ -440,7 +360,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
[Fact]
|
||||
public void TestEmptyList()
|
||||
{
|
||||
var result = VideoListResolver.Resolve(new List<FileSystemMetadata>(), _namingOptions).ToList();
|
||||
var result = VideoListResolver.Resolve(new List<VideoFileInfo>(), _namingOptions).ToList();
|
||||
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
@@ -22,9 +22,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"Bad Boys (2006)-trailer.mkv"
|
||||
};
|
||||
|
||||
var resolver = GetResolver();
|
||||
|
||||
var result = resolver.ResolveFiles(files).ToList();
|
||||
var result = StackResolver.ResolveFiles(files, _namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
TestStackInfo(result[0], "Bad Boys (2006)", 4);
|
||||
@@ -39,9 +37,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"Bad Boys (2007).mkv"
|
||||
};
|
||||
|
||||
var resolver = GetResolver();
|
||||
|
||||
var result = resolver.ResolveFiles(files).ToList();
|
||||
var result = StackResolver.ResolveFiles(files, _namingOptions).ToList();
|
||||
|
||||
Assert.Empty(result);
|
||||
}
|
||||
@@ -55,9 +51,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"Bad Boys 2007.mkv"
|
||||
};
|
||||
|
||||
var resolver = GetResolver();
|
||||
|
||||
var result = resolver.ResolveFiles(files).ToList();
|
||||
var result = StackResolver.ResolveFiles(files, _namingOptions).ToList();
|
||||
|
||||
Assert.Empty(result);
|
||||
}
|
||||
@@ -71,9 +65,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"300 (2007).mkv"
|
||||
};
|
||||
|
||||
var resolver = GetResolver();
|
||||
|
||||
var result = resolver.ResolveFiles(files).ToList();
|
||||
var result = StackResolver.ResolveFiles(files, _namingOptions).ToList();
|
||||
|
||||
Assert.Empty(result);
|
||||
}
|
||||
@@ -87,9 +79,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"300 2007.mkv"
|
||||
};
|
||||
|
||||
var resolver = GetResolver();
|
||||
|
||||
var result = resolver.ResolveFiles(files).ToList();
|
||||
var result = StackResolver.ResolveFiles(files, _namingOptions).ToList();
|
||||
|
||||
Assert.Empty(result);
|
||||
}
|
||||
@@ -103,9 +93,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"Star Trek 2- The wrath of khan.mkv"
|
||||
};
|
||||
|
||||
var resolver = GetResolver();
|
||||
|
||||
var result = resolver.ResolveFiles(files).ToList();
|
||||
var result = StackResolver.ResolveFiles(files, _namingOptions).ToList();
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
@@ -119,9 +107,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"Red Riding in the Year of Our Lord 1974 (2009).mkv"
|
||||
};
|
||||
|
||||
var resolver = GetResolver();
|
||||
|
||||
var result = resolver.ResolveFiles(files).ToList();
|
||||
var result = StackResolver.ResolveFiles(files, _namingOptions).ToList();
|
||||
|
||||
Assert.Empty(result);
|
||||
}
|
||||
@@ -135,16 +121,14 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"d:/movies/300 2006 part2.mkv"
|
||||
};
|
||||
|
||||
var resolver = GetResolver();
|
||||
|
||||
var result = resolver.ResolveFiles(files).ToList();
|
||||
var result = StackResolver.ResolveFiles(files, _namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
TestStackInfo(result[0], "300 2006", 2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestDirtyNames()
|
||||
public void ResolveFiles_GivenPartInMiddleOfName_ReturnsNoStack()
|
||||
{
|
||||
var files = new[]
|
||||
{
|
||||
@@ -155,16 +139,13 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"Bad Boys (2006)-trailer.mkv"
|
||||
};
|
||||
|
||||
var resolver = GetResolver();
|
||||
var result = StackResolver.ResolveFiles(files, _namingOptions).ToList();
|
||||
|
||||
var result = resolver.ResolveFiles(files).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
TestStackInfo(result[0], "Bad Boys (2006).stv.unrated.multi.1080p.bluray.x264-rough", 4);
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestNumberedFiles()
|
||||
public void ResolveFiles_FileNamesWithMissingPartType_ReturnsNoStack()
|
||||
{
|
||||
var files = new[]
|
||||
{
|
||||
@@ -175,9 +156,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"Bad Boys (2006)-trailer.mkv"
|
||||
};
|
||||
|
||||
var resolver = GetResolver();
|
||||
|
||||
var result = resolver.ResolveFiles(files).ToList();
|
||||
var result = StackResolver.ResolveFiles(files, _namingOptions).ToList();
|
||||
|
||||
Assert.Empty(result);
|
||||
}
|
||||
@@ -194,9 +173,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"300 (2006)-trailer.mkv"
|
||||
};
|
||||
|
||||
var resolver = GetResolver();
|
||||
|
||||
var result = resolver.ResolveFiles(files).ToList();
|
||||
var result = StackResolver.ResolveFiles(files, _namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
TestStackInfo(result[0], "300 (2006)", 4);
|
||||
@@ -214,9 +191,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"Bad Boys (2006)-trailer.mkv"
|
||||
};
|
||||
|
||||
var resolver = GetResolver();
|
||||
|
||||
var result = resolver.ResolveFiles(files).ToList();
|
||||
var result = StackResolver.ResolveFiles(files, _namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
TestStackInfo(result[0], "Bad Boys (2006)", 3);
|
||||
@@ -238,9 +213,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"300 (2006)-trailer.mkv"
|
||||
};
|
||||
|
||||
var resolver = GetResolver();
|
||||
|
||||
var result = resolver.ResolveFiles(files).ToList();
|
||||
var result = StackResolver.ResolveFiles(files, _namingOptions).ToList();
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
TestStackInfo(result[1], "Bad Boys (2006)", 4);
|
||||
@@ -256,9 +229,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"blah blah - cd 2"
|
||||
};
|
||||
|
||||
var resolver = GetResolver();
|
||||
|
||||
var result = resolver.ResolveDirectories(files).ToList();
|
||||
var result = StackResolver.ResolveDirectories(files, _namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
TestStackInfo(result[0], "blah blah", 2);
|
||||
@@ -275,9 +246,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"300-trailer.mkv"
|
||||
};
|
||||
|
||||
var resolver = GetResolver();
|
||||
|
||||
var result = resolver.ResolveFiles(files).ToList();
|
||||
var result = StackResolver.ResolveFiles(files, _namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
|
||||
@@ -297,9 +266,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"Avengers part3.mkv"
|
||||
};
|
||||
|
||||
var resolver = GetResolver();
|
||||
|
||||
var result = resolver.ResolveFiles(files).ToList();
|
||||
var result = StackResolver.ResolveFiles(files, _namingOptions).ToList();
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
|
||||
@@ -328,9 +295,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"300-trailer.mkv"
|
||||
};
|
||||
|
||||
var resolver = GetResolver();
|
||||
|
||||
var result = resolver.ResolveFiles(files).ToList();
|
||||
var result = StackResolver.ResolveFiles(files, _namingOptions).ToList();
|
||||
|
||||
Assert.Equal(3, result.Count);
|
||||
|
||||
@@ -354,9 +319,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"300 (2006)-trailer.mkv"
|
||||
};
|
||||
|
||||
var resolver = GetResolver();
|
||||
|
||||
var result = resolver.ResolveFiles(files).ToList();
|
||||
var result = StackResolver.ResolveFiles(files, _namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
|
||||
@@ -375,9 +338,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
new FileSystemMetadata { FullName = "300 (2006) part1", IsDirectory = true }
|
||||
};
|
||||
|
||||
var resolver = GetResolver();
|
||||
|
||||
var result = resolver.Resolve(files).ToList();
|
||||
var result = StackResolver.Resolve(files, _namingOptions).ToList();
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
TestStackInfo(result[0], "300 (2006)", 3);
|
||||
@@ -397,9 +358,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"Harry Potter and the Deathly Hallows 4.mkv"
|
||||
};
|
||||
|
||||
var resolver = GetResolver();
|
||||
|
||||
var result = resolver.ResolveFiles(files).ToList();
|
||||
var result = StackResolver.ResolveFiles(files, _namingOptions).ToList();
|
||||
|
||||
Assert.Empty(result);
|
||||
}
|
||||
@@ -414,9 +373,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"Neverland (2011)[720p][PG][Voted 6.5][Family-Fantasy]part2.mkv"
|
||||
};
|
||||
|
||||
var resolver = GetResolver();
|
||||
|
||||
var result = resolver.ResolveFiles(files).ToList();
|
||||
var result = StackResolver.ResolveFiles(files, _namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Equal(2, result[0].Files.Count);
|
||||
@@ -432,9 +389,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"M:/Movies (DVD)/Movies (Musical)/The Sound of Music/The Sound of Music (1965) (Disc 02)"
|
||||
};
|
||||
|
||||
var resolver = GetResolver();
|
||||
|
||||
var result = resolver.ResolveDirectories(files).ToList();
|
||||
var result = StackResolver.ResolveDirectories(files, _namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Equal(2, result[0].Files.Count);
|
||||
@@ -445,10 +400,5 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
Assert.Equal(fileCount, stack.Files.Count);
|
||||
Assert.Equal(name, stack.Name);
|
||||
}
|
||||
|
||||
private StackResolver GetResolver()
|
||||
{
|
||||
return new StackResolver(_namingOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Linq;
|
||||
using Emby.Naming.Common;
|
||||
using Emby.Naming.Video;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
using Xunit;
|
||||
|
||||
@@ -41,23 +42,28 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(5, result.Count);
|
||||
Assert.Equal(11, result.Count);
|
||||
var batman = result.FirstOrDefault(x => string.Equals(x.Name, "Batman", StringComparison.Ordinal));
|
||||
Assert.NotNull(batman);
|
||||
Assert.Equal(3, batman!.Files.Count);
|
||||
Assert.Equal(3, batman!.Extras.Count);
|
||||
|
||||
var harry = result.FirstOrDefault(x => string.Equals(x.Name, "Harry Potter and the Deathly Hallows", StringComparison.Ordinal));
|
||||
Assert.NotNull(harry);
|
||||
Assert.Equal(4, harry!.Files.Count);
|
||||
Assert.Equal(2, harry!.Extras.Count);
|
||||
|
||||
Assert.False(result[2].ExtraType.HasValue);
|
||||
|
||||
Assert.Equal(ExtraType.Trailer, result[3].ExtraType);
|
||||
Assert.Equal(ExtraType.Trailer, result[4].ExtraType);
|
||||
Assert.Equal(ExtraType.DeletedScene, result[5].ExtraType);
|
||||
Assert.Equal(ExtraType.Sample, result[6].ExtraType);
|
||||
Assert.Equal(ExtraType.Trailer, result[7].ExtraType);
|
||||
Assert.Equal(ExtraType.Trailer, result[8].ExtraType);
|
||||
Assert.Equal(ExtraType.Trailer, result[9].ExtraType);
|
||||
Assert.Equal(ExtraType.Trailer, result[10].ExtraType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -70,11 +76,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
@@ -90,14 +92,12 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.False(result[0].ExtraType.HasValue);
|
||||
Assert.Equal(ExtraType.Trailer, result[1].ExtraType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -110,14 +110,12 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.False(result[0].ExtraType.HasValue);
|
||||
Assert.Equal(ExtraType.Trailer, result[1].ExtraType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -131,34 +129,51 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.False(result[0].ExtraType.HasValue);
|
||||
Assert.Equal(ExtraType.Trailer, result[1].ExtraType);
|
||||
Assert.Equal(ExtraType.Trailer, result[2].ExtraType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestDifferentNames()
|
||||
public void Resolve_SameNameAndYear_ReturnsSingleItem()
|
||||
{
|
||||
var files = new[]
|
||||
{
|
||||
"Looper (2012)-trailer.mkv",
|
||||
"Looper 2012-trailer.mkv",
|
||||
"Looper.2012.bluray.720p.x264.mkv"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.False(result[0].ExtraType.HasValue);
|
||||
Assert.Equal(ExtraType.Trailer, result[1].ExtraType);
|
||||
Assert.Equal(ExtraType.Trailer, result[2].ExtraType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Resolve_TrailerMatchesFolderName_ReturnsSingleItem()
|
||||
{
|
||||
var files = new[]
|
||||
{
|
||||
"/movies/Looper (2012)/Looper (2012)-trailer.mkv",
|
||||
"/movies/Looper (2012)/Looper.bluray.720p.x264.mkv"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.False(result[0].ExtraType.HasValue);
|
||||
Assert.Equal(ExtraType.Trailer, result[1].ExtraType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -175,11 +190,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(5, result.Count);
|
||||
@@ -195,11 +206,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = true,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, true, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
@@ -216,11 +223,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = true,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, true, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
@@ -233,39 +236,18 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
{
|
||||
@"No (2012) part1.mp4",
|
||||
@"No (2012) part2.mp4",
|
||||
@"No (2012) part1-trailer.mp4"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestStackedWithTrailer2()
|
||||
{
|
||||
var files = new[]
|
||||
{
|
||||
@"No (2012) part1.mp4",
|
||||
@"No (2012) part2.mp4",
|
||||
@"No (2012) part1-trailer.mp4",
|
||||
@"No (2012)-trailer.mp4"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.False(result[0].ExtraType.HasValue);
|
||||
Assert.Equal(ExtraType.Trailer, result[1].ExtraType);
|
||||
Assert.Equal(ExtraType.Trailer, result[2].ExtraType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -276,18 +258,18 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
@"/Movies/Top Gun (1984)/movie.mp4",
|
||||
@"/Movies/Top Gun (1984)/Top Gun (1984)-trailer.mp4",
|
||||
@"/Movies/Top Gun (1984)/Top Gun (1984)-trailer2.mp4",
|
||||
@"trailer.mp4"
|
||||
@"/Movies/trailer.mp4"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Equal(4, result.Count);
|
||||
Assert.False(result[0].ExtraType.HasValue);
|
||||
Assert.Equal(ExtraType.Trailer, result[1].ExtraType);
|
||||
Assert.Equal(ExtraType.Trailer, result[2].ExtraType);
|
||||
Assert.Equal(ExtraType.Trailer, result[3].ExtraType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -302,11 +284,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
@@ -321,11 +299,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
@@ -340,11 +314,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
@@ -360,11 +330,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
@@ -380,11 +346,7 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
@@ -396,40 +358,34 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
var files = new[]
|
||||
{
|
||||
@"/Server/Despicable Me/Despicable Me (2010).mkv",
|
||||
@"/Server/Despicable Me/movie-trailer.mkv"
|
||||
@"/Server/Despicable Me/trailer.mkv"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.False(result[0].ExtraType.HasValue);
|
||||
Assert.Equal(ExtraType.Trailer, result[1].ExtraType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestTrailerFalsePositives()
|
||||
public void Resolve_TrailerInTrailersFolder_ReturnsCorrectExtraType()
|
||||
{
|
||||
var files = new[]
|
||||
{
|
||||
@"/Server/Despicable Me/Skyscraper (2018) - Big Game Spot.mkv",
|
||||
@"/Server/Despicable Me/Skyscraper (2018) - Trailer.mkv",
|
||||
@"/Server/Despicable Me/Baywatch (2017) - Big Game Spot.mkv",
|
||||
@"/Server/Despicable Me/Baywatch (2017) - Trailer.mkv"
|
||||
@"/Server/Despicable Me/Despicable Me (2010).mkv",
|
||||
@"/Server/Despicable Me/trailers/some title.mkv"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Equal(4, result.Count);
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.False(result[0].ExtraType.HasValue);
|
||||
Assert.Equal(ExtraType.Trailer, result[1].ExtraType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -442,20 +398,18 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => new FileSystemMetadata
|
||||
{
|
||||
IsDirectory = false,
|
||||
FullName = i
|
||||
}).ToList(),
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.False(result[0].ExtraType.HasValue);
|
||||
Assert.Equal(ExtraType.Trailer, result[1].ExtraType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestDirectoryStack()
|
||||
{
|
||||
var stack = new FileStack();
|
||||
var stack = new FileStack(string.Empty, false, Array.Empty<string>());
|
||||
Assert.False(stack.ContainsFile("XX", true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Emby.Naming.Common;
|
||||
using Emby.Naming.Video;
|
||||
@@ -11,148 +10,134 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
{
|
||||
private static NamingOptions _namingOptions = new NamingOptions();
|
||||
|
||||
public static IEnumerable<object[]> ResolveFile_ValidFileNameTestData()
|
||||
public static TheoryData<VideoFileInfo> ResolveFile_ValidFileNameTestData()
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
var data = new TheoryData<VideoFileInfo>();
|
||||
data.Add(
|
||||
new VideoFileInfo(
|
||||
path: @"/server/Movies/7 Psychos.mkv/7 Psychos.mkv",
|
||||
container: "mkv",
|
||||
name: "7 Psychos")
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
name: "7 Psychos"));
|
||||
|
||||
data.Add(
|
||||
new VideoFileInfo(
|
||||
path: @"/server/Movies/3 days to kill (2005)/3 days to kill (2005).mkv",
|
||||
container: "mkv",
|
||||
name: "3 days to kill",
|
||||
year: 2005)
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
year: 2005));
|
||||
|
||||
data.Add(
|
||||
new VideoFileInfo(
|
||||
path: @"/server/Movies/American Psycho/American.Psycho.mkv",
|
||||
container: "mkv",
|
||||
name: "American.Psycho")
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
name: "American.Psycho"));
|
||||
|
||||
data.Add(
|
||||
new VideoFileInfo(
|
||||
path: @"/server/Movies/brave (2007)/brave (2006).3d.sbs.mkv",
|
||||
container: "mkv",
|
||||
name: "brave",
|
||||
year: 2006,
|
||||
is3D: true,
|
||||
format3D: "sbs")
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
format3D: "sbs"));
|
||||
|
||||
data.Add(
|
||||
new VideoFileInfo(
|
||||
path: @"/server/Movies/300 (2007)/300 (2006).3d1.sbas.mkv",
|
||||
container: "mkv",
|
||||
name: "300",
|
||||
year: 2006)
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
year: 2006));
|
||||
|
||||
data.Add(
|
||||
new VideoFileInfo(
|
||||
path: @"/server/Movies/300 (2007)/300 (2006).3d.sbs.mkv",
|
||||
container: "mkv",
|
||||
name: "300",
|
||||
year: 2006,
|
||||
is3D: true,
|
||||
format3D: "sbs")
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
format3D: "sbs"));
|
||||
|
||||
data.Add(
|
||||
new VideoFileInfo(
|
||||
path: @"/server/Movies/brave (2007)/brave (2006)-trailer.bluray.disc",
|
||||
container: "disc",
|
||||
name: "brave",
|
||||
year: 2006,
|
||||
isStub: true,
|
||||
stubType: "bluray")
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
stubType: "bluray"));
|
||||
|
||||
data.Add(
|
||||
new VideoFileInfo(
|
||||
path: @"/server/Movies/300 (2007)/300 (2006)-trailer.bluray.disc",
|
||||
container: "disc",
|
||||
name: "300",
|
||||
year: 2006,
|
||||
isStub: true,
|
||||
stubType: "bluray")
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
stubType: "bluray"));
|
||||
|
||||
data.Add(
|
||||
new VideoFileInfo(
|
||||
path: @"/server/Movies/Brave (2007)/Brave (2006).bluray.disc",
|
||||
container: "disc",
|
||||
name: "Brave",
|
||||
year: 2006,
|
||||
isStub: true,
|
||||
stubType: "bluray")
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
stubType: "bluray"));
|
||||
|
||||
data.Add(
|
||||
new VideoFileInfo(
|
||||
path: @"/server/Movies/300 (2007)/300 (2006).bluray.disc",
|
||||
container: "disc",
|
||||
name: "300",
|
||||
year: 2006,
|
||||
isStub: true,
|
||||
stubType: "bluray")
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
stubType: "bluray"));
|
||||
|
||||
data.Add(
|
||||
new VideoFileInfo(
|
||||
path: @"/server/Movies/300 (2007)/300 (2006)-trailer.mkv",
|
||||
container: "mkv",
|
||||
name: "300",
|
||||
year: 2006,
|
||||
extraType: ExtraType.Trailer)
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
extraType: ExtraType.Trailer));
|
||||
|
||||
data.Add(
|
||||
new VideoFileInfo(
|
||||
path: @"/server/Movies/Brave (2007)/Brave (2006)-trailer.mkv",
|
||||
container: "mkv",
|
||||
name: "Brave",
|
||||
year: 2006,
|
||||
extraType: ExtraType.Trailer)
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
extraType: ExtraType.Trailer));
|
||||
|
||||
data.Add(
|
||||
new VideoFileInfo(
|
||||
path: @"/server/Movies/300 (2007)/300 (2006).mkv",
|
||||
container: "mkv",
|
||||
name: "300",
|
||||
year: 2006)
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
year: 2006));
|
||||
|
||||
data.Add(
|
||||
new VideoFileInfo(
|
||||
path: @"/server/Movies/Bad Boys (1995)/Bad Boys (1995).mkv",
|
||||
container: "mkv",
|
||||
name: "Bad Boys",
|
||||
year: 1995)
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
year: 1995));
|
||||
|
||||
data.Add(
|
||||
new VideoFileInfo(
|
||||
path: @"/server/Movies/Brave (2007)/Brave (2006).mkv",
|
||||
container: "mkv",
|
||||
name: "Brave",
|
||||
year: 2006)
|
||||
};
|
||||
yield return new object[]
|
||||
{
|
||||
year: 2006));
|
||||
|
||||
data.Add(
|
||||
new VideoFileInfo(
|
||||
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)
|
||||
};
|
||||
year: 1988));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0" />
|
||||
@@ -23,7 +23,7 @@
|
||||
<!-- 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="StyleCop.Analyzers" Version="1.2.0-beta.376" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Jellyfin.Networking.Tests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks that thge given IP address is not in the network provided.
|
||||
/// Checks that the given IP address is not in the network provided.
|
||||
/// </summary>
|
||||
/// <param name="network">Network address(es).</param>
|
||||
/// <param name="value">The IP to check.</param>
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Jellyfin.Networking.Tests
|
||||
CallBase = true
|
||||
};
|
||||
configManager.Setup(x => x.GetConfiguration(It.IsAny<string>())).Returns(conf);
|
||||
return (IConfigurationManager)configManager.Object;
|
||||
return configManager.Object;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -35,9 +35,9 @@ namespace Jellyfin.Networking.Tests
|
||||
// eth16 only
|
||||
[InlineData("192.168.1.208/24,-16,eth16|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[192.168.1.208/24]")]
|
||||
// All interfaces excluded. (including loopbacks)
|
||||
[InlineData("192.168.1.208/24,-16,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[127.0.0.1/8,::1/128]")]
|
||||
[InlineData("192.168.1.208/24,-16,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[]")]
|
||||
// vEthernet1 and vEthernet212 should be excluded.
|
||||
[InlineData("192.168.1.200/24,-20,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24;200.200.200.200/24", "[200.200.200.200/24,127.0.0.1/8,::1/128]")]
|
||||
[InlineData("192.168.1.200/24,-20,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24;200.200.200.200/24", "[200.200.200.200/24]")]
|
||||
// Overlapping interface,
|
||||
[InlineData("192.168.1.110/24,-20,br0|192.168.1.10/24,-16,br0|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[192.168.1.110/24,192.168.1.10/24]")]
|
||||
public void IgnoreVirtualInterfaces(string interfaces, string lan, string value)
|
||||
@@ -476,5 +476,51 @@ namespace Jellyfin.Networking.Tests
|
||||
|
||||
Assert.NotEqual(nm.HasRemoteAccess(IPAddress.Parse(remoteIp)), denied);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("192.168.1.209/24,-16,eth16", "192.168.1.0/24", "", "192.168.1.209")] // Only 1 address so use it.
|
||||
[InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "", "192.168.1.208")] // LAN address is specified by default.
|
||||
[InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "10.0.0.1", "10.0.0.1")] // return bind address
|
||||
|
||||
public void GetBindInterface_NoSourceGiven_Success(string interfaces, string lan, string bind, string result)
|
||||
{
|
||||
var conf = new NetworkConfiguration
|
||||
{
|
||||
EnableIPV4 = true,
|
||||
LocalNetworkSubnets = lan.Split(','),
|
||||
LocalNetworkAddresses = bind.Split(',')
|
||||
};
|
||||
|
||||
NetworkManager.MockNetworkSettings = interfaces;
|
||||
using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger<NetworkManager>());
|
||||
|
||||
var interfaceToUse = nm.GetBindInterface(string.Empty, out _);
|
||||
|
||||
Assert.Equal(result, interfaceToUse);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("192.168.1.209/24,-16,eth16", "192.168.1.0/24", "", "192.168.1.210", "192.168.1.209")] // Source on LAN
|
||||
[InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "", "192.168.1.209", "192.168.1.208")] // Source on LAN
|
||||
[InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "", "8.8.8.8", "10.0.0.1")] // Source external.
|
||||
[InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "10.0.0.1", "192.168.1.209", "10.0.0.1")] // LAN not bound, so return external.
|
||||
[InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "192.168.1.208,10.0.0.1", "8.8.8.8", "10.0.0.1")] // return external bind address
|
||||
[InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "192.168.1.208,10.0.0.1", "192.168.1.210", "192.168.1.208")] // return LAN bind address
|
||||
public void GetBindInterface_ValidSourceGiven_Success(string interfaces, string lan, string bind, string source, string result)
|
||||
{
|
||||
var conf = new NetworkConfiguration
|
||||
{
|
||||
EnableIPV4 = true,
|
||||
LocalNetworkSubnets = lan.Split(','),
|
||||
LocalNetworkAddresses = bind.Split(',')
|
||||
};
|
||||
|
||||
NetworkManager.MockNetworkSettings = interfaces;
|
||||
using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger<NetworkManager>());
|
||||
|
||||
var interfaceToUse = nm.GetBindInterface(source, out _);
|
||||
|
||||
Assert.Equal(result, interfaceToUse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,13 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<None Include="Test Data\**\*.*">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
@@ -23,7 +29,7 @@
|
||||
<!-- 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="StyleCop.Analyzers" Version="1.2.0-beta.376" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
597
tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs
Normal file
597
tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs
Normal file
@@ -0,0 +1,597 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.Drawing;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using MediaBrowser.Providers.Manager;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Providers.Tests.Manager
|
||||
{
|
||||
public class ItemImageProviderTests
|
||||
{
|
||||
private const string TestDataImagePath = "Test Data/Images/blank{0}.jpg";
|
||||
|
||||
[Fact]
|
||||
public void ValidateImages_PhotoEmptyProviders_NoChange()
|
||||
{
|
||||
var itemImageProvider = GetItemImageProvider(null, null);
|
||||
var changed = itemImageProvider.ValidateImages(new Photo(), Enumerable.Empty<ILocalImageProvider>(), null);
|
||||
|
||||
Assert.False(changed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidateImages_EmptyItemEmptyProviders_NoChange()
|
||||
{
|
||||
var itemImageProvider = GetItemImageProvider(null, null);
|
||||
var changed = itemImageProvider.ValidateImages(new Video(), Enumerable.Empty<ILocalImageProvider>(), null);
|
||||
|
||||
Assert.False(changed);
|
||||
}
|
||||
|
||||
private static TheoryData<ImageType, int> GetImageTypesWithCount()
|
||||
{
|
||||
var theoryTypes = new TheoryData<ImageType, int>
|
||||
{
|
||||
// minimal test cases that hit different handling
|
||||
{ ImageType.Primary, 1 },
|
||||
{ ImageType.Backdrop, 1 },
|
||||
{ ImageType.Backdrop, 2 }
|
||||
};
|
||||
|
||||
return theoryTypes;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(GetImageTypesWithCount))]
|
||||
public void ValidateImages_EmptyItemAndPopulatedProviders_AddsImages(ImageType imageType, int imageCount)
|
||||
{
|
||||
// Has to exist for querying DateModified time on file, results stored but not checked so not populating
|
||||
BaseItem.FileSystem = Mock.Of<IFileSystem>();
|
||||
|
||||
var item = new Video();
|
||||
var imageProvider = GetImageProvider(imageType, imageCount, true);
|
||||
|
||||
var itemImageProvider = GetItemImageProvider(null, null);
|
||||
var changed = itemImageProvider.ValidateImages(item, new[] { imageProvider }, null);
|
||||
|
||||
Assert.True(changed);
|
||||
Assert.Equal(imageCount, item.GetImages(imageType).Count());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(GetImageTypesWithCount))]
|
||||
public void ValidateImages_PopulatedItemWithGoodPathsAndEmptyProviders_NoChange(ImageType imageType, int imageCount)
|
||||
{
|
||||
var item = GetItemWithImages(imageType, imageCount, true);
|
||||
|
||||
var itemImageProvider = GetItemImageProvider(null, null);
|
||||
var changed = itemImageProvider.ValidateImages(item, Enumerable.Empty<ILocalImageProvider>(), null);
|
||||
|
||||
Assert.False(changed);
|
||||
Assert.Equal(imageCount, item.GetImages(imageType).Count());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(GetImageTypesWithCount))]
|
||||
public void ValidateImages_PopulatedItemWithBadPathsAndEmptyProviders_RemovesImage(ImageType imageType, int imageCount)
|
||||
{
|
||||
var item = GetItemWithImages(imageType, imageCount, false);
|
||||
|
||||
var itemImageProvider = GetItemImageProvider(null, null);
|
||||
var changed = itemImageProvider.ValidateImages(item, Enumerable.Empty<ILocalImageProvider>(), null);
|
||||
|
||||
Assert.True(changed);
|
||||
Assert.Empty(item.GetImages(imageType));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MergeImages_EmptyItemNewImagesEmpty_NoChange()
|
||||
{
|
||||
var itemImageProvider = GetItemImageProvider(null, null);
|
||||
var changed = itemImageProvider.MergeImages(new Video(), Array.Empty<LocalImageInfo>());
|
||||
|
||||
Assert.False(changed);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(GetImageTypesWithCount))]
|
||||
public void MergeImages_PopulatedItemWithGoodPathsAndPopulatedNewImages_AddsUpdatesImages(ImageType imageType, int imageCount)
|
||||
{
|
||||
// valid and not valid paths - should replace the valid paths with the invalid ones
|
||||
var item = GetItemWithImages(imageType, imageCount, true);
|
||||
var images = GetImages(imageType, imageCount, false);
|
||||
|
||||
var itemImageProvider = GetItemImageProvider(null, null);
|
||||
var changed = itemImageProvider.MergeImages(item, images);
|
||||
|
||||
Assert.True(changed);
|
||||
// adds for types that allow multiple, replaces singular type images
|
||||
if (item.AllowsMultipleImages(imageType))
|
||||
{
|
||||
Assert.Equal(imageCount * 2, item.GetImages(imageType).Count());
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Single(item.GetImages(imageType));
|
||||
Assert.Same(images[0].FileInfo.FullName, item.GetImages(imageType).First().Path);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(GetImageTypesWithCount))]
|
||||
public void MergeImages_PopulatedItemWithGoodPathsAndSameNewImages_NoChange(ImageType imageType, int imageCount)
|
||||
{
|
||||
var oldTime = new DateTime(1970, 1, 1);
|
||||
|
||||
// match update time with time added to item images (unix epoch)
|
||||
var fileSystem = new Mock<IFileSystem>();
|
||||
fileSystem.Setup(fs => fs.GetLastWriteTimeUtc(It.IsAny<FileSystemMetadata>()))
|
||||
.Returns(oldTime);
|
||||
BaseItem.FileSystem = fileSystem.Object;
|
||||
|
||||
// all valid paths - matching for strictly updating
|
||||
var item = GetItemWithImages(imageType, imageCount, true);
|
||||
// set size to non-zero to allow for updates to occur
|
||||
foreach (var image in item.GetImages(imageType))
|
||||
{
|
||||
image.DateModified = oldTime;
|
||||
image.Height = 1;
|
||||
image.Width = 1;
|
||||
}
|
||||
|
||||
var images = GetImages(imageType, imageCount, true);
|
||||
|
||||
var itemImageProvider = GetItemImageProvider(null, fileSystem);
|
||||
var changed = itemImageProvider.MergeImages(item, images);
|
||||
|
||||
Assert.False(changed);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(GetImageTypesWithCount))]
|
||||
public void MergeImages_PopulatedItemWithGoodPathsAndSameNewImagesWithNewTimestamps_ResetsImageSizes(ImageType imageType, int imageCount)
|
||||
{
|
||||
var oldTime = new DateTime(1970, 1, 1);
|
||||
var updatedTime = new DateTime(2021, 1, 1);
|
||||
|
||||
var fileSystem = new Mock<IFileSystem>();
|
||||
fileSystem.Setup(fs => fs.GetLastWriteTimeUtc(It.IsAny<FileSystemMetadata>()))
|
||||
.Returns(updatedTime);
|
||||
BaseItem.FileSystem = fileSystem.Object;
|
||||
|
||||
// all valid paths - matching for strictly updating
|
||||
var item = GetItemWithImages(imageType, imageCount, true);
|
||||
// set size to non-zero to allow for image size reset to occur
|
||||
foreach (var image in item.GetImages(imageType))
|
||||
{
|
||||
image.DateModified = oldTime;
|
||||
image.Height = 1;
|
||||
image.Width = 1;
|
||||
}
|
||||
|
||||
var images = GetImages(imageType, imageCount, true);
|
||||
|
||||
var itemImageProvider = GetItemImageProvider(null, fileSystem);
|
||||
var changed = itemImageProvider.MergeImages(item, images);
|
||||
|
||||
Assert.True(changed);
|
||||
// before and after paths are the same, verify updated by size reset to 0
|
||||
Assert.Equal(imageCount, item.GetImages(imageType).Count());
|
||||
foreach (var image in item.GetImages(imageType))
|
||||
{
|
||||
Assert.Equal(updatedTime, image.DateModified);
|
||||
Assert.Equal(0, image.Height);
|
||||
Assert.Equal(0, image.Width);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(ImageType.Primary, 1, false)]
|
||||
[InlineData(ImageType.Backdrop, 2, false)]
|
||||
[InlineData(ImageType.Primary, 1, true)]
|
||||
[InlineData(ImageType.Backdrop, 2, true)]
|
||||
public async void RefreshImages_PopulatedItemPopulatedProviderDynamic_UpdatesImagesIfForced(ImageType imageType, int imageCount, bool forceRefresh)
|
||||
{
|
||||
var item = GetItemWithImages(imageType, imageCount, false);
|
||||
|
||||
var libraryOptions = GetLibraryOptions(item, imageType, imageCount);
|
||||
|
||||
var imageResponse = new DynamicImageResponse
|
||||
{
|
||||
HasImage = true,
|
||||
Format = ImageFormat.Jpg,
|
||||
Path = "url path",
|
||||
Protocol = MediaProtocol.Http
|
||||
};
|
||||
|
||||
var dynamicProvider = new Mock<IDynamicImageProvider>(MockBehavior.Strict);
|
||||
dynamicProvider.Setup(rp => rp.Name).Returns("MockDynamicProvider");
|
||||
dynamicProvider.Setup(rp => rp.GetSupportedImages(item))
|
||||
.Returns(new[] { imageType });
|
||||
dynamicProvider.Setup(rp => rp.GetImage(item, imageType, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(imageResponse);
|
||||
|
||||
var refreshOptions = forceRefresh
|
||||
? new ImageRefreshOptions(Mock.Of<IDirectoryService>())
|
||||
{
|
||||
ImageRefreshMode = MetadataRefreshMode.FullRefresh,
|
||||
ReplaceAllImages = true
|
||||
}
|
||||
: new ImageRefreshOptions(Mock.Of<IDirectoryService>());
|
||||
|
||||
var itemImageProvider = GetItemImageProvider(null, new Mock<IFileSystem>());
|
||||
var result = await itemImageProvider.RefreshImages(item, libraryOptions, new List<IImageProvider> { dynamicProvider.Object }, refreshOptions, CancellationToken.None);
|
||||
|
||||
Assert.Equal(forceRefresh, result.UpdateType.HasFlag(ItemUpdateType.ImageUpdate));
|
||||
if (forceRefresh)
|
||||
{
|
||||
// replaces multi-types
|
||||
Assert.Single(item.GetImages(imageType));
|
||||
}
|
||||
else
|
||||
{
|
||||
// adds to multi-types if room
|
||||
Assert.Equal(imageCount, item.GetImages(imageType).Count());
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(ImageType.Primary, 1, true, MediaProtocol.Http)]
|
||||
[InlineData(ImageType.Backdrop, 2, true, MediaProtocol.Http)]
|
||||
[InlineData(ImageType.Primary, 1, true, MediaProtocol.File)]
|
||||
[InlineData(ImageType.Backdrop, 2, true, MediaProtocol.File)]
|
||||
[InlineData(ImageType.Primary, 1, false, MediaProtocol.File)]
|
||||
[InlineData(ImageType.Backdrop, 2, false, MediaProtocol.File)]
|
||||
public async void RefreshImages_EmptyItemPopulatedProviderDynamic_AddsImages(ImageType imageType, int imageCount, bool responseHasPath, MediaProtocol protocol)
|
||||
{
|
||||
// Has to exist for querying DateModified time on file, results stored but not checked so not populating
|
||||
BaseItem.FileSystem = Mock.Of<IFileSystem>();
|
||||
|
||||
var item = new Video();
|
||||
|
||||
var libraryOptions = GetLibraryOptions(item, imageType, imageCount);
|
||||
|
||||
// Path must exist if set: is read in as a stream by AsyncFile.OpenRead
|
||||
var imageResponse = new DynamicImageResponse
|
||||
{
|
||||
HasImage = true,
|
||||
Format = ImageFormat.Jpg,
|
||||
Path = responseHasPath ? string.Format(CultureInfo.InvariantCulture, TestDataImagePath, 0) : null,
|
||||
Protocol = protocol
|
||||
};
|
||||
|
||||
var dynamicProvider = new Mock<IDynamicImageProvider>(MockBehavior.Strict);
|
||||
dynamicProvider.Setup(rp => rp.Name).Returns("MockDynamicProvider");
|
||||
dynamicProvider.Setup(rp => rp.GetSupportedImages(item))
|
||||
.Returns(new[] { imageType });
|
||||
dynamicProvider.Setup(rp => rp.GetImage(item, imageType, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(imageResponse);
|
||||
|
||||
var refreshOptions = new ImageRefreshOptions(Mock.Of<IDirectoryService>());
|
||||
|
||||
var providerManager = new Mock<IProviderManager>(MockBehavior.Strict);
|
||||
providerManager.Setup(pm => pm.SaveImage(item, It.IsAny<Stream>(), It.IsAny<string>(), imageType, null, It.IsAny<CancellationToken>()))
|
||||
.Callback<BaseItem, Stream, string, ImageType, int?, CancellationToken>((callbackItem, _, _, callbackType, _, _) => callbackItem.SetImagePath(callbackType, 0, new FileSystemMetadata()))
|
||||
.Returns(Task.CompletedTask);
|
||||
var itemImageProvider = GetItemImageProvider(providerManager.Object, null);
|
||||
var result = await itemImageProvider.RefreshImages(item, libraryOptions, new List<IImageProvider> { dynamicProvider.Object }, refreshOptions, CancellationToken.None);
|
||||
|
||||
Assert.True(result.UpdateType.HasFlag(ItemUpdateType.ImageUpdate));
|
||||
// dynamic provider unable to return multiple images
|
||||
Assert.Single(item.GetImages(imageType));
|
||||
if (protocol == MediaProtocol.Http)
|
||||
{
|
||||
Assert.Equal(imageResponse.Path, item.GetImagePath(imageType, 0));
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(ImageType.Primary, 1, false)]
|
||||
[InlineData(ImageType.Backdrop, 1, false)]
|
||||
[InlineData(ImageType.Backdrop, 2, false)]
|
||||
[InlineData(ImageType.Primary, 1, true)]
|
||||
[InlineData(ImageType.Backdrop, 1, true)]
|
||||
[InlineData(ImageType.Backdrop, 2, true)]
|
||||
public async void RefreshImages_PopulatedItemPopulatedProviderRemote_UpdatesImagesIfForced(ImageType imageType, int imageCount, bool forceRefresh)
|
||||
{
|
||||
var item = GetItemWithImages(imageType, imageCount, false);
|
||||
|
||||
var libraryOptions = GetLibraryOptions(item, imageType, imageCount);
|
||||
|
||||
var remoteProvider = new Mock<IRemoteImageProvider>(MockBehavior.Strict);
|
||||
remoteProvider.Setup(rp => rp.Name).Returns("MockRemoteProvider");
|
||||
remoteProvider.Setup(rp => rp.GetSupportedImages(item))
|
||||
.Returns(new[] { imageType });
|
||||
|
||||
var refreshOptions = forceRefresh
|
||||
? new ImageRefreshOptions(Mock.Of<IDirectoryService>())
|
||||
{
|
||||
ImageRefreshMode = MetadataRefreshMode.FullRefresh,
|
||||
ReplaceAllImages = true
|
||||
}
|
||||
: new ImageRefreshOptions(Mock.Of<IDirectoryService>());
|
||||
|
||||
var remoteInfo = new RemoteImageInfo[imageCount];
|
||||
for (int i = 0; i < imageCount; i++)
|
||||
{
|
||||
remoteInfo[i] = new RemoteImageInfo
|
||||
{
|
||||
Type = imageType,
|
||||
Url = "image url " + i,
|
||||
Width = 1 // min width is set to 0, this will always pass
|
||||
};
|
||||
}
|
||||
|
||||
var providerManager = new Mock<IProviderManager>(MockBehavior.Strict);
|
||||
providerManager.Setup(pm => pm.GetAvailableRemoteImages(It.IsAny<BaseItem>(), It.IsAny<RemoteImageQuery>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(remoteInfo);
|
||||
var itemImageProvider = GetItemImageProvider(providerManager.Object, new Mock<IFileSystem>());
|
||||
var result = await itemImageProvider.RefreshImages(item, libraryOptions, new List<IImageProvider> { remoteProvider.Object }, refreshOptions, CancellationToken.None);
|
||||
|
||||
Assert.Equal(forceRefresh, result.UpdateType.HasFlag(ItemUpdateType.ImageUpdate));
|
||||
Assert.Equal(imageCount, item.GetImages(imageType).Count());
|
||||
foreach (var image in item.GetImages(imageType))
|
||||
{
|
||||
if (forceRefresh)
|
||||
{
|
||||
Assert.Matches(@"image url [0-9]", image.Path);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.DoesNotMatch(@"image url [0-9]", image.Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(ImageType.Primary, 0, false)] // singular type only fetches if type is missing from item, no caching
|
||||
[InlineData(ImageType.Backdrop, 0, false)] // empty item, no cache to check
|
||||
[InlineData(ImageType.Backdrop, 1, false)] // populated item, cached so no download
|
||||
[InlineData(ImageType.Backdrop, 1, true)] // populated item, forced to download
|
||||
public async void RefreshImages_NonStubItemPopulatedProviderRemote_DownloadsIfNecessary(ImageType imageType, int initialImageCount, bool fullRefresh)
|
||||
{
|
||||
var targetImageCount = 1;
|
||||
|
||||
// Set path and media source manager so images will be downloaded (EnableImageStub will return false)
|
||||
var item = GetItemWithImages(imageType, initialImageCount, false);
|
||||
item.Path = "non-empty path";
|
||||
BaseItem.MediaSourceManager = Mock.Of<IMediaSourceManager>();
|
||||
|
||||
// seek 2 so it won't short-circuit out of downloading when populated
|
||||
var libraryOptions = GetLibraryOptions(item, imageType, 2);
|
||||
|
||||
const string Content = "Content";
|
||||
var remoteProvider = new Mock<IRemoteImageProvider>(MockBehavior.Strict);
|
||||
remoteProvider.Setup(rp => rp.Name).Returns("MockRemoteProvider");
|
||||
remoteProvider.Setup(rp => rp.GetSupportedImages(item))
|
||||
.Returns(new[] { imageType });
|
||||
remoteProvider.Setup(rp => rp.GetImageResponse(It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync((string url, CancellationToken _) => new HttpResponseMessage
|
||||
{
|
||||
ReasonPhrase = url,
|
||||
StatusCode = HttpStatusCode.OK,
|
||||
Content = new StringContent(Content, Encoding.UTF8, "image/jpeg")
|
||||
});
|
||||
|
||||
var refreshOptions = fullRefresh
|
||||
? new ImageRefreshOptions(Mock.Of<IDirectoryService>())
|
||||
{
|
||||
ImageRefreshMode = MetadataRefreshMode.FullRefresh,
|
||||
ReplaceAllImages = true
|
||||
}
|
||||
: new ImageRefreshOptions(Mock.Of<IDirectoryService>());
|
||||
|
||||
var remoteInfo = new RemoteImageInfo[targetImageCount];
|
||||
for (int i = 0; i < targetImageCount; i++)
|
||||
{
|
||||
remoteInfo[i] = new RemoteImageInfo()
|
||||
{
|
||||
Type = imageType,
|
||||
Url = "image url " + i,
|
||||
Width = 1 // min width is set to 0, this will always pass
|
||||
};
|
||||
}
|
||||
|
||||
var providerManager = new Mock<IProviderManager>(MockBehavior.Strict);
|
||||
providerManager.Setup(pm => pm.GetAvailableRemoteImages(It.IsAny<BaseItem>(), It.IsAny<RemoteImageQuery>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(remoteInfo);
|
||||
providerManager.Setup(pm => pm.SaveImage(item, It.IsAny<Stream>(), It.IsAny<string>(), imageType, null, It.IsAny<CancellationToken>()))
|
||||
.Callback<BaseItem, Stream, string, ImageType, int?, CancellationToken>((callbackItem, _, _, callbackType, _, _) =>
|
||||
callbackItem.SetImagePath(callbackType, callbackItem.AllowsMultipleImages(callbackType) ? callbackItem.GetImages(callbackType).Count() : 0, new FileSystemMetadata()))
|
||||
.Returns(Task.CompletedTask);
|
||||
var fileSystem = new Mock<IFileSystem>();
|
||||
// match reported file size to image content length - condition for skipping already downloaded multi-images
|
||||
fileSystem.Setup(fs => fs.GetFileInfo(It.IsAny<string>()))
|
||||
.Returns(new FileSystemMetadata { Length = Content.Length });
|
||||
var itemImageProvider = GetItemImageProvider(providerManager.Object, fileSystem);
|
||||
var result = await itemImageProvider.RefreshImages(item, libraryOptions, new List<IImageProvider> { remoteProvider.Object }, refreshOptions, CancellationToken.None);
|
||||
|
||||
Assert.Equal(initialImageCount == 0 || fullRefresh, result.UpdateType.HasFlag(ItemUpdateType.ImageUpdate));
|
||||
Assert.Equal(targetImageCount, item.GetImages(imageType).Count());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(GetImageTypesWithCount))]
|
||||
public async void RefreshImages_EmptyItemPopulatedProviderRemoteExtras_LimitsImages(ImageType imageType, int imageCount)
|
||||
{
|
||||
var item = new Video();
|
||||
|
||||
var libraryOptions = GetLibraryOptions(item, imageType, imageCount);
|
||||
|
||||
var remoteProvider = new Mock<IRemoteImageProvider>(MockBehavior.Strict);
|
||||
remoteProvider.Setup(rp => rp.Name).Returns("MockRemoteProvider");
|
||||
remoteProvider.Setup(rp => rp.GetSupportedImages(item))
|
||||
.Returns(new[] { imageType });
|
||||
|
||||
var refreshOptions = new ImageRefreshOptions(Mock.Of<IDirectoryService>());
|
||||
|
||||
// populate remote with double the required images to verify count is trimmed to the library option count
|
||||
var remoteInfoCount = imageCount * 2;
|
||||
var remoteInfo = new RemoteImageInfo[remoteInfoCount];
|
||||
for (int i = 0; i < remoteInfoCount; i++)
|
||||
{
|
||||
remoteInfo[i] = new RemoteImageInfo()
|
||||
{
|
||||
Type = imageType,
|
||||
Url = "image url " + i,
|
||||
Width = 1 // min width is set to 0, this will always pass
|
||||
};
|
||||
}
|
||||
|
||||
var providerManager = new Mock<IProviderManager>(MockBehavior.Strict);
|
||||
providerManager.Setup(pm => pm.GetAvailableRemoteImages(It.IsAny<BaseItem>(), It.IsAny<RemoteImageQuery>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(remoteInfo);
|
||||
var itemImageProvider = GetItemImageProvider(providerManager.Object, null);
|
||||
var result = await itemImageProvider.RefreshImages(item, libraryOptions, new List<IImageProvider> { remoteProvider.Object }, refreshOptions, CancellationToken.None);
|
||||
|
||||
Assert.True(result.UpdateType.HasFlag(ItemUpdateType.ImageUpdate));
|
||||
var actualImages = item.GetImages(imageType).ToList();
|
||||
Assert.Equal(imageCount, actualImages.Count);
|
||||
// images from the provider manager are sorted by preference (earlier images are higher priority) so we can verify that low url numbers are chosen
|
||||
foreach (var image in actualImages)
|
||||
{
|
||||
var index = int.Parse(Regex.Match(image.Path, @"[0-9]+").Value, NumberStyles.Integer, CultureInfo.InvariantCulture);
|
||||
Assert.True(index < imageCount);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(GetImageTypesWithCount))]
|
||||
public async void RefreshImages_PopulatedItemEmptyProviderRemoteFullRefresh_DoesntClearImages(ImageType imageType, int imageCount)
|
||||
{
|
||||
var item = GetItemWithImages(imageType, imageCount, false);
|
||||
|
||||
var libraryOptions = GetLibraryOptions(item, imageType, imageCount);
|
||||
|
||||
var remoteProvider = new Mock<IRemoteImageProvider>(MockBehavior.Strict);
|
||||
remoteProvider.Setup(rp => rp.Name).Returns("MockRemoteProvider");
|
||||
remoteProvider.Setup(rp => rp.GetSupportedImages(item))
|
||||
.Returns(new[] { imageType });
|
||||
|
||||
var refreshOptions = new ImageRefreshOptions(Mock.Of<IDirectoryService>())
|
||||
{
|
||||
ImageRefreshMode = MetadataRefreshMode.FullRefresh,
|
||||
ReplaceAllImages = true
|
||||
};
|
||||
|
||||
var itemImageProvider = GetItemImageProvider(Mock.Of<IProviderManager>(), null);
|
||||
var result = await itemImageProvider.RefreshImages(item, libraryOptions, new List<IImageProvider> { remoteProvider.Object }, refreshOptions, CancellationToken.None);
|
||||
|
||||
Assert.False(result.UpdateType.HasFlag(ItemUpdateType.ImageUpdate));
|
||||
Assert.Equal(imageCount, item.GetImages(imageType).Count());
|
||||
}
|
||||
|
||||
private static ItemImageProvider GetItemImageProvider(IProviderManager? providerManager, Mock<IFileSystem>? mockFileSystem)
|
||||
{
|
||||
// strict to ensure this isn't accidentally used where a prepared mock is intended
|
||||
providerManager ??= Mock.Of<IProviderManager>(MockBehavior.Strict);
|
||||
|
||||
// BaseItem.ValidateImages depends on the directory service being able to list directory contents, give it the expected valid file paths
|
||||
mockFileSystem ??= new Mock<IFileSystem>(MockBehavior.Strict);
|
||||
mockFileSystem.Setup(fs => fs.GetFilePaths(It.IsAny<string>(), It.IsAny<bool>()))
|
||||
.Returns(new[]
|
||||
{
|
||||
string.Format(CultureInfo.InvariantCulture, TestDataImagePath, 0),
|
||||
string.Format(CultureInfo.InvariantCulture, TestDataImagePath, 1)
|
||||
});
|
||||
|
||||
return new ItemImageProvider(new NullLogger<ItemImageProvider>(), providerManager, mockFileSystem.Object);
|
||||
}
|
||||
|
||||
private static BaseItem GetItemWithImages(ImageType type, int count, bool validPaths)
|
||||
{
|
||||
// Has to exist for querying DateModified time on file, results stored but not checked so not populating
|
||||
BaseItem.FileSystem ??= Mock.Of<IFileSystem>();
|
||||
|
||||
var item = new Video();
|
||||
|
||||
var path = validPaths ? TestDataImagePath : "invalid path {0}";
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
item.SetImagePath(type, i, new FileSystemMetadata
|
||||
{
|
||||
FullName = string.Format(CultureInfo.InvariantCulture, path, i),
|
||||
});
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ILocalImageProvider GetImageProvider(ImageType type, int count, bool validPaths)
|
||||
{
|
||||
var images = GetImages(type, count, validPaths);
|
||||
|
||||
var imageProvider = new Mock<ILocalImageProvider>();
|
||||
imageProvider.Setup(ip => ip.GetImages(It.IsAny<BaseItem>(), It.IsAny<IDirectoryService>()))
|
||||
.Returns(images);
|
||||
return imageProvider.Object;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a list of <see cref="LocalImageInfo"/> references of the specified type and size, optionally pointing to files that exist.
|
||||
/// </summary>
|
||||
private static LocalImageInfo[] GetImages(ImageType type, int count, bool validPaths)
|
||||
{
|
||||
var path = validPaths ? TestDataImagePath : "invalid path {0}";
|
||||
var images = new LocalImageInfo[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
images[i] = new LocalImageInfo
|
||||
{
|
||||
Type = type,
|
||||
FileInfo = new FileSystemMetadata
|
||||
{
|
||||
FullName = string.Format(CultureInfo.InvariantCulture, path, i)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return images;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a <see cref="LibraryOptions"/> object that will allow for the requested number of images for the target type.
|
||||
/// </summary>
|
||||
private static LibraryOptions GetLibraryOptions(BaseItem item, ImageType type, int count)
|
||||
{
|
||||
return new LibraryOptions
|
||||
{
|
||||
TypeOptions = new[]
|
||||
{
|
||||
new TypeOptions
|
||||
{
|
||||
Type = item.GetType().Name,
|
||||
ImageOptions = new[]
|
||||
{
|
||||
new ImageOption
|
||||
{
|
||||
Type = type,
|
||||
Limit = count,
|
||||
MinWidth = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Model.Drawing;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Providers.MediaInfo;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Providers.Tests.MediaInfo
|
||||
{
|
||||
public class EmbeddedImageProviderTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(typeof(AudioBook))]
|
||||
[InlineData(typeof(BoxSet))]
|
||||
[InlineData(typeof(Series))]
|
||||
[InlineData(typeof(Season))]
|
||||
[InlineData(typeof(Episode), ImageType.Primary)]
|
||||
[InlineData(typeof(Movie), ImageType.Logo, ImageType.Backdrop, ImageType.Primary)]
|
||||
public void GetSupportedImages_AnyBaseItem_ReturnsExpected(Type type, params ImageType[] expected)
|
||||
{
|
||||
BaseItem item = (BaseItem)Activator.CreateInstance(type)!;
|
||||
var embeddedImageProvider = new EmbeddedImageProvider(Mock.Of<IMediaSourceManager>(), Mock.Of<IMediaEncoder>(), new NullLogger<EmbeddedImageProvider>());
|
||||
var actual = embeddedImageProvider.GetSupportedImages(item);
|
||||
Assert.Equal(expected.OrderBy(i => i.ToString()), actual.OrderBy(i => i.ToString()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void GetImage_NoStreams_ReturnsNoImage()
|
||||
{
|
||||
var input = new Movie();
|
||||
|
||||
var mediaSourceManager = GetMediaSourceManager(input, new List<MediaAttachment>(), new List<MediaStream>());
|
||||
var embeddedImageProvider = new EmbeddedImageProvider(mediaSourceManager, null, new NullLogger<EmbeddedImageProvider>());
|
||||
|
||||
var actual = await embeddedImageProvider.GetImage(input, ImageType.Primary, CancellationToken.None);
|
||||
Assert.NotNull(actual);
|
||||
Assert.False(actual.HasImage);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("chapter", null, 1, ImageType.Chapter, null)] // unexpected type, nothing found
|
||||
[InlineData("unmatched", null, 1, ImageType.Primary, null)] // doesn't default on no match
|
||||
[InlineData("clearlogo.png", null, 1, ImageType.Logo, ImageFormat.Png)] // extract extension from name
|
||||
[InlineData("backdrop", "image/bmp", 2, ImageType.Backdrop, ImageFormat.Bmp)] // extract extension from mimetype
|
||||
[InlineData("poster", null, 3, ImageType.Primary, ImageFormat.Jpg)] // default extension to jpg
|
||||
public async void GetImage_Attachment_ReturnsCorrectSelection(string filename, string mimetype, int targetIndex, ImageType type, ImageFormat? expectedFormat)
|
||||
{
|
||||
var attachments = new List<MediaAttachment>();
|
||||
string pathPrefix = "path";
|
||||
for (int i = 1; i <= targetIndex; i++)
|
||||
{
|
||||
var name = i == targetIndex ? filename : "unmatched";
|
||||
attachments.Add(new()
|
||||
{
|
||||
FileName = name,
|
||||
MimeType = mimetype,
|
||||
Index = i
|
||||
});
|
||||
}
|
||||
|
||||
var input = new Movie();
|
||||
|
||||
var mediaEncoder = new Mock<IMediaEncoder>(MockBehavior.Strict);
|
||||
mediaEncoder.Setup(encoder => encoder.ExtractVideoImage(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MediaSourceInfo>(), It.IsAny<MediaStream>(), It.IsAny<int>(), It.IsAny<ImageFormat>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<string, string, MediaSourceInfo, MediaStream, int, ImageFormat, CancellationToken>((_, _, _, _, index, ext, _) => Task.FromResult(pathPrefix + index + "." + ext));
|
||||
var mediaSourceManager = GetMediaSourceManager(input, attachments, new List<MediaStream>());
|
||||
var embeddedImageProvider = new EmbeddedImageProvider(mediaSourceManager, mediaEncoder.Object, new NullLogger<EmbeddedImageProvider>());
|
||||
|
||||
var actual = await embeddedImageProvider.GetImage(input, type, CancellationToken.None);
|
||||
Assert.NotNull(actual);
|
||||
if (expectedFormat == null)
|
||||
{
|
||||
Assert.False(actual.HasImage);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.True(actual.HasImage);
|
||||
Assert.Equal(pathPrefix + targetIndex + "." + expectedFormat, actual.Path, StringComparer.OrdinalIgnoreCase);
|
||||
Assert.Equal(expectedFormat, actual.Format);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("chapter", null, 1, ImageType.Chapter, null)] // unexpected type, nothing found
|
||||
[InlineData(null, null, 1, ImageType.Backdrop, null)] // no label, can only find primary
|
||||
[InlineData(null, null, 1, ImageType.Primary, ImageFormat.Jpg)] // no label, finds primary
|
||||
[InlineData("backdrop", null, 2, ImageType.Backdrop, ImageFormat.Jpg)] // uses label to find index 2, not just pulling first stream
|
||||
[InlineData("cover", null, 2, ImageType.Primary, ImageFormat.Jpg)] // uses label to find index 2, not just pulling first stream
|
||||
[InlineData(null, "mjpeg", 1, ImageType.Primary, ImageFormat.Jpg)]
|
||||
[InlineData(null, "png", 1, ImageType.Primary, ImageFormat.Png)]
|
||||
[InlineData(null, "gif", 1, ImageType.Primary, ImageFormat.Gif)]
|
||||
public async void GetImage_Embedded_ReturnsCorrectSelection(string label, string? codec, int targetIndex, ImageType type, ImageFormat? expectedFormat)
|
||||
{
|
||||
var streams = new List<MediaStream>();
|
||||
for (int i = 1; i <= targetIndex; i++)
|
||||
{
|
||||
var comment = i == targetIndex ? label : "unmatched";
|
||||
streams.Add(new()
|
||||
{
|
||||
Type = MediaStreamType.EmbeddedImage,
|
||||
Index = i,
|
||||
Comment = comment,
|
||||
Codec = codec
|
||||
});
|
||||
}
|
||||
|
||||
var input = new Movie();
|
||||
|
||||
var pathPrefix = "path";
|
||||
var mediaEncoder = new Mock<IMediaEncoder>(MockBehavior.Strict);
|
||||
mediaEncoder.Setup(encoder => encoder.ExtractVideoImage(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MediaSourceInfo>(), It.IsAny<MediaStream>(), It.IsAny<int>(), It.IsAny<ImageFormat>(), It.IsAny<CancellationToken>()))
|
||||
.Returns<string, string, MediaSourceInfo, MediaStream, int, ImageFormat, CancellationToken>((_, _, _, stream, index, ext, _) =>
|
||||
{
|
||||
Assert.Equal(streams[index - 1], stream);
|
||||
return Task.FromResult(pathPrefix + index + "." + ext);
|
||||
});
|
||||
var mediaSourceManager = GetMediaSourceManager(input, new List<MediaAttachment>(), streams);
|
||||
var embeddedImageProvider = new EmbeddedImageProvider(mediaSourceManager, mediaEncoder.Object, new NullLogger<EmbeddedImageProvider>());
|
||||
|
||||
var actual = await embeddedImageProvider.GetImage(input, type, CancellationToken.None);
|
||||
Assert.NotNull(actual);
|
||||
if (expectedFormat == null)
|
||||
{
|
||||
Assert.False(actual.HasImage);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.True(actual.HasImage);
|
||||
Assert.Equal(pathPrefix + targetIndex + "." + expectedFormat, actual.Path, StringComparer.OrdinalIgnoreCase);
|
||||
Assert.Equal(expectedFormat, actual.Format);
|
||||
}
|
||||
}
|
||||
|
||||
private static IMediaSourceManager GetMediaSourceManager(BaseItem item, List<MediaAttachment> mediaAttachments, List<MediaStream> mediaStreams)
|
||||
{
|
||||
var mediaSourceManager = new Mock<IMediaSourceManager>(MockBehavior.Strict);
|
||||
mediaSourceManager.Setup(i => i.GetMediaAttachments(item.Id))
|
||||
.Returns(mediaAttachments);
|
||||
mediaSourceManager.Setup(i => i.GetMediaStreams(It.Is<MediaStreamQuery>(q => q.ItemId == item.Id && q.Type == MediaStreamType.EmbeddedImage)))
|
||||
.Returns(mediaStreams);
|
||||
return mediaSourceManager.Object;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma warning disable CA1002 // Do not expose generic lists
|
||||
#pragma warning disable CA1002 // Do not expose generic lists
|
||||
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Model.Entities;
|
||||
@@ -11,11 +11,12 @@ namespace Jellyfin.Providers.Tests.MediaInfo
|
||||
{
|
||||
public class SubtitleResolverTests
|
||||
{
|
||||
public static IEnumerable<object[]> AddExternalSubtitleStreams_GivenMixedFilenames_ReturnsValidSubtitles_TestData()
|
||||
public static TheoryData<List<MediaStream>, string, int, string[], MediaStream[]> AddExternalSubtitleStreams_GivenMixedFilenames_ReturnsValidSubtitles_TestData()
|
||||
{
|
||||
var data = new TheoryData<List<MediaStream>, string, int, string[], MediaStream[]>();
|
||||
|
||||
var index = 0;
|
||||
yield return new object[]
|
||||
{
|
||||
data.Add(
|
||||
new List<MediaStream>(),
|
||||
"/video/My.Video.mkv",
|
||||
index,
|
||||
@@ -52,8 +53,9 @@ namespace Jellyfin.Providers.Tests.MediaInfo
|
||||
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),
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -78,9 +80,40 @@ namespace Jellyfin.Providers.Tests.MediaInfo
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/video/My Video.mkv", "/video/My Video.srt", "srt", null, false, false)]
|
||||
[InlineData("/video/My.Video.mkv", "/video/My.Video.srt", "srt", null, false, false)]
|
||||
[InlineData("/video/My.Video.mkv", "/video/My.Video.foreign.srt", "srt", null, true, false)]
|
||||
[InlineData("/video/My Video.mkv", "/video/My Video.forced.srt", "srt", null, true, false)]
|
||||
[InlineData("/video/My.Video.mkv", "/video/My.Video.default.srt", "srt", null, false, true)]
|
||||
[InlineData("/video/My.Video.mkv", "/video/My.Video.forced.default.srt", "srt", null, true, true)]
|
||||
[InlineData("/video/My.Video.mkv", "/video/My.Video.en.srt", "srt", "en", false, false)]
|
||||
[InlineData("/video/My.Video.mkv", "/video/My.Video.default.en.srt", "srt", "en", false, true)]
|
||||
[InlineData("/video/My.Video.mkv", "/video/My.Video.default.forced.en.srt", "srt", "en", true, true)]
|
||||
[InlineData("/video/My.Video.mkv", "/video/My.Video.en.default.forced.srt", "srt", "en", true, true)]
|
||||
public void AddExternalSubtitleStreams_GivenSingleFile_ReturnsExpectedSubtitle(string videoPath, string file, string codec, string? language, bool isForced, bool isDefault)
|
||||
{
|
||||
var streams = new List<MediaStream>();
|
||||
var expected = CreateMediaStream(file, codec, language, 0, isForced, isDefault);
|
||||
|
||||
new SubtitleResolver(Mock.Of<ILocalizationManager>()).AddExternalSubtitleStreams(streams, videoPath, 0, new[] { file });
|
||||
|
||||
Assert.Single(streams);
|
||||
|
||||
var actual = streams[0];
|
||||
|
||||
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 ()
|
||||
return new()
|
||||
{
|
||||
Index = index,
|
||||
Codec = codec,
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Model.Drawing;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Providers.MediaInfo;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Providers.Tests.MediaInfo
|
||||
{
|
||||
public class VideoImageProviderTests
|
||||
{
|
||||
private static TheoryData<Video> GetImage_UnsupportedInput_ReturnsNoImage_TestData()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
new Movie { IsPlaceHolder = true },
|
||||
|
||||
new Movie { DefaultVideoStreamIndex = null },
|
||||
|
||||
// set a default index but don't put anything there (invalid input, but provider shouldn't break)
|
||||
new Movie { DefaultVideoStreamIndex = 0 }
|
||||
};
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(GetImage_UnsupportedInput_ReturnsNoImage_TestData))]
|
||||
public async void GetImage_UnsupportedInput_ReturnsNoImage(Video input)
|
||||
{
|
||||
var mediaSourceManager = GetMediaSourceManager(input, null, new List<MediaStream>());
|
||||
var videoImageProvider = new VideoImageProvider(mediaSourceManager, Mock.Of<IMediaEncoder>(), new NullLogger<VideoImageProvider>());
|
||||
|
||||
var actual = await videoImageProvider.GetImage(input, ImageType.Primary, CancellationToken.None);
|
||||
Assert.NotNull(actual);
|
||||
Assert.False(actual.HasImage);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1, 1)] // default not first stream
|
||||
[InlineData(5, 0)] // default out of valid range
|
||||
public async void GetImage_DefaultVideoStreams_ReturnsCorrectStreamImage(int defaultIndex, int targetIndex)
|
||||
{
|
||||
var input = new Movie { DefaultVideoStreamIndex = defaultIndex };
|
||||
|
||||
string targetPath = "path.jpg";
|
||||
var mediaStreams = new List<MediaStream>();
|
||||
var mediaEncoder = new Mock<IMediaEncoder>(MockBehavior.Strict);
|
||||
|
||||
for (int i = 0; i <= targetIndex; i++)
|
||||
{
|
||||
var mediaStream = new MediaStream { Type = MediaStreamType.Video, Index = i };
|
||||
mediaStreams.Add(mediaStream);
|
||||
|
||||
var path = i == targetIndex ? targetPath : "wrong stream called!";
|
||||
mediaEncoder.Setup(encoder => encoder.ExtractVideoImage(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MediaSourceInfo>(), mediaStream, It.IsAny<Video3DFormat?>(), It.IsAny<TimeSpan?>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(path));
|
||||
}
|
||||
|
||||
var defaultStream = defaultIndex < mediaStreams.Count ? mediaStreams[targetIndex] : null;
|
||||
var mediaSourceManager = GetMediaSourceManager(input, defaultStream, mediaStreams);
|
||||
|
||||
var videoImageProvider = new VideoImageProvider(mediaSourceManager, mediaEncoder.Object, new NullLogger<VideoImageProvider>());
|
||||
|
||||
var actual = await videoImageProvider.GetImage(input, ImageType.Primary, CancellationToken.None);
|
||||
Assert.NotNull(actual);
|
||||
Assert.True(actual.HasImage);
|
||||
Assert.Equal(targetPath, actual.Path);
|
||||
Assert.Equal(ImageFormat.Jpg, actual.Format);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, 10)] // default time
|
||||
[InlineData(500, 50)] // calculated time
|
||||
public async void GetImage_TimeSpan_SelectsCorrectTime(int? runTimeSeconds, long expectedSeconds)
|
||||
{
|
||||
MediaStream targetStream = new() { Type = MediaStreamType.Video, Index = 0 };
|
||||
var input = new Movie
|
||||
{
|
||||
DefaultVideoStreamIndex = 0,
|
||||
RunTimeTicks = runTimeSeconds * TimeSpan.TicksPerSecond
|
||||
};
|
||||
|
||||
var mediaSourceManager = GetMediaSourceManager(input, targetStream, new List<MediaStream> { targetStream });
|
||||
|
||||
// use a callback to catch the actual value
|
||||
// provides more information on failure than verifying a specific input was called on the mock
|
||||
TimeSpan? actualTimeSpan = null;
|
||||
var mediaEncoder = new Mock<IMediaEncoder>(MockBehavior.Strict);
|
||||
mediaEncoder.Setup(encoder => encoder.ExtractVideoImage(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MediaSourceInfo>(), It.IsAny<MediaStream>(), It.IsAny<Video3DFormat?>(), It.IsAny<TimeSpan?>(), CancellationToken.None))
|
||||
.Callback<string, string, MediaSourceInfo, MediaStream, Video3DFormat?, TimeSpan?, CancellationToken>((_, _, _, _, _, timeSpan, _) => actualTimeSpan = timeSpan)
|
||||
.Returns(Task.FromResult("path"));
|
||||
|
||||
var videoImageProvider = new VideoImageProvider(mediaSourceManager, mediaEncoder.Object, new NullLogger<VideoImageProvider>());
|
||||
|
||||
// not testing return, just verifying what gets requested for time span
|
||||
await videoImageProvider.GetImage(input, ImageType.Primary, CancellationToken.None);
|
||||
|
||||
Assert.Equal(TimeSpan.FromSeconds(expectedSeconds), actualTimeSpan);
|
||||
}
|
||||
|
||||
private static IMediaSourceManager GetMediaSourceManager(Video item, MediaStream? defaultStream, List<MediaStream> mediaStreams)
|
||||
{
|
||||
var defaultStreamList = new List<MediaStream>();
|
||||
if (defaultStream != null)
|
||||
{
|
||||
defaultStreamList.Add(defaultStream);
|
||||
}
|
||||
|
||||
var mediaSourceManager = new Mock<IMediaSourceManager>(MockBehavior.Strict);
|
||||
mediaSourceManager.Setup(i => i.GetMediaStreams(It.Is<MediaStreamQuery>(q => q.ItemId == item.Id && q.Index == item.DefaultVideoStreamIndex)))
|
||||
.Returns(defaultStreamList);
|
||||
mediaSourceManager.Setup(i => i.GetMediaStreams(It.Is<MediaStreamQuery>(q => q.ItemId == item.Id && q.Type == MediaStreamType.Video)))
|
||||
.Returns(mediaStreams);
|
||||
return mediaSourceManager.Object;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,5 +23,18 @@ namespace Jellyfin.Providers.Tests.Tmdb
|
||||
{
|
||||
Assert.Equal(expected, TmdbUtils.NormalizeLanguage(input!));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, null, null)]
|
||||
[InlineData(null, "en-US", null)]
|
||||
[InlineData("en", null, "en")]
|
||||
[InlineData("en", "en-US", "en-US")]
|
||||
[InlineData("fr-CA", "fr-BE", "fr-CA")]
|
||||
[InlineData("fr-CA", "fr", "fr-CA")]
|
||||
[InlineData("de", "en-US", "de")]
|
||||
public static void AdjustImageLanguage_Valid_Success(string imageLanguage, string requestLanguage, string expected)
|
||||
{
|
||||
Assert.Equal(expected, TmdbUtils.AdjustImageLanguage(imageLanguage, requestLanguage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,10 +32,11 @@ namespace Jellyfin.Server.Implementations.Tests.Data
|
||||
_sqliteItemRepository = _fixture.Create<SqliteItemRepository>();
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> ItemImageInfoFromValueString_Valid_TestData()
|
||||
public static TheoryData<string, ItemImageInfo> ItemImageInfoFromValueString_Valid_TestData()
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
var data = new TheoryData<string, ItemImageInfo>();
|
||||
|
||||
data.Add(
|
||||
"/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
|
||||
{
|
||||
@@ -45,41 +46,33 @@ namespace Jellyfin.Server.Implementations.Tests.Data
|
||||
Width = 1920,
|
||||
Height = 1080,
|
||||
BlurHash = "WjQbtJtSO8nhNZ%L_Io#R*oaS6o}-;adXAoIn7j[%hW9s:WGw[nN"
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
data.Add(
|
||||
"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[]
|
||||
{
|
||||
data.Add(
|
||||
"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[]
|
||||
{
|
||||
data.Add(
|
||||
"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[]
|
||||
{
|
||||
data.Add(
|
||||
"%MetadataPath%/library/68/68578562b96c80a7ebd530848801f645/poster.jpg*637264380567586027*Primary*600*336",
|
||||
new ItemImageInfo
|
||||
{
|
||||
@@ -88,8 +81,9 @@ namespace Jellyfin.Server.Implementations.Tests.Data
|
||||
DateModified = new DateTime(637264380567586027, DateTimeKind.Utc),
|
||||
Width = 600,
|
||||
Height = 336
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -117,10 +111,10 @@ namespace Jellyfin.Server.Implementations.Tests.Data
|
||||
Assert.Null(_sqliteItemRepository.ItemImageInfoFromValueString(value));
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> DeserializeImages_Valid_TestData()
|
||||
public static TheoryData<string, ItemImageInfo[]> DeserializeImages_Valid_TestData()
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
var data = new TheoryData<string, ItemImageInfo[]>();
|
||||
data.Add(
|
||||
"/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[]
|
||||
{
|
||||
@@ -133,11 +127,9 @@ namespace Jellyfin.Server.Implementations.Tests.Data
|
||||
Height = 1080,
|
||||
BlurHash = "WjQbtJtSO8nhNZ%L_Io#R*oaS6o}-;adXAoIn7j[%hW9s:WGw[nN"
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
data.Add(
|
||||
"%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[]
|
||||
{
|
||||
@@ -165,24 +157,23 @@ namespace Jellyfin.Server.Implementations.Tests.Data
|
||||
Type = ImageType.Backdrop,
|
||||
DateModified = new DateTime(637261226721685297, DateTimeKind.Utc),
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> DeserializeImages_ValidAndInvalid_TestData()
|
||||
public static TheoryData<string, ItemImageInfo[]> DeserializeImages_ValidAndInvalid_TestData()
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
var data = new TheoryData<string, ItemImageInfo[]>();
|
||||
data.Add(
|
||||
string.Empty,
|
||||
Array.Empty<ItemImageInfo>()
|
||||
};
|
||||
Array.Empty<ItemImageInfo>());
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
data.Add(
|
||||
"/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 ()
|
||||
new()
|
||||
{
|
||||
Path = "/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg",
|
||||
Type = ImageType.Primary,
|
||||
@@ -191,14 +182,13 @@ namespace Jellyfin.Server.Implementations.Tests.Data
|
||||
Height = 1080,
|
||||
BlurHash = "WjQbtJtSO8nhNZ%L_Io#R*oaS6o}-;adXAoIn7j[%hW9s:WGw[nN"
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
data.Add(
|
||||
"|",
|
||||
Array.Empty<ItemImageInfo>()
|
||||
};
|
||||
Array.Empty<ItemImageInfo>());
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -242,30 +232,27 @@ namespace Jellyfin.Server.Implementations.Tests.Data
|
||||
Assert.Equal(expected, _sqliteItemRepository.SerializeImages(value));
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> DeserializeProviderIds_Valid_TestData()
|
||||
public static TheoryData<string, Dictionary<string, string>> DeserializeProviderIds_Valid_TestData()
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
var data = new TheoryData<string, Dictionary<string, string>>();
|
||||
|
||||
data.Add(
|
||||
"Imdb=tt0119567",
|
||||
new Dictionary<string, string>()
|
||||
{
|
||||
{ "Imdb", "tt0119567" },
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
data.Add(
|
||||
"Imdb=tt0119567|Tmdb=330|TmdbCollection=328",
|
||||
new Dictionary<string, string>()
|
||||
{
|
||||
{ "Imdb", "tt0119567" },
|
||||
{ "Tmdb", "330" },
|
||||
{ "TmdbCollection", "328" },
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
data.Add(
|
||||
"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>()
|
||||
{
|
||||
@@ -274,8 +261,9 @@ namespace Jellyfin.Server.Implementations.Tests.Data
|
||||
{ "AudioDbArtist", "111352" },
|
||||
{ "AudioDbAlbum", "2116560" },
|
||||
{ "MusicBrainzAlbumArtist", "20244d07-534f-4eff-b4d4-930878889970" },
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Jellyfin.Server.Implementations.Tests.HttpServer
|
||||
[Fact]
|
||||
public void DeserializeWebSocketMessage_SingleSegment_Success()
|
||||
{
|
||||
var con = new WebSocketConnection(new NullLogger<WebSocketConnection>(), null!, null!, null!);
|
||||
var con = new WebSocketConnection(new NullLogger<WebSocketConnection>(), null!, null!);
|
||||
var bytes = File.ReadAllBytes("Test Data/HttpServer/ForceKeepAlive.json");
|
||||
con.DeserializeWebSocketMessage(new ReadOnlySequence<byte>(bytes), out var bytesConsumed);
|
||||
Assert.Equal(109, bytesConsumed);
|
||||
@@ -23,7 +23,7 @@ namespace Jellyfin.Server.Implementations.Tests.HttpServer
|
||||
public void DeserializeWebSocketMessage_MultipleSegments_Success()
|
||||
{
|
||||
const int SplitPos = 64;
|
||||
var con = new WebSocketConnection(new NullLogger<WebSocketConnection>(), null!, null!, null!);
|
||||
var con = new WebSocketConnection(new NullLogger<WebSocketConnection>(), null!, null!);
|
||||
var bytes = File.ReadAllBytes("Test Data/HttpServer/ForceKeepAlive.json");
|
||||
var seg1 = new BufferSegment(new Memory<byte>(bytes, 0, SplitPos));
|
||||
var seg2 = seg1.Append(new Memory<byte>(bytes, SplitPos, bytes.Length - SplitPos));
|
||||
@@ -34,7 +34,7 @@ namespace Jellyfin.Server.Implementations.Tests.HttpServer
|
||||
[Fact]
|
||||
public void DeserializeWebSocketMessage_ValidPartial_Success()
|
||||
{
|
||||
var con = new WebSocketConnection(new NullLogger<WebSocketConnection>(), null!, null!, null!);
|
||||
var con = new WebSocketConnection(new NullLogger<WebSocketConnection>(), null!, null!);
|
||||
var bytes = File.ReadAllBytes("Test Data/HttpServer/ValidPartial.json");
|
||||
con.DeserializeWebSocketMessage(new ReadOnlySequence<byte>(bytes), out var bytesConsumed);
|
||||
Assert.Equal(109, bytesConsumed);
|
||||
@@ -43,7 +43,7 @@ namespace Jellyfin.Server.Implementations.Tests.HttpServer
|
||||
[Fact]
|
||||
public void DeserializeWebSocketMessage_Partial_ThrowJsonException()
|
||||
{
|
||||
var con = new WebSocketConnection(new NullLogger<WebSocketConnection>(), null!, null!, null!);
|
||||
var con = new WebSocketConnection(new NullLogger<WebSocketConnection>(), null!, null!);
|
||||
var bytes = File.ReadAllBytes("Test Data/HttpServer/Partial.json");
|
||||
Assert.Throws<JsonException>(() => con.DeserializeWebSocketMessage(new ReadOnlySequence<byte>(bytes), out var bytesConsumed));
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<ItemGroup>
|
||||
<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="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
@@ -32,7 +32,7 @@
|
||||
<!-- 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="StyleCop.Analyzers" Version="1.2.0-beta.376" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using Emby.Naming.Common;
|
||||
using Emby.Server.Implementations.Library.Resolvers.TV;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
@@ -14,22 +14,21 @@ namespace Jellyfin.Server.Implementations.Tests.Library
|
||||
{
|
||||
public class EpisodeResolverTest
|
||||
{
|
||||
private static readonly NamingOptions _namingOptions = new();
|
||||
|
||||
[Fact]
|
||||
public void Resolve_GivenVideoInExtrasFolder_DoesNotResolveToEpisode()
|
||||
{
|
||||
var season = new Season { Name = "Season 1" };
|
||||
var parent = new Folder { Name = "extras" };
|
||||
var libraryManagerMock = new Mock<ILibraryManager>();
|
||||
libraryManagerMock.Setup(x => x.GetItemById(It.IsAny<Guid>())).Returns(season);
|
||||
|
||||
var episodeResolver = new EpisodeResolver(libraryManagerMock.Object);
|
||||
var episodeResolver = new EpisodeResolver(_namingOptions);
|
||||
var itemResolveArgs = new ItemResolveArgs(
|
||||
Mock.Of<IServerApplicationPaths>(),
|
||||
Mock.Of<IDirectoryService>())
|
||||
{
|
||||
Parent = parent,
|
||||
CollectionType = CollectionType.TvShows,
|
||||
FileInfo = new FileSystemMetadata()
|
||||
FileInfo = new FileSystemMetadata
|
||||
{
|
||||
FullName = "All My Children/Season 01/Extras/All My Children S01E01 - Behind The Scenes.mkv"
|
||||
}
|
||||
@@ -45,14 +44,14 @@ namespace Jellyfin.Server.Implementations.Tests.Library
|
||||
|
||||
// Have to create a mock because of moq proxies not being castable to a concrete implementation
|
||||
// https://github.com/jellyfin/jellyfin/blob/ab0cff8556403e123642dc9717ba778329554634/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs#L48
|
||||
var episodeResolver = new EpisodeResolverMock(Mock.Of<ILibraryManager>());
|
||||
var episodeResolver = new EpisodeResolverMock(_namingOptions);
|
||||
var itemResolveArgs = new ItemResolveArgs(
|
||||
Mock.Of<IServerApplicationPaths>(),
|
||||
Mock.Of<IDirectoryService>())
|
||||
{
|
||||
Parent = series,
|
||||
CollectionType = CollectionType.TvShows,
|
||||
FileInfo = new FileSystemMetadata()
|
||||
FileInfo = new FileSystemMetadata
|
||||
{
|
||||
FullName = "Extras/Extras S01E01.mkv"
|
||||
}
|
||||
@@ -62,11 +61,11 @@ namespace Jellyfin.Server.Implementations.Tests.Library
|
||||
|
||||
private class EpisodeResolverMock : EpisodeResolver
|
||||
{
|
||||
public EpisodeResolverMock(ILibraryManager libraryManager) : base(libraryManager)
|
||||
public EpisodeResolverMock(NamingOptions namingOptions) : base(namingOptions)
|
||||
{
|
||||
}
|
||||
|
||||
protected override TVideoType ResolveVideo<TVideoType>(ItemResolveArgs args, bool parseName) => new ();
|
||||
protected override TVideoType ResolveVideo<TVideoType>(ItemResolveArgs args, bool parseName) => new();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,285 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using AutoFixture;
|
||||
using AutoFixture.AutoMoq;
|
||||
using Emby.Naming.Common;
|
||||
using Emby.Server.Implementations.Library.Resolvers.Audio;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Controller.Resolvers;
|
||||
using MediaBrowser.Controller.Sorting;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.Library.LibraryManager;
|
||||
|
||||
public class FindExtrasTests
|
||||
{
|
||||
private readonly Emby.Server.Implementations.Library.LibraryManager _libraryManager;
|
||||
private readonly Mock<IFileSystem> _fileSystemMock;
|
||||
|
||||
public FindExtrasTests()
|
||||
{
|
||||
var fixture = new Fixture().Customize(new AutoMoqCustomization());
|
||||
fixture.Register(() => new NamingOptions());
|
||||
var configMock = fixture.Freeze<Mock<IServerConfigurationManager>>();
|
||||
configMock.Setup(c => c.ApplicationPaths.ProgramDataPath).Returns("/data");
|
||||
var itemRepository = fixture.Freeze<Mock<IItemRepository>>();
|
||||
itemRepository.Setup(i => i.RetrieveItem(It.IsAny<Guid>())).Returns<BaseItem>(null);
|
||||
_fileSystemMock = fixture.Freeze<Mock<IFileSystem>>();
|
||||
_fileSystemMock.Setup(f => f.GetFileInfo(It.IsAny<string>())).Returns<string>(path => new FileSystemMetadata { FullName = path });
|
||||
_libraryManager = fixture.Build<Emby.Server.Implementations.Library.LibraryManager>().Do(s => s.AddParts(
|
||||
fixture.Create<IEnumerable<IResolverIgnoreRule>>(),
|
||||
new List<IItemResolver> { new AudioResolver(fixture.Create<NamingOptions>()) },
|
||||
fixture.Create<IEnumerable<IIntroProvider>>(),
|
||||
fixture.Create<IEnumerable<IBaseItemComparer>>(),
|
||||
fixture.Create<IEnumerable<ILibraryPostScanTask>>()))
|
||||
.Create();
|
||||
|
||||
// This is pretty terrible but unavoidable
|
||||
BaseItem.FileSystem ??= fixture.Create<IFileSystem>();
|
||||
BaseItem.MediaSourceManager ??= fixture.Create<IMediaSourceManager>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindExtras_SeparateMovieFolder_FindsCorrectExtras()
|
||||
{
|
||||
var owner = new Movie { Name = "Up", Path = "/movies/Up/Up.mkv" };
|
||||
var paths = new List<string>
|
||||
{
|
||||
"/movies/Up/Up.mkv",
|
||||
"/movies/Up/Up - trailer.mkv",
|
||||
"/movies/Up/Up - sample.mkv",
|
||||
"/movies/Up/Up something else.mkv"
|
||||
};
|
||||
|
||||
var files = paths.Select(p => new FileSystemMetadata
|
||||
{
|
||||
FullName = p,
|
||||
IsDirectory = false
|
||||
}).ToList();
|
||||
|
||||
var extras = _libraryManager.FindExtras(owner, files, new DirectoryService(_fileSystemMock.Object)).OrderBy(e => e.ExtraType).ToList();
|
||||
|
||||
Assert.Equal(2, extras.Count);
|
||||
Assert.Equal(ExtraType.Trailer, extras[0].ExtraType);
|
||||
Assert.Equal(typeof(Trailer), extras[0].GetType());
|
||||
Assert.Equal(ExtraType.Sample, extras[1].ExtraType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindExtras_SeparateMovieFolderWithMixedExtras_FindsCorrectExtras()
|
||||
{
|
||||
var owner = new Movie { Name = "Up", Path = "/movies/Up/Up.mkv" };
|
||||
var paths = new List<string>
|
||||
{
|
||||
"/movies/Up/Up.mkv",
|
||||
"/movies/Up/Up - trailer.mkv",
|
||||
"/movies/Up/trailers",
|
||||
"/movies/Up/theme-music",
|
||||
"/movies/Up/theme.mp3",
|
||||
"/movies/Up/not a theme.mp3",
|
||||
"/movies/Up/behind the scenes",
|
||||
"/movies/Up/behind the scenes.mkv",
|
||||
"/movies/Up/Up - sample.mkv",
|
||||
"/movies/Up/Up something else.mkv"
|
||||
};
|
||||
|
||||
_fileSystemMock.Setup(f => f.GetFiles(
|
||||
"/movies/Up/trailers",
|
||||
It.IsAny<string[]>(),
|
||||
false,
|
||||
false))
|
||||
.Returns(new List<FileSystemMetadata>
|
||||
{
|
||||
new()
|
||||
{
|
||||
FullName = "/movies/Up/trailers/some trailer.mkv",
|
||||
Name = "some trailer.mkv",
|
||||
IsDirectory = false
|
||||
}
|
||||
}).Verifiable();
|
||||
|
||||
_fileSystemMock.Setup(f => f.GetFiles(
|
||||
"/movies/Up/behind the scenes",
|
||||
It.IsAny<string[]>(),
|
||||
false,
|
||||
false))
|
||||
.Returns(new List<FileSystemMetadata>
|
||||
{
|
||||
new()
|
||||
{
|
||||
FullName = "/movies/Up/behind the scenes/the making of Up.mkv",
|
||||
Name = "the making of Up.mkv",
|
||||
IsDirectory = false
|
||||
}
|
||||
}).Verifiable();
|
||||
|
||||
_fileSystemMock.Setup(f => f.GetFiles(
|
||||
"/movies/Up/theme-music",
|
||||
It.IsAny<string[]>(),
|
||||
false,
|
||||
false))
|
||||
.Returns(new List<FileSystemMetadata>
|
||||
{
|
||||
new()
|
||||
{
|
||||
FullName = "/movies/Up/theme-music/theme2.mp3",
|
||||
Name = "theme2.mp3",
|
||||
IsDirectory = false
|
||||
}
|
||||
}).Verifiable();
|
||||
|
||||
var files = paths.Select(p => new FileSystemMetadata
|
||||
{
|
||||
FullName = p,
|
||||
Name = Path.GetFileName(p),
|
||||
IsDirectory = !Path.HasExtension(p)
|
||||
}).ToList();
|
||||
|
||||
var extras = _libraryManager.FindExtras(owner, files, new DirectoryService(_fileSystemMock.Object)).OrderBy(e => e.ExtraType).ToList();
|
||||
|
||||
_fileSystemMock.Verify();
|
||||
Assert.Equal(6, extras.Count);
|
||||
Assert.Equal(ExtraType.Trailer, extras[0].ExtraType);
|
||||
Assert.Equal(typeof(Trailer), extras[0].GetType());
|
||||
Assert.Equal(ExtraType.Trailer, extras[1].ExtraType);
|
||||
Assert.Equal(typeof(Trailer), extras[1].GetType());
|
||||
Assert.Equal(ExtraType.BehindTheScenes, extras[2].ExtraType);
|
||||
Assert.Equal(ExtraType.Sample, extras[3].ExtraType);
|
||||
Assert.Equal(ExtraType.ThemeSong, extras[4].ExtraType);
|
||||
Assert.Equal(typeof(Audio), extras[4].GetType());
|
||||
Assert.Equal(ExtraType.ThemeSong, extras[5].ExtraType);
|
||||
Assert.Equal(typeof(Audio), extras[5].GetType());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindExtras_SeparateMovieFolderWithMixedExtras_FindsOnlyExtrasInMovieFolder()
|
||||
{
|
||||
var owner = new Movie { Name = "Up", Path = "/movies/Up/Up.mkv" };
|
||||
var paths = new List<string>
|
||||
{
|
||||
"/movies/Up/Up.mkv",
|
||||
"/movies/Up/trailer.mkv",
|
||||
"/movies/Another Movie/trailer.mkv"
|
||||
};
|
||||
|
||||
var files = paths.Select(p => new FileSystemMetadata
|
||||
{
|
||||
FullName = p,
|
||||
IsDirectory = false
|
||||
}).ToList();
|
||||
|
||||
var extras = _libraryManager.FindExtras(owner, files, new DirectoryService(_fileSystemMock.Object)).OrderBy(e => e.ExtraType).ToList();
|
||||
|
||||
Assert.Single(extras);
|
||||
Assert.Equal(ExtraType.Trailer, extras[0].ExtraType);
|
||||
Assert.Equal(typeof(Trailer), extras[0].GetType());
|
||||
Assert.Equal("trailer", extras[0].FileNameWithoutExtension);
|
||||
Assert.Equal("/movies/Up/trailer.mkv", extras[0].Path);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindExtras_SeparateMovieFolderWithParts_FindsCorrectExtras()
|
||||
{
|
||||
var owner = new Movie { Name = "Up", Path = "/movies/Up/Up - part1.mkv" };
|
||||
var paths = new List<string>
|
||||
{
|
||||
"/movies/Up/Up - part1.mkv",
|
||||
"/movies/Up/Up - part2.mkv",
|
||||
"/movies/Up/trailer.mkv",
|
||||
"/movies/Another Movie/trailer.mkv"
|
||||
};
|
||||
|
||||
var files = paths.Select(p => new FileSystemMetadata
|
||||
{
|
||||
FullName = p,
|
||||
IsDirectory = false
|
||||
}).ToList();
|
||||
|
||||
var extras = _libraryManager.FindExtras(owner, files, new DirectoryService(_fileSystemMock.Object)).OrderBy(e => e.ExtraType).ToList();
|
||||
|
||||
Assert.Single(extras);
|
||||
Assert.Equal(ExtraType.Trailer, extras[0].ExtraType);
|
||||
Assert.Equal(typeof(Trailer), extras[0].GetType());
|
||||
Assert.Equal("trailer", extras[0].FileNameWithoutExtension);
|
||||
Assert.Equal("/movies/Up/trailer.mkv", extras[0].Path);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindExtras_WrongExtensions_FindsNoExtras()
|
||||
{
|
||||
var owner = new Movie { Name = "Up", Path = "/movies/Up/Up.mkv" };
|
||||
var paths = new List<string>
|
||||
{
|
||||
"/movies/Up/Up.mkv",
|
||||
"/movies/Up/trailer.noext",
|
||||
"/movies/Up/theme.png",
|
||||
"/movies/Up/trailers"
|
||||
};
|
||||
|
||||
var files = paths.Select(p => new FileSystemMetadata
|
||||
{
|
||||
FullName = p,
|
||||
Name = Path.GetFileName(p),
|
||||
IsDirectory = !Path.HasExtension(p)
|
||||
}).ToList();
|
||||
|
||||
_fileSystemMock.Setup(f => f.GetFiles(
|
||||
"/movies/Up/trailers",
|
||||
It.IsAny<string[]>(),
|
||||
false,
|
||||
false))
|
||||
.Returns(new List<FileSystemMetadata>
|
||||
{
|
||||
new()
|
||||
{
|
||||
FullName = "/movies/Up/trailers/trailer.jpg",
|
||||
Name = "trailer.jpg",
|
||||
IsDirectory = false
|
||||
}
|
||||
}).Verifiable();
|
||||
|
||||
var extras = _libraryManager.FindExtras(owner, files, new DirectoryService(_fileSystemMock.Object)).OrderBy(e => e.ExtraType).ToList();
|
||||
|
||||
_fileSystemMock.Verify();
|
||||
Assert.Empty(extras);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindExtras_SeriesWithTrailers_FindsCorrectExtras()
|
||||
{
|
||||
var owner = new Series { Name = "Dexter", Path = "/series/Dexter" };
|
||||
var paths = new List<string>
|
||||
{
|
||||
"/series/Dexter/Season 1/S01E01.mkv",
|
||||
"/series/Dexter/trailer.mkv",
|
||||
"/series/Dexter/trailers/trailer2.mkv",
|
||||
};
|
||||
|
||||
var files = paths.Select(p => new FileSystemMetadata
|
||||
{
|
||||
FullName = p,
|
||||
IsDirectory = string.IsNullOrEmpty(Path.GetExtension(p))
|
||||
}).ToList();
|
||||
|
||||
var extras = _libraryManager.FindExtras(owner, files, new DirectoryService(_fileSystemMock.Object)).OrderBy(e => e.ExtraType).ToList();
|
||||
|
||||
Assert.Equal(2, extras.Count);
|
||||
Assert.Equal(ExtraType.Trailer, extras[0].ExtraType);
|
||||
Assert.Equal(typeof(Trailer), extras[0].GetType());
|
||||
Assert.Equal("trailer", extras[0].FileNameWithoutExtension);
|
||||
Assert.Equal("/series/Dexter/trailer.mkv", extras[0].Path);
|
||||
Assert.Equal("/series/Dexter/trailers/trailer2.mkv", extras[1].Path);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using AutoFixture;
|
||||
using AutoFixture.AutoMoq;
|
||||
using Emby.Server.Implementations.IO;
|
||||
using Emby.Server.Implementations.Library;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.Library
|
||||
{
|
||||
public class MediaSourceManagerTests
|
||||
{
|
||||
private readonly MediaSourceManager _mediaSourceManager;
|
||||
|
||||
public MediaSourceManagerTests()
|
||||
{
|
||||
IFixture fixture = new Fixture().Customize(new AutoMoqCustomization { ConfigureMembers = true });
|
||||
fixture.Inject<IFileSystem>(fixture.Create<ManagedFileSystem>());
|
||||
_mediaSourceManager = fixture.Create<MediaSourceManager>();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"C:\mydir\myfile.ext", MediaProtocol.File)]
|
||||
[InlineData("/mydir/myfile.ext", MediaProtocol.File)]
|
||||
[InlineData("file:///mydir/myfile.ext", MediaProtocol.File)]
|
||||
[InlineData("http://example.com/stream.m3u8", MediaProtocol.Http)]
|
||||
[InlineData("https://example.com/stream.m3u8", MediaProtocol.Http)]
|
||||
[InlineData("rtsp://media.example.com:554/twister/audiotrack", MediaProtocol.Rtsp)]
|
||||
public void GetPathProtocol_ValidArg_Correct(string path, MediaProtocol expected)
|
||||
=> Assert.Equal(expected, _mediaSourceManager.GetPathProtocol(path));
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,27 @@ namespace Jellyfin.Server.Implementations.Tests.Library
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("Superman: Red Son [imdbid=tt10985510]", "imdbid", "tt10985510")]
|
||||
[InlineData("Superman: Red Son [imdbid-tt10985510]", "imdbid", "tt10985510")]
|
||||
[InlineData("Superman: Red Son - tt10985510", "imdbid", "tt10985510")]
|
||||
[InlineData("Superman: Red Son", "imdbid", null)]
|
||||
[InlineData("Superman: Red Son", "something", null)]
|
||||
[InlineData("Superman: Red Son [imdbid1=tt11111111][imdbid=tt10985510]", "imdbid", "tt10985510")]
|
||||
[InlineData("Superman: Red Son [imdbid1-tt11111111][imdbid=tt10985510]", "imdbid", "tt10985510")]
|
||||
[InlineData("Superman: Red Son [tmdbid=618355][imdbid=tt10985510]", "imdbid", "tt10985510")]
|
||||
[InlineData("Superman: Red Son [tmdbid-618355][imdbid-tt10985510]", "imdbid", "tt10985510")]
|
||||
[InlineData("Superman: Red Son [tmdbid-618355][imdbid-tt10985510]", "tmdbid", "618355")]
|
||||
[InlineData("[tmdbid=618355]", "tmdbid", "618355")]
|
||||
[InlineData("[tmdbid-618355]", "tmdbid", "618355")]
|
||||
[InlineData("tmdbid=111111][tmdbid=618355]", "tmdbid", "618355")]
|
||||
[InlineData("[tmdbid=618355]tmdbid=111111]", "tmdbid", "618355")]
|
||||
[InlineData("tmdbid=618355]", "tmdbid", null)]
|
||||
[InlineData("[tmdbid=618355", "tmdbid", null)]
|
||||
[InlineData("tmdbid=618355", "tmdbid", null)]
|
||||
[InlineData("tmdbid=", "tmdbid", null)]
|
||||
[InlineData("tmdbid", "tmdbid", null)]
|
||||
[InlineData("[tmdbid=][imdbid=tt10985510]", "tmdbid", null)]
|
||||
[InlineData("[tmdbid-][imdbid-tt10985510]", "tmdbid", null)]
|
||||
[InlineData("Superman: Red Son [tmdbid-618355][tmdbid=1234567]", "tmdbid", "618355")]
|
||||
public void GetAttributeValue_ValidArgs_Correct(string input, string attribute, string? expectedResult)
|
||||
{
|
||||
Assert.Equal(expectedResult, PathExtensions.GetAttributeValue(input, attribute));
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Emby.Server.Implementations.LiveTv.EmbyTV;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using Xunit;
|
||||
@@ -8,43 +7,36 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
|
||||
{
|
||||
public static class RecordingHelperTests
|
||||
{
|
||||
public static IEnumerable<object[]> GetRecordingName_Success_TestData()
|
||||
public static TheoryData<string, TimerInfo> GetRecordingName_Success_TestData()
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
var data = new TheoryData<string, TimerInfo>();
|
||||
|
||||
data.Add(
|
||||
"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[]
|
||||
{
|
||||
data.Add(
|
||||
"The Incredibles (2004)",
|
||||
new TimerInfo
|
||||
{
|
||||
Name = "The Incredibles",
|
||||
IsMovie = true,
|
||||
ProductionYear = 2004
|
||||
}
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
});
|
||||
data.Add(
|
||||
"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[]
|
||||
{
|
||||
});
|
||||
data.Add(
|
||||
"The Big Bang Theory S12E10",
|
||||
new TimerInfo
|
||||
{
|
||||
@@ -52,11 +44,8 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
|
||||
IsProgramSeries = true,
|
||||
SeasonNumber = 12,
|
||||
EpisodeNumber = 10
|
||||
}
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
});
|
||||
data.Add(
|
||||
"The Big Bang Theory S12E10 The VCR Illumination",
|
||||
new TimerInfo
|
||||
{
|
||||
@@ -65,34 +54,27 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
|
||||
SeasonNumber = 12,
|
||||
EpisodeNumber = 10,
|
||||
EpisodeTitle = "The VCR Illumination"
|
||||
}
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
});
|
||||
data.Add(
|
||||
"The Big Bang Theory 2018-12-06",
|
||||
new TimerInfo
|
||||
{
|
||||
Name = "The Big Bang Theory",
|
||||
IsProgramSeries = true,
|
||||
OriginalAirDate = new DateTime(2018, 12, 6)
|
||||
}
|
||||
};
|
||||
OriginalAirDate = new DateTime(2018, 12, 6, 0, 0, 0, DateTimeKind.Local)
|
||||
});
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
data.Add(
|
||||
"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),
|
||||
OriginalAirDate = new DateTime(2018, 12, 6, 0, 0, 0, DateTimeKind.Local),
|
||||
EpisodeTitle = "The VCR Illumination"
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
data.Add(
|
||||
"The Big Bang Theory 2018_12_06_21_06_00 - The VCR Illumination",
|
||||
new TimerInfo
|
||||
{
|
||||
@@ -101,8 +83,9 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
|
||||
IsProgramSeries = true,
|
||||
OriginalAirDate = new DateTime(2018, 12, 6),
|
||||
EpisodeTitle = "The VCR Illumination"
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.LiveTv.SchedulesDirect
|
||||
{
|
||||
public class SchedulesDirectDeserializeTests
|
||||
{
|
||||
private readonly JsonSerializerOptions _jsonOptions;
|
||||
|
||||
public SchedulesDirectDeserializeTests()
|
||||
{
|
||||
_jsonOptions = JsonDefaults.Options;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// /token reponse.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Deserialize_Token_Response_Live_Success()
|
||||
{
|
||||
var bytes = File.ReadAllBytes("Test Data/SchedulesDirect/token_live_response.json");
|
||||
var tokenDto = JsonSerializer.Deserialize<TokenDto>(bytes, _jsonOptions);
|
||||
|
||||
Assert.NotNull(tokenDto);
|
||||
Assert.Equal(0, tokenDto!.Code);
|
||||
Assert.Equal("OK", tokenDto.Message);
|
||||
Assert.Equal("AWS-SD-web.1", tokenDto.ServerId);
|
||||
Assert.Equal(new DateTime(2016, 08, 23, 13, 55, 25, DateTimeKind.Utc), tokenDto.TokenTimestamp);
|
||||
Assert.Equal("f3fca79989cafe7dead71beefedc812b", tokenDto.Token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// /token response.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Deserialize_Token_Response_Offline_Success()
|
||||
{
|
||||
var bytes = File.ReadAllBytes("Test Data/SchedulesDirect/token_offline_response.json");
|
||||
var tokenDto = JsonSerializer.Deserialize<TokenDto>(bytes, _jsonOptions);
|
||||
|
||||
Assert.NotNull(tokenDto);
|
||||
Assert.Equal(3_000, tokenDto!.Code);
|
||||
Assert.Equal("Server offline for maintenance.", tokenDto.Message);
|
||||
Assert.Equal("20141201.web.1", tokenDto.ServerId);
|
||||
Assert.Equal(new DateTime(2015, 04, 23, 00, 03, 32, DateTimeKind.Utc), tokenDto.TokenTimestamp);
|
||||
Assert.Equal("CAFEDEADBEEFCAFEDEADBEEFCAFEDEADBEEFCAFE", tokenDto.Token);
|
||||
Assert.Equal("SERVICE_OFFLINE", tokenDto.Response);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// /schedules request.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Serialize_Schedule_Request_Success()
|
||||
{
|
||||
var expectedString = File.ReadAllText("Test Data/SchedulesDirect/schedules_request.json").Trim();
|
||||
|
||||
var requestObject = new RequestScheduleForChannelDto[]
|
||||
{
|
||||
new RequestScheduleForChannelDto
|
||||
{
|
||||
StationId = "20454",
|
||||
Date = new[]
|
||||
{
|
||||
"2015-03-13",
|
||||
"2015-03-17"
|
||||
}
|
||||
},
|
||||
new RequestScheduleForChannelDto
|
||||
{
|
||||
StationId = "10021",
|
||||
Date = new[]
|
||||
{
|
||||
"2015-03-12",
|
||||
"2015-03-13"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var requestString = JsonSerializer.Serialize(requestObject, _jsonOptions);
|
||||
Assert.Equal(expectedString, requestString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// /schedules response.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Deserialize_Schedule_Response_Success()
|
||||
{
|
||||
var bytes = File.ReadAllBytes("Test Data/SchedulesDirect/schedules_response.json");
|
||||
var days = JsonSerializer.Deserialize<IReadOnlyList<DayDto>>(bytes, _jsonOptions);
|
||||
|
||||
Assert.NotNull(days);
|
||||
Assert.Equal(1, days!.Count);
|
||||
|
||||
var dayDto = days[0];
|
||||
Assert.Equal("20454", dayDto.StationId);
|
||||
Assert.Equal(2, dayDto.Programs.Count);
|
||||
|
||||
Assert.Equal("SH005371070000", dayDto.Programs[0].ProgramId);
|
||||
Assert.Equal(new DateTime(2015, 03, 03, 00, 00, 00, DateTimeKind.Utc), dayDto.Programs[0].AirDateTime);
|
||||
Assert.Equal(1_800, dayDto.Programs[0].Duration);
|
||||
Assert.Equal("Sy8HEMBPcuiAx3FBukUhKQ", dayDto.Programs[0].Md5);
|
||||
Assert.True(dayDto.Programs[0].New);
|
||||
Assert.Equal(2, dayDto.Programs[0].AudioProperties.Count);
|
||||
Assert.Equal("stereo", dayDto.Programs[0].AudioProperties[0]);
|
||||
Assert.Equal("cc", dayDto.Programs[0].AudioProperties[1]);
|
||||
Assert.Equal(1, dayDto.Programs[0].VideoProperties.Count);
|
||||
Assert.Equal("hdtv", dayDto.Programs[0].VideoProperties[0]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// /programs response.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Deserialize_Program_Response_Success()
|
||||
{
|
||||
var bytes = File.ReadAllBytes("Test Data/SchedulesDirect/programs_response.json");
|
||||
var programDtos = JsonSerializer.Deserialize<IReadOnlyList<ProgramDetailsDto>>(bytes, _jsonOptions);
|
||||
|
||||
Assert.NotNull(programDtos);
|
||||
Assert.Equal(2, programDtos!.Count);
|
||||
Assert.Equal("EP000000060003", programDtos[0].ProgramId);
|
||||
Assert.Equal(1, programDtos[0].Titles.Count);
|
||||
Assert.Equal("'Allo 'Allo!", programDtos[0].Titles[0].Title120);
|
||||
Assert.Equal("Series", programDtos[0].EventDetails?.SubType);
|
||||
Assert.Equal("en", programDtos[0].Descriptions?.Description1000[0].DescriptionLanguage);
|
||||
Assert.Equal("A disguised British Intelligence officer is sent to help the airmen.", programDtos[0].Descriptions?.Description1000[0].Description);
|
||||
Assert.Equal(new DateTime(1985, 11, 04), programDtos[0].OriginalAirDate);
|
||||
Assert.Equal(1, programDtos[0].Genres.Count);
|
||||
Assert.Equal("Sitcom", programDtos[0].Genres[0]);
|
||||
Assert.Equal("The Poloceman Cometh", programDtos[0].EpisodeTitle150);
|
||||
Assert.Equal(2, programDtos[0].Metadata[0].Gracenote?.Season);
|
||||
Assert.Equal(3, programDtos[0].Metadata[0].Gracenote?.Episode);
|
||||
Assert.Equal(13, programDtos[0].Cast.Count);
|
||||
Assert.Equal("383774", programDtos[0].Cast[0].PersonId);
|
||||
Assert.Equal("392649", programDtos[0].Cast[0].NameId);
|
||||
Assert.Equal("Gorden Kaye", programDtos[0].Cast[0].Name);
|
||||
Assert.Equal("Actor", programDtos[0].Cast[0].Role);
|
||||
Assert.Equal("01", programDtos[0].Cast[0].BillingOrder);
|
||||
Assert.Equal(3, programDtos[0].Crew.Count);
|
||||
Assert.Equal("354407", programDtos[0].Crew[0].PersonId);
|
||||
Assert.Equal("363281", programDtos[0].Crew[0].NameId);
|
||||
Assert.Equal("David Croft", programDtos[0].Crew[0].Name);
|
||||
Assert.Equal("Director", programDtos[0].Crew[0].Role);
|
||||
Assert.Equal("01", programDtos[0].Crew[0].BillingOrder);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// /metadata/programs response.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Deserialize_Metadata_Programs_Response_Success()
|
||||
{
|
||||
var bytes = File.ReadAllBytes("Test Data/SchedulesDirect/metadata_programs_response.json");
|
||||
var showImagesDtos = JsonSerializer.Deserialize<IReadOnlyList<ShowImagesDto>>(bytes, _jsonOptions);
|
||||
|
||||
Assert.NotNull(showImagesDtos);
|
||||
Assert.Equal(1, showImagesDtos!.Count);
|
||||
Assert.Equal("SH00712240", showImagesDtos[0].ProgramId);
|
||||
Assert.Equal(4, showImagesDtos[0].Data.Count);
|
||||
Assert.Equal("135", showImagesDtos[0].Data[0].Width);
|
||||
Assert.Equal("180", showImagesDtos[0].Data[0].Height);
|
||||
Assert.Equal("assets/p282288_b_v2_aa.jpg", showImagesDtos[0].Data[0].Uri);
|
||||
Assert.Equal("Sm", showImagesDtos[0].Data[0].Size);
|
||||
Assert.Equal("3x4", showImagesDtos[0].Data[0].Aspect);
|
||||
Assert.Equal("Banner-L3", showImagesDtos[0].Data[0].Category);
|
||||
Assert.Equal("yes", showImagesDtos[0].Data[0].Text);
|
||||
Assert.Equal("true", showImagesDtos[0].Data[0].Primary);
|
||||
Assert.Equal("Series", showImagesDtos[0].Data[0].Tier);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// /headends response.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Deserialize_Headends_Response_Success()
|
||||
{
|
||||
var bytes = File.ReadAllBytes("Test Data/SchedulesDirect/headends_response.json");
|
||||
var headendsDtos = JsonSerializer.Deserialize<IReadOnlyList<HeadendsDto>>(bytes, _jsonOptions);
|
||||
|
||||
Assert.NotNull(headendsDtos);
|
||||
Assert.Equal(8, headendsDtos!.Count);
|
||||
Assert.Equal("CA00053", headendsDtos[0].Headend);
|
||||
Assert.Equal("Cable", headendsDtos[0].Transport);
|
||||
Assert.Equal("Beverly Hills", headendsDtos[0].Location);
|
||||
Assert.Equal(2, headendsDtos[0].Lineups.Count);
|
||||
Assert.Equal("Time Warner Cable - Cable", headendsDtos[0].Lineups[0].Name);
|
||||
Assert.Equal("USA-CA00053-DEFAULT", headendsDtos[0].Lineups[0].Lineup);
|
||||
Assert.Equal("/20141201/lineups/USA-CA00053-DEFAULT", headendsDtos[0].Lineups[0].Uri);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// /lineups response.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Deserialize_Lineups_Response_Success()
|
||||
{
|
||||
var bytes = File.ReadAllBytes("Test Data/SchedulesDirect/lineups_response.json");
|
||||
var lineupsDto = JsonSerializer.Deserialize<LineupsDto>(bytes, _jsonOptions);
|
||||
|
||||
Assert.NotNull(lineupsDto);
|
||||
Assert.Equal(0, lineupsDto!.Code);
|
||||
Assert.Equal("20141201.web.1", lineupsDto.ServerId);
|
||||
Assert.Equal(new DateTime(2015, 04, 17, 14, 22, 17, DateTimeKind.Utc), lineupsDto.LineupTimestamp);
|
||||
Assert.Equal(5, lineupsDto.Lineups.Count);
|
||||
Assert.Equal("GBR-0001317-DEFAULT", lineupsDto.Lineups[0].Lineup);
|
||||
Assert.Equal("Freeview - Carlton - LWT (Southeast)", lineupsDto.Lineups[0].Name);
|
||||
Assert.Equal("DVB-T", lineupsDto.Lineups[0].Transport);
|
||||
Assert.Equal("London", lineupsDto.Lineups[0].Location);
|
||||
Assert.Equal("/20141201/lineups/GBR-0001317-DEFAULT", lineupsDto.Lineups[0].Uri);
|
||||
|
||||
Assert.Equal("DELETED LINEUP", lineupsDto.Lineups[4].Name);
|
||||
Assert.True(lineupsDto.Lineups[4].IsDeleted);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// /lineup/:id response.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Deserialize_Lineup_Response_Success()
|
||||
{
|
||||
var bytes = File.ReadAllBytes("Test Data/SchedulesDirect/lineup_response.json");
|
||||
var channelDto = JsonSerializer.Deserialize<ChannelDto>(bytes, _jsonOptions);
|
||||
|
||||
Assert.NotNull(channelDto);
|
||||
Assert.Equal(2, channelDto!.Map.Count);
|
||||
Assert.Equal("24326", channelDto.Map[0].StationId);
|
||||
Assert.Equal("001", channelDto.Map[0].Channel);
|
||||
Assert.Equal("BBC ONE South", channelDto.Map[0].ProvderCallsign);
|
||||
Assert.Equal("1", channelDto.Map[0].LogicalChannelNumber);
|
||||
Assert.Equal("providerCallsign", channelDto.Map[0].MatchType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization
|
||||
await localizationManager.LoadAll();
|
||||
var cultures = localizationManager.GetCultures().ToList();
|
||||
|
||||
Assert.Equal(189, cultures.Count);
|
||||
Assert.Equal(190, cultures.Count);
|
||||
|
||||
var germany = cultures.FirstOrDefault(x => x.TwoLetterISOLanguageName.Equals("de", StringComparison.Ordinal));
|
||||
Assert.NotNull(germany);
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Threading.Tasks;
|
||||
using AutoFixture;
|
||||
using AutoFixture.AutoMoq;
|
||||
using Emby.Server.Implementations.QuickConnect;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller.Authentication;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Net;
|
||||
@@ -51,6 +52,21 @@ namespace Jellyfin.Server.Implementations.Tests.QuickConnect
|
||||
public void IsEnabled_QuickConnectUnavailable_False()
|
||||
=> Assert.False(_quickConnectManager.IsEnabled);
|
||||
|
||||
[Theory]
|
||||
[InlineData("", "DeviceId", "Client", "1.0.0")]
|
||||
[InlineData("Device", "", "Client", "1.0.0")]
|
||||
[InlineData("Device", "DeviceId", "", "1.0.0")]
|
||||
[InlineData("Device", "DeviceId", "Client", "")]
|
||||
public void TryConnect_InvalidAuthorizationInfo_ThrowsArgumentException(string device, string deviceId, string client, string version)
|
||||
=> Assert.Throws<ArgumentException>(() => _quickConnectManager.TryConnect(
|
||||
new AuthorizationInfo
|
||||
{
|
||||
Device = device,
|
||||
DeviceId = deviceId,
|
||||
Client = client,
|
||||
Version = version
|
||||
}));
|
||||
|
||||
[Fact]
|
||||
public void TryConnect_QuickConnectUnavailable_ThrowsAuthenticationException()
|
||||
=> Assert.Throws<AuthenticationException>(() => _quickConnectManager.TryConnect(_quickConnectAuthInfo));
|
||||
@@ -63,6 +79,10 @@ namespace Jellyfin.Server.Implementations.Tests.QuickConnect
|
||||
public void AuthorizeRequest_QuickConnectUnavailable_ThrowsAuthenticationException()
|
||||
=> Assert.ThrowsAsync<AuthenticationException>(() => _quickConnectManager.AuthorizeRequest(Guid.Empty, string.Empty));
|
||||
|
||||
[Fact]
|
||||
public void GetAuthorizedRequest_QuickConnectUnavailable_ThrowsAuthenticationException()
|
||||
=> Assert.Throws<AuthenticationException>(() => _quickConnectManager.GetAuthorizedRequest(string.Empty));
|
||||
|
||||
[Fact]
|
||||
public void IsEnabled_QuickConnectAvailable_True()
|
||||
{
|
||||
@@ -79,6 +99,20 @@ namespace Jellyfin.Server.Implementations.Tests.QuickConnect
|
||||
Assert.Equal(res1, res2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckRequestStatus_UnknownSecret_ThrowsResourceNotFoundException()
|
||||
{
|
||||
_config.QuickConnectAvailable = true;
|
||||
Assert.Throws<ResourceNotFoundException>(() => _quickConnectManager.CheckRequestStatus("Unknown secret"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAuthorizedRequest_UnknownSecret_ThrowsResourceNotFoundException()
|
||||
{
|
||||
_config.QuickConnectAvailable = true;
|
||||
Assert.Throws<ResourceNotFoundException>(() => _quickConnectManager.GetAuthorizedRequest("Unknown secret"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AuthorizeRequest_QuickConnectAvailable_Success()
|
||||
{
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Emby.Server.Implementations.Sorting;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
@@ -13,7 +11,7 @@ namespace Jellyfin.Server.Implementations.Tests.Sorting
|
||||
{
|
||||
[Theory]
|
||||
[ClassData(typeof(EpisodeBadData))]
|
||||
public void Compare_GivenNull_ThrowsArgumentNullException(BaseItem x, BaseItem y)
|
||||
public void Compare_GivenNull_ThrowsArgumentNullException(BaseItem? x, BaseItem? y)
|
||||
{
|
||||
var cmp = new AiredEpisodeOrderComparer();
|
||||
Assert.Throws<ArgumentNullException>(() => cmp.Compare(x, y));
|
||||
@@ -29,171 +27,138 @@ namespace Jellyfin.Server.Implementations.Tests.Sorting
|
||||
Assert.Equal(-expected, cmp.Compare(y, x));
|
||||
}
|
||||
|
||||
private class EpisodeBadData : IEnumerable<object?[]>
|
||||
private class EpisodeBadData : TheoryData<BaseItem?, BaseItem?>
|
||||
{
|
||||
public IEnumerator<object?[]> GetEnumerator()
|
||||
public EpisodeBadData()
|
||||
{
|
||||
yield return new object?[] { null, new Episode() };
|
||||
yield return new object?[] { new Episode(), null };
|
||||
Add(null, new Episode());
|
||||
Add(new Episode(), null);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
|
||||
private class EpisodeTestData : IEnumerable<object?[]>
|
||||
private class EpisodeTestData : TheoryData<BaseItem, BaseItem, int>
|
||||
{
|
||||
public IEnumerator<object?[]> GetEnumerator()
|
||||
public EpisodeTestData()
|
||||
{
|
||||
yield return new object?[]
|
||||
{
|
||||
Add(
|
||||
new Movie(),
|
||||
new Movie(),
|
||||
0
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
0);
|
||||
|
||||
Add(
|
||||
new Movie(),
|
||||
new Episode(),
|
||||
1
|
||||
};
|
||||
1);
|
||||
|
||||
// Good cases
|
||||
yield return new object?[]
|
||||
{
|
||||
Add(
|
||||
new Episode(),
|
||||
new Episode(),
|
||||
0
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
0);
|
||||
|
||||
Add(
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
|
||||
0
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
0);
|
||||
|
||||
Add(
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 2 },
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
|
||||
1
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
1);
|
||||
|
||||
Add(
|
||||
new Episode { ParentIndexNumber = 2, IndexNumber = 1 },
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
|
||||
1
|
||||
};
|
||||
1);
|
||||
|
||||
// Good Specials
|
||||
yield return new object?[]
|
||||
{
|
||||
Add(
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1 },
|
||||
0
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
0);
|
||||
|
||||
Add(
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 2 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1 },
|
||||
1
|
||||
};
|
||||
1);
|
||||
|
||||
// Specials to Episodes
|
||||
yield return new object?[]
|
||||
{
|
||||
Add(
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1 },
|
||||
1
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
1);
|
||||
|
||||
Add(
|
||||
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
|
||||
};
|
||||
1);
|
||||
|
||||
yield return new object?[]
|
||||
{
|
||||
Add(
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 2 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1 },
|
||||
1
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
1);
|
||||
|
||||
Add(
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 2 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1 },
|
||||
1);
|
||||
|
||||
Add(
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 2 },
|
||||
1
|
||||
};
|
||||
1);
|
||||
|
||||
yield return new object?[]
|
||||
{
|
||||
Add(
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1 },
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
|
||||
1
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
1);
|
||||
|
||||
Add(
|
||||
new Episode { ParentIndexNumber = 3, IndexNumber = 1 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1 },
|
||||
1
|
||||
};
|
||||
1);
|
||||
|
||||
yield return new object?[]
|
||||
{
|
||||
Add(
|
||||
new Episode { ParentIndexNumber = 3, IndexNumber = 1 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 },
|
||||
1
|
||||
};
|
||||
1);
|
||||
|
||||
yield return new object?[]
|
||||
{
|
||||
Add(
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1 },
|
||||
1
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
1);
|
||||
|
||||
Add(
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 2 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 },
|
||||
1
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
1);
|
||||
|
||||
Add(
|
||||
new Episode { ParentIndexNumber = 1 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 },
|
||||
0
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
0);
|
||||
|
||||
Add(
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 3 },
|
||||
new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 },
|
||||
1
|
||||
};
|
||||
// Premiere Date
|
||||
yield return new object?[]
|
||||
{
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1, PremiereDate = new DateTime(2021, 09, 12, 0, 0, 0) },
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1, PremiereDate = new DateTime(2021, 09, 12, 0, 0, 0) },
|
||||
0
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1, PremiereDate = new DateTime(2021, 09, 11, 0, 0, 0) },
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1, PremiereDate = new DateTime(2021, 09, 12, 0, 0, 0) },
|
||||
-1
|
||||
};
|
||||
yield return new object?[]
|
||||
{
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1, PremiereDate = new DateTime(2021, 09, 12, 0, 0, 0) },
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1, PremiereDate = new DateTime(2021, 09, 11, 0, 0, 0) },
|
||||
1
|
||||
};
|
||||
}
|
||||
1);
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
// Premiere Date
|
||||
Add(
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1, PremiereDate = new DateTime(2021, 09, 12, 0, 0, 0) },
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1, PremiereDate = new DateTime(2021, 09, 12, 0, 0, 0) },
|
||||
0);
|
||||
|
||||
Add(
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1, PremiereDate = new DateTime(2021, 09, 11, 0, 0, 0) },
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1, PremiereDate = new DateTime(2021, 09, 12, 0, 0, 0) },
|
||||
-1);
|
||||
|
||||
Add(
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1, PremiereDate = new DateTime(2021, 09, 12, 0, 0, 0) },
|
||||
new Episode { ParentIndexNumber = 1, IndexNumber = 1, PremiereDate = new DateTime(2021, 09, 11, 0, 0, 0) },
|
||||
1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
[{"headend":"CA00053","transport":"Cable","location":"Beverly Hills","lineups":[{"name":"Time Warner Cable - Cable","lineup":"USA-CA00053-DEFAULT","uri":"/20141201/lineups/USA-CA00053-DEFAULT"},{"name":"Time Warner Cable - Digital","lineup":"USA-CA00053-X","uri":"/20141201/lineups/USA-CA00053-X"}]},{"headend":"CA61222","transport":"Cable","location":"Beverly Hills","lineups":[{"name":"Mulholland Estates - Cable","lineup":"USA-CA61222-DEFAULT","uri":"/20141201/lineups/USA-CA61222-DEFAULT"}]},{"headend":"CA66511","transport":"Cable","location":"Los Angeles","lineups":[{"name":"AT&T U-verse TV - Digital","lineup":"USA-CA66511-X","uri":"/20141201/lineups/USA-CA66511-X"}]},{"headend":"CA67309","transport":"Cable","location":"Westchester","lineups":[{"name":"Time Warner Cable Sherman Oaks - Cable","lineup":"USA-CA67309-DEFAULT","uri":"/20141201/lineups/USA-CA67309-DEFAULT"},{"name":"Time Warner Cable Sherman Oaks - Digital","lineup":"USA-CA67309-X","uri":"/20141201/lineups/USA-CA67309-X"}]},{"headend":"CA67310","transport":"Cable","location":"Eagle Rock","lineups":[{"name":"Time Warner Cable City of Los Angeles - Cable","lineup":"USA-CA67310-DEFAULT","uri":"/20141201/lineups/USA-CA67310-DEFAULT"},{"name":"Time Warner Cable City of Los Angeles - Digital","lineup":"USA-CA67310-X","uri":"/20141201/lineups/USA-CA67310-X"}]},{"headend":"DISH803","transport":"Satellite","location":"Los Angeles","lineups":[{"name":"DISH Los Angeles - Satellite","lineup":"USA-DISH803-DEFAULT","uri":"/20141201/lineups/USA-DISH803-DEFAULT"}]},{"headend":"DITV803","transport":"Satellite","location":"Los Angeles","lineups":[{"name":"DIRECTV Los Angeles - Satellite","lineup":"USA-DITV803-DEFAULT","uri":"/20141201/lineups/USA-DITV803-DEFAULT"}]},{"headend":"90210","transport":"Antenna","location":"90210","lineups":[{"name":"Antenna","lineup":"USA-OTA-90210","uri":"/20141201/lineups/USA-OTA-90210"}]}]
|
||||
@@ -0,0 +1 @@
|
||||
{"map":[{"stationID":"24326","channel":"001","providerCallsign":"BBC ONE South","logicalChannelNumber":"1","matchType":"providerCallsign"},{"stationID":"17154","channel":"002","providerCallsign":"BBC TWO","logicalChannelNumber":"2","matchType":"providerCallsign"}]}
|
||||
@@ -0,0 +1 @@
|
||||
{"code":0,"serverID":"20141201.web.1","datetime":"2015-04-17T14:22:17Z","lineups":[{"lineup":"GBR-0001317-DEFAULT","name":"Freeview - Carlton - LWT (Southeast)","transport":"DVB-T","location":"London","uri":"/20141201/lineups/GBR-0001317-DEFAULT"},{"lineup":"USA-IL57303-X","name":"Comcast Waukegan/Lake Forest Area - Digital","transport":"Cable","location":"Lake Forest","uri":"/20141201/lineups/USA-IL57303-X"},{"lineup":"USA-NY67791-X","name":"Verizon Fios Queens - Digital","transport":"Cable","location":"Fresh Meadows","uri":"/20141201/lineups/USA-NY67791-X"},{"lineup":"USA-OTA-60030","name":"Local Over the Air Broadcast","transport":"Antenna","location":"60030","uri":"/20141201/lineups/USA-OTA-60030"},{"lineup":"USA-WI61859-DEFAULT","name":"DELETED LINEUP","isDeleted":true}]}
|
||||
@@ -0,0 +1 @@
|
||||
[{"programID":"SH00712240","data":[{"width":"135","height":"180","uri":"assets/p282288_b_v2_aa.jpg","size":"Sm","aspect":"3x4","category":"Banner-L3","text":"yes","primary":"true","tier":"Series"},{"width":"720","height":"540","uri":"assets/p282288_b_h6_aa.jpg","size":"Lg","aspect":"4x3","category":"Banner-L3","text":"yes","primary":"true","tier":"Series"},{"width":"960","height":"1440","uri":"assets/p282288_b_v8_aa.jpg","size":"Ms","aspect":"2x3","category":"Banner-L3","text":"yes","primary":"true","tier":"Series"},{"width":"180","height":"135","uri":"assets/p282288_b_h5_aa.jpg","size":"Sm","aspect":"4x3","category":"Banner-L3","text":"yes","primary":"true","tier":"Series"}]}]
|
||||
@@ -0,0 +1 @@
|
||||
[{"programID":"EP000000060003","titles":[{"title120":"'Allo 'Allo!"}],"eventDetails":{"subType":"Series"},"descriptions":{"description1000":[{"descriptionLanguage":"en","description":"A disguised British Intelligence officer is sent to help the airmen."}]},"originalAirDate":"1985-11-04","genres":["Sitcom"],"episodeTitle150":"The Poloceman Cometh","metadata":[{"Gracenote":{"season":2,"episode":3}}],"cast":[{"personId":"383774","nameId":"392649","name":"Gorden Kaye","role":"Actor","billingOrder":"01"},{"personId":"246840","nameId":"250387","name":"Carmen Silvera","role":"Actor","billingOrder":"02"},{"personId":"376955","nameId":"385830","name":"Rose Hill","role":"Actor","billingOrder":"03"},{"personId":"259773","nameId":"263340","name":"Vicki Michelle","role":"Actor","billingOrder":"04"},{"personId":"353113","nameId":"361987","name":"Kirsten Cooke","role":"Actor","billingOrder":"05"},{"personId":"77787","nameId":"77787","name":"Richard Marner","role":"Actor","billingOrder":"06"},{"personId":"230921","nameId":"234193","name":"Guy Siner","role":"Actor","billingOrder":"07"},{"personId":"374934","nameId":"383809","name":"Kim Hartman","role":"Actor","billingOrder":"08"},{"personId":"369151","nameId":"378026","name":"Richard Gibson","role":"Actor","billingOrder":"09"},{"personId":"343690","nameId":"352564","name":"Arthur Bostrom","role":"Actor","billingOrder":"10"},{"personId":"352557","nameId":"361431","name":"John D. Collins","role":"Actor","billingOrder":"11"},{"personId":"605275","nameId":"627734","name":"Nicholas Frankau","role":"Actor","billingOrder":"12"},{"personId":"373394","nameId":"382269","name":"Jack Haig","role":"Actor","billingOrder":"13"}],"crew":[{"personId":"354407","nameId":"363281","name":"David Croft","role":"Director","billingOrder":"01"},{"personId":"354407","nameId":"363281","name":"David Croft","role":"Writer","billingOrder":"02"},{"personId":"105145","nameId":"105145","name":"Jeremy Lloyd","role":"Writer","billingOrder":"03"}],"showType":"Series","hasImageArtwork":true,"md5":"Jo5NKxoo44xRvBCAq8QT2A"},{"programID":"EP000000510142","titles":[{"title120":"A Different World"}],"eventDetails":{"subType":"Series"},"descriptions":{"description1000":[{"descriptionLanguage":"en","description":"Whitley and Dwayne tell new students about their honeymoon in Los Angeles."}]},"originalAirDate":"1992-09-24","genres":["Sitcom"],"episodeTitle150":"Honeymoon in L.A.","metadata":[{"Gracenote":{"season":6,"episode":1}}],"cast":[{"personId":"700","nameId":"700","name":"Jasmine Guy","role":"Actor","billingOrder":"01"},{"personId":"729","nameId":"729","name":"Kadeem Hardison","role":"Actor","billingOrder":"02"},{"personId":"120","nameId":"120","name":"Darryl M. Bell","role":"Actor","billingOrder":"03"},{"personId":"1729","nameId":"1729","name":"Cree Summer","role":"Actor","billingOrder":"04"},{"personId":"217","nameId":"217","name":"Charnele Brown","role":"Actor","billingOrder":"05"},{"personId":"1811","nameId":"1811","name":"Glynn Turman","role":"Actor","billingOrder":"06"},{"personId":"1232","nameId":"1232","name":"Lou Myers","role":"Actor","billingOrder":"07"},{"personId":"1363","nameId":"1363","name":"Jada Pinkett","role":"Guest Star","billingOrder":"08"},{"personId":"222967","nameId":"225536","name":"Ajai Sanders","role":"Guest Star","billingOrder":"09"},{"personId":"181744","nameId":"183292","name":"Karen Malina White","role":"Guest Star","billingOrder":"10"},{"personId":"305017","nameId":"318897","name":"Patrick Y. Malone","role":"Guest Star","billingOrder":"11"},{"personId":"9841","nameId":"9841","name":"Bumper Robinson","role":"Guest Star","billingOrder":"12"},{"personId":"426422","nameId":"435297","name":"Sister Souljah","role":"Guest Star","billingOrder":"13"},{"personId":"25","nameId":"25","name":"Debbie Allen","role":"Guest Star","billingOrder":"14"},{"personId":"668","nameId":"668","name":"Gilbert Gottfried","role":"Guest Star","billingOrder":"15"}],"showType":"Series","hasImageArtwork":true,"md5":"P5kz0QmCeYxIA+yL0H4DWw"}]
|
||||
@@ -0,0 +1 @@
|
||||
[{"stationID":"20454","date":["2015-03-13","2015-03-17"]},{"stationID":"10021","date":["2015-03-12","2015-03-13"]}]
|
||||
@@ -0,0 +1 @@
|
||||
[{"stationID":"20454","programs":[{"programID":"SH005371070000","airDateTime":"2015-03-03T00:00:00Z","duration":1800,"md5":"Sy8HEMBPcuiAx3FBukUhKQ","new":true,"audioProperties":["stereo","cc"],"videoProperties":["hdtv"]},{"programID":"EP000014577244","airDateTime":"2015-03-03T00:30:00Z","duration":1800,"md5":"25DNXVXO192JI7Y9vSW9lQ","new":true,"audioProperties":["stereo","cc"],"videoProperties":["hdtv"]}]}]
|
||||
@@ -0,0 +1 @@
|
||||
{"code":0,"message":"OK","serverID":"AWS-SD-web.1","datetime":"2016-08-23T13:55:25Z","token":"f3fca79989cafe7dead71beefedc812b"}
|
||||
@@ -0,0 +1 @@
|
||||
{"response":"SERVICE_OFFLINE","code":3000,"serverID":"20141201.web.1","message":"Server offline for maintenance.","datetime":"2015-04-23T00:03:32Z","token":"CAFEDEADBEEFCAFEDEADBEEFCAFEDEADBEEFCAFE"}
|
||||
Binary file not shown.
@@ -40,7 +40,8 @@ namespace Jellyfin.Server.Implementations.Tests.Updates
|
||||
_fixture.Customize(new AutoMoqCustomization
|
||||
{
|
||||
ConfigureMembers = true
|
||||
}).Inject(http);
|
||||
});
|
||||
_fixture.Inject(http);
|
||||
_installationManager = _fixture.Create<InstallationManager>();
|
||||
}
|
||||
|
||||
@@ -78,5 +79,32 @@ namespace Jellyfin.Server.Implementations.Tests.Updates
|
||||
packages = _installationManager.FilterPackages(packages, id: new Guid("a4df60c5-6ab4-412a-8f79-2cab93fb2bc5")).ToArray();
|
||||
Assert.Single(packages);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InstallPackage_InvalidChecksum_ThrowsInvalidDataException()
|
||||
{
|
||||
var packageInfo = new InstallationInfo()
|
||||
{
|
||||
Name = "Test",
|
||||
SourceUrl = "https://repo.jellyfin.org/releases/plugin/empty/empty.zip",
|
||||
Checksum = "InvalidChecksum"
|
||||
};
|
||||
|
||||
await Assert.ThrowsAsync<InvalidDataException>(() => _installationManager.InstallPackage(packageInfo, CancellationToken.None)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InstallPackage_Valid_Success()
|
||||
{
|
||||
var packageInfo = new InstallationInfo()
|
||||
{
|
||||
Name = "Test",
|
||||
SourceUrl = "https://repo.jellyfin.org/releases/plugin/empty/empty.zip",
|
||||
Checksum = "11b5b2f1a9ebc4f66d6ef19018543361"
|
||||
};
|
||||
|
||||
var ex = await Record.ExceptionAsync(() => _installationManager.InstallPackage(packageInfo, CancellationToken.None)).ConfigureAwait(false);
|
||||
Assert.Null(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Mime;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Api.Models.StartupDtos;
|
||||
@@ -26,14 +26,13 @@ namespace Jellyfin.Server.Integration.Tests
|
||||
using var completeResponse = await client.PostAsync("/Startup/Complete", new ByteArrayContent(Array.Empty<byte>())).ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.NoContent, completeResponse.StatusCode);
|
||||
|
||||
using var content = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(
|
||||
using var content = JsonContent.Create(
|
||||
new AuthenticateUserByName()
|
||||
{
|
||||
Username = user!.Name,
|
||||
Pw = user.Password,
|
||||
},
|
||||
jsonOptions));
|
||||
content.Headers.ContentType = MediaTypeHeaderValue.Parse(MediaTypeNames.Application.Json);
|
||||
options: jsonOptions);
|
||||
content.Headers.Add("X-Emby-Authorization", DummyAuthHeader);
|
||||
|
||||
using var authResponse = await client.PostAsync("/Users/AuthenticateByName", content).ConfigureAwait(false);
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Jellyfin.Server.Integration.Tests.Controllers
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Equal(MediaTypeNames.Text.Html, response.Content.Headers.ContentType?.MediaType);
|
||||
StreamReader reader = new StreamReader(typeof(TestPlugin).Assembly.GetManifestResourceStream("Jellyfin.Server.Integration.Tests.TestPage.html")!);
|
||||
Assert.Equal(await response.Content.ReadAsStringAsync(), reader.ReadToEnd());
|
||||
Assert.Equal(await response.Content.ReadAsStringAsync().ConfigureAwait(false), await reader.ReadToEndAsync().ConfigureAwait(false));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using System.Net.Mime;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using Xunit;
|
||||
using Xunit.Priority;
|
||||
|
||||
namespace Jellyfin.Server.Integration.Tests.Controllers
|
||||
{
|
||||
[TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)]
|
||||
public sealed class DlnaControllerTests : IClassFixture<JellyfinApplicationFactory>
|
||||
{
|
||||
private const string NonExistentProfile = "1322f35b8f2c434dad3cc07c9b97dbd1";
|
||||
private readonly JellyfinApplicationFactory _factory;
|
||||
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
|
||||
private static string? _accessToken;
|
||||
private static string? _newDeviceProfileId;
|
||||
|
||||
public DlnaControllerTests(JellyfinApplicationFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Priority(0)]
|
||||
public async Task GetProfile_DoesNotExist_NotFound()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
using var getResponse = await client.GetAsync("/Dlna/Profiles/" + NonExistentProfile).ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.NotFound, getResponse.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Priority(0)]
|
||||
public async Task DeleteProfile_DoesNotExist_NotFound()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
using var getResponse = await client.DeleteAsync("/Dlna/Profiles/" + NonExistentProfile).ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.NotFound, getResponse.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Priority(0)]
|
||||
public async Task UpdateProfile_DoesNotExist_NotFound()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
var deviceProfile = new DeviceProfile()
|
||||
{
|
||||
Name = "ThisProfileDoesNotExist"
|
||||
};
|
||||
|
||||
using var getResponse = await client.PostAsJsonAsync("/Dlna/Profiles/" + NonExistentProfile, deviceProfile, _jsonOptions).ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.NotFound, getResponse.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Priority(1)]
|
||||
public async Task CreateProfile_Valid_NoContent()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
var deviceProfile = new DeviceProfile()
|
||||
{
|
||||
Name = "ThisProfileIsNew"
|
||||
};
|
||||
|
||||
using var getResponse = await client.PostAsJsonAsync("/Dlna/Profiles", deviceProfile, _jsonOptions).ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.NoContent, getResponse.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Priority(2)]
|
||||
public async Task GetProfileInfos_Valid_ContainsThisProfileIsNew()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
using var response = await client.GetAsync("/Dlna/ProfileInfos").ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Equal(MediaTypeNames.Application.Json, response.Content.Headers.ContentType?.MediaType);
|
||||
Assert.Equal(Encoding.UTF8.BodyName, response.Content.Headers.ContentType?.CharSet);
|
||||
|
||||
var profiles = await JsonSerializer.DeserializeAsync<DeviceProfileInfo[]>(
|
||||
await response.Content.ReadAsStreamAsync().ConfigureAwait(false),
|
||||
_jsonOptions).ConfigureAwait(false);
|
||||
|
||||
var newProfile = profiles?.FirstOrDefault(x => string.Equals(x.Name, "ThisProfileIsNew", StringComparison.Ordinal));
|
||||
Assert.NotNull(newProfile);
|
||||
_newDeviceProfileId = newProfile!.Id;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Priority(3)]
|
||||
public async Task UpdateProfile_Valid_NoContent()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
var updatedProfile = new DeviceProfile()
|
||||
{
|
||||
Name = "ThisProfileIsUpdated",
|
||||
Id = _newDeviceProfileId
|
||||
};
|
||||
|
||||
using var getResponse = await client.PostAsJsonAsync("/Dlna/Profiles", updatedProfile, _jsonOptions).ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.NoContent, getResponse.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Priority(4)]
|
||||
public async Task DeleteProfile_Valid_NoContent()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
using var getResponse = await client.DeleteAsync("/Dlna/Profiles/" + _newDeviceProfileId).ConfigureAwait(false);
|
||||
Console.WriteLine(await getResponse.Content.ReadAsStringAsync().ConfigureAwait(false));
|
||||
Assert.Equal(HttpStatusCode.NoContent, getResponse.StatusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Mime;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Api.Models.LibraryStructureDto;
|
||||
@@ -71,9 +70,7 @@ namespace Jellyfin.Server.Integration.Tests.Controllers
|
||||
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);
|
||||
var response = await client.PostAsJsonAsync("Library/VirtualFolders/Paths", data, _jsonOptions).ConfigureAwait(false);
|
||||
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
@@ -90,9 +87,7 @@ namespace Jellyfin.Server.Integration.Tests.Controllers
|
||||
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);
|
||||
var response = await client.PostAsJsonAsync("Library/VirtualFolders/Paths/Update", data, _jsonOptions).ConfigureAwait(false);
|
||||
|
||||
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using System.Net.Mime;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
@@ -36,9 +36,7 @@ namespace Jellyfin.Server.Integration.Tests.Controllers
|
||||
PreferredMetadataLanguage = "nl"
|
||||
};
|
||||
|
||||
using var postContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(config, _jsonOptions));
|
||||
postContent.Headers.ContentType = MediaTypeHeaderValue.Parse(MediaTypeNames.Application.Json);
|
||||
using var postResponse = await client.PostAsync("/Startup/Configuration", postContent).ConfigureAwait(false);
|
||||
using var postResponse = await client.PostAsJsonAsync("/Startup/Configuration", config, _jsonOptions).ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.NoContent, postResponse.StatusCode);
|
||||
|
||||
using var getResponse = await client.GetAsync("/Startup/Configuration").ConfigureAwait(false);
|
||||
@@ -80,9 +78,7 @@ namespace Jellyfin.Server.Integration.Tests.Controllers
|
||||
Password = "NewPassword"
|
||||
};
|
||||
|
||||
using var postContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(user, _jsonOptions));
|
||||
postContent.Headers.ContentType = MediaTypeHeaderValue.Parse(MediaTypeNames.Application.Json);
|
||||
var postResponse = await client.PostAsync("/Startup/User", postContent).ConfigureAwait(false);
|
||||
var postResponse = await client.PostAsJsonAsync("/Startup/User", user, _jsonOptions).ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.NoContent, postResponse.StatusCode);
|
||||
|
||||
var getResponse = await client.GetAsync("/Startup/User").ConfigureAwait(false);
|
||||
|
||||
@@ -3,8 +3,7 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Mime;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Api.Models.UserDtos;
|
||||
@@ -31,18 +30,10 @@ namespace Jellyfin.Server.Integration.Tests.Controllers
|
||||
}
|
||||
|
||||
private Task<HttpResponseMessage> CreateUserByName(HttpClient httpClient, CreateUserByName request)
|
||||
{
|
||||
using var postContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(request, _jsonOpions));
|
||||
postContent.Headers.ContentType = MediaTypeHeaderValue.Parse(MediaTypeNames.Application.Json);
|
||||
return httpClient.PostAsync("Users/New", postContent);
|
||||
}
|
||||
=> httpClient.PostAsJsonAsync("Users/New", request, _jsonOpions);
|
||||
|
||||
private Task<HttpResponseMessage> UpdateUserPassword(HttpClient httpClient, Guid userId, UpdateUserPassword request)
|
||||
{
|
||||
using var postContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(request, _jsonOpions));
|
||||
postContent.Headers.ContentType = MediaTypeHeaderValue.Parse(MediaTypeNames.Application.Json);
|
||||
return httpClient.PostAsync("Users/" + userId.ToString("N", CultureInfo.InvariantCulture) + "/Password", postContent);
|
||||
}
|
||||
=> httpClient.PostAsJsonAsync("Users/" + userId.ToString("N", CultureInfo.InvariantCulture) + "/Password", request, _jsonOpions);
|
||||
|
||||
[Fact]
|
||||
[Priority(-1)]
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
<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.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.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.1.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -29,7 +29,7 @@
|
||||
<!-- 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="StyleCop.Analyzers" Version="1.2.0-beta.376" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using Emby.Server.Implementations;
|
||||
using Emby.Server.Implementations.IO;
|
||||
using MediaBrowser.Common;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
@@ -67,7 +66,7 @@ namespace Jellyfin.Server.Integration.Tests
|
||||
var startupConfig = Program.CreateAppConfiguration(commandLineOpts, appPaths);
|
||||
|
||||
ILoggerFactory loggerFactory = new SerilogLoggerFactory();
|
||||
var serviceCollection = new ServiceCollection();
|
||||
|
||||
_disposableComponents.Add(loggerFactory);
|
||||
|
||||
// Create the app host and initialize it
|
||||
@@ -75,11 +74,10 @@ namespace Jellyfin.Server.Integration.Tests
|
||||
appPaths,
|
||||
loggerFactory,
|
||||
commandLineOpts,
|
||||
new ConfigurationBuilder().Build(),
|
||||
new ManagedFileSystem(loggerFactory.CreateLogger<ManagedFileSystem>(), appPaths),
|
||||
serviceCollection);
|
||||
new ConfigurationBuilder().Build());
|
||||
_disposableComponents.Add(appHost);
|
||||
appHost.Init();
|
||||
var serviceCollection = new ServiceCollection();
|
||||
appHost.Init(serviceCollection);
|
||||
|
||||
// Configure the web host builder
|
||||
Program.ConfigureWebHostBuilder(builder, appHost, serviceCollection, commandLineOpts, startupConfig, appPaths);
|
||||
|
||||
@@ -2,9 +2,7 @@ using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Emby.Server.Implementations;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Model.IO;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Jellyfin.Server.Integration.Tests
|
||||
@@ -21,22 +19,16 @@ namespace Jellyfin.Server.Integration.Tests
|
||||
/// <param name="loggerFactory">The <see cref="ILoggerFactory" /> to be used by the <see cref="CoreAppHost" />.</param>
|
||||
/// <param name="options">The <see cref="StartupOptions" /> to be used by the <see cref="CoreAppHost" />.</param>
|
||||
/// <param name="startup">The <see cref="IConfiguration" /> to be used by the <see cref="CoreAppHost" />.</param>
|
||||
/// <param name="fileSystem">The <see cref="IFileSystem" /> to be used by the <see cref="CoreAppHost" />.</param>
|
||||
/// <param name="collection">The <see cref="IServiceCollection"/> to be used by the <see cref="CoreAppHost"/>.</param>
|
||||
public TestAppHost(
|
||||
IServerApplicationPaths applicationPaths,
|
||||
ILoggerFactory loggerFactory,
|
||||
IStartupOptions options,
|
||||
IConfiguration startup,
|
||||
IFileSystem fileSystem,
|
||||
IServiceCollection collection)
|
||||
IConfiguration startup)
|
||||
: base(
|
||||
applicationPaths,
|
||||
loggerFactory,
|
||||
options,
|
||||
startup,
|
||||
fileSystem,
|
||||
collection)
|
||||
startup)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,8 @@ namespace Jellyfin.Server.Integration.Tests
|
||||
{
|
||||
Scheme = "ws",
|
||||
Path = "websocket"
|
||||
}.Uri, CancellationToken.None));
|
||||
}.Uri,
|
||||
CancellationToken.None));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,19 +10,19 @@
|
||||
<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.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.0" />
|
||||
<PackageReference Include="Moq" Version="4.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="StyleCop.Analyzers" Version="1.2.0-beta.376" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
@@ -23,7 +23,7 @@
|
||||
<!-- 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="StyleCop.Analyzers" Version="1.2.0-beta.376" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Jellyfin.Data.Entities;
|
||||
@@ -157,33 +158,33 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
|
||||
// Images
|
||||
Assert.Equal(7, result.RemoteImages.Count);
|
||||
|
||||
var posters = result.RemoteImages.Where(x => x.type == ImageType.Primary).ToList();
|
||||
var posters = result.RemoteImages.Where(x => x.Type == ImageType.Primary).ToList();
|
||||
Assert.Single(posters);
|
||||
Assert.Equal("http://image.tmdb.org/t/p/original/9rtrRGeRnL0JKtu9IMBWsmlmmZz.jpg", posters[0].url);
|
||||
Assert.Equal("http://image.tmdb.org/t/p/original/9rtrRGeRnL0JKtu9IMBWsmlmmZz.jpg", posters[0].Url);
|
||||
|
||||
var logos = result.RemoteImages.Where(x => x.type == ImageType.Logo).ToList();
|
||||
var logos = result.RemoteImages.Where(x => x.Type == ImageType.Logo).ToList();
|
||||
Assert.Single(logos);
|
||||
Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/hdmovielogo/justice-league-5865bf95cbadb.png", logos[0].url);
|
||||
Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/hdmovielogo/justice-league-5865bf95cbadb.png", logos[0].Url);
|
||||
|
||||
var banners = result.RemoteImages.Where(x => x.type == ImageType.Banner).ToList();
|
||||
var banners = result.RemoteImages.Where(x => x.Type == ImageType.Banner).ToList();
|
||||
Assert.Single(banners);
|
||||
Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/moviebanner/justice-league-586017e95adbd.jpg", banners[0].url);
|
||||
Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/moviebanner/justice-league-586017e95adbd.jpg", banners[0].Url);
|
||||
|
||||
var thumbs = result.RemoteImages.Where(x => x.type == ImageType.Thumb).ToList();
|
||||
var thumbs = result.RemoteImages.Where(x => x.Type == ImageType.Thumb).ToList();
|
||||
Assert.Single(thumbs);
|
||||
Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/moviethumb/justice-league-585fb155c3743.jpg", thumbs[0].url);
|
||||
Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/moviethumb/justice-league-585fb155c3743.jpg", thumbs[0].Url);
|
||||
|
||||
var art = result.RemoteImages.Where(x => x.type == ImageType.Art).ToList();
|
||||
var art = result.RemoteImages.Where(x => x.Type == ImageType.Art).ToList();
|
||||
Assert.Single(art);
|
||||
Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/hdmovieclearart/justice-league-5865c23193041.png", art[0].url);
|
||||
Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/hdmovieclearart/justice-league-5865c23193041.png", art[0].Url);
|
||||
|
||||
var discArt = result.RemoteImages.Where(x => x.type == ImageType.Disc).ToList();
|
||||
var discArt = result.RemoteImages.Where(x => x.Type == ImageType.Disc).ToList();
|
||||
Assert.Single(discArt);
|
||||
Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-5a3af26360617.png", discArt[0].url);
|
||||
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();
|
||||
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);
|
||||
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);
|
||||
@@ -216,8 +217,8 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
|
||||
|
||||
_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);
|
||||
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]
|
||||
|
||||
Reference in New Issue
Block a user