aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs10
-rw-r--r--Emby.Server.Implementations/Library/UserViewManager.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/DiscoverResponse.cs42
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs42
-rw-r--r--Emby.Server.Implementations/Properties/AssemblyInfo.cs2
-rw-r--r--Jellyfin.Api/Controllers/ImageController.cs8
-rw-r--r--Jellyfin.Api/Controllers/QuickConnectController.cs4
-rw-r--r--Jellyfin.Api/Controllers/UserController.cs40
-rw-r--r--Jellyfin.Networking/Manager/NetworkManager.cs4
-rw-r--r--MediaBrowser.Common/Json/Converters/JsonBoolNumberConverter.cs30
-rw-r--r--MediaBrowser.Common/Json/Converters/JsonGuidConverter.cs10
-rw-r--r--MediaBrowser.Common/Json/JsonDefaults.cs1
-rw-r--r--MediaBrowser.Model/Dto/BaseItemDto.cs10
-rw-r--r--tests/Jellyfin.Common.Tests/Json/JsonBoolNumberTests.cs34
-rw-r--r--tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs6
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj4
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunHostTests.cs77
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/LiveTv/discover.json1
19 files changed, 238 insertions, 95 deletions
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index f3e3a6397..686944a28 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -1138,7 +1138,10 @@ namespace Emby.Server.Implementations.Dto
if (episodeSeries != null)
{
dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, episodeSeries, ImageType.Primary);
- AttachPrimaryImageAspectRatio(dto, episodeSeries);
+ if (!dto.ImageTags.ContainsKey(ImageType.Primary))
+ {
+ AttachPrimaryImageAspectRatio(dto, episodeSeries);
+ }
}
}
@@ -1185,7 +1188,10 @@ namespace Emby.Server.Implementations.Dto
if (series != null)
{
dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, series, ImageType.Primary);
- AttachPrimaryImageAspectRatio(dto, series);
+ if (!dto.ImageTags.ContainsKey(ImageType.Primary))
+ {
+ AttachPrimaryImageAspectRatio(dto, series);
+ }
}
}
}
diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs
index f51657c63..e4221dd50 100644
--- a/Emby.Server.Implementations/Library/UserViewManager.cs
+++ b/Emby.Server.Implementations/Library/UserViewManager.cs
@@ -139,13 +139,13 @@ namespace Emby.Server.Implementations.Library
return list
.OrderBy(i =>
{
- var index = orders.IndexOf(i.Id.ToString("N", CultureInfo.InvariantCulture));
+ var index = orders.IndexOf(i.Id.ToString("D", CultureInfo.InvariantCulture));
if (index == -1
&& i is UserView view
&& view.DisplayParentId != Guid.Empty)
{
- index = orders.IndexOf(view.DisplayParentId.ToString("N", CultureInfo.InvariantCulture));
+ index = orders.IndexOf(view.DisplayParentId.ToString("D", CultureInfo.InvariantCulture));
}
return index == -1 ? int.MaxValue : index;
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index 8c9bb6ba0..7842be716 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -1928,7 +1928,7 @@ namespace Emby.Server.Implementations.LiveTv
foreach (var programDto in currentProgramDtos)
{
- if (currentChannelsDict.TryGetValue(programDto.ChannelId, out BaseItemDto channelDto))
+ if (programDto.ChannelId.HasValue && currentChannelsDict.TryGetValue(programDto.ChannelId.Value, out BaseItemDto channelDto))
{
channelDto.CurrentProgram = programDto;
}
@@ -2018,7 +2018,7 @@ namespace Emby.Server.Implementations.LiveTv
info.DayPattern = _tvDtoService.GetDayPattern(info.Days);
info.Name = program.Name;
- info.ChannelId = programDto.ChannelId;
+ info.ChannelId = programDto.ChannelId ?? Guid.Empty;
info.ChannelName = programDto.ChannelName;
info.StartDate = program.StartDate;
info.Name = program.Name;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/DiscoverResponse.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/DiscoverResponse.cs
new file mode 100644
index 000000000..3d739f308
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/DiscoverResponse.cs
@@ -0,0 +1,42 @@
+#pragma warning disable CS1591
+
+using System;
+
+namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
+{
+ public class DiscoverResponse
+ {
+ public string FriendlyName { get; set; }
+
+ public string ModelNumber { get; set; }
+
+ public string FirmwareName { get; set; }
+
+ public string FirmwareVersion { get; set; }
+
+ public string DeviceID { get; set; }
+
+ public string DeviceAuth { get; set; }
+
+ public string BaseURL { get; set; }
+
+ public string LineupURL { get; set; }
+
+ public int TunerCount { get; set; }
+
+ public bool SupportsTranscoding
+ {
+ get
+ {
+ var model = ModelNumber ?? string.Empty;
+
+ if (model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index b6444b172..1329084b6 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -8,10 +8,12 @@ using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text.Json;
+using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Json.Converters;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
@@ -109,7 +111,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}).Cast<ChannelInfo>().ToList();
}
- private async Task<DiscoverResponse> GetModelInfo(TunerHostInfo info, bool throwAllExceptions, CancellationToken cancellationToken)
+ internal async Task<DiscoverResponse> GetModelInfo(TunerHostInfo info, bool throwAllExceptions, CancellationToken cancellationToken)
{
var cacheKey = info.Id;
@@ -127,7 +129,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
try
{
using var response = await _httpClientFactory.CreateClient(NamedClient.Default)
- .GetAsync(string.Format(CultureInfo.InvariantCulture, "{0}/discover.json", GetApiUrl(info)), HttpCompletionOption.ResponseHeadersRead, cancellationToken)
+ .GetAsync(GetApiUrl(info) + "/discover.json", HttpCompletionOption.ResponseHeadersRead, cancellationToken)
.ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
var discoverResponse = await JsonSerializer.DeserializeAsync<DiscoverResponse>(stream, cancellationToken: cancellationToken)
@@ -674,42 +676,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
}
- public class DiscoverResponse
- {
- public string FriendlyName { get; set; }
-
- public string ModelNumber { get; set; }
-
- public string FirmwareName { get; set; }
-
- public string FirmwareVersion { get; set; }
-
- public string DeviceID { get; set; }
-
- public string DeviceAuth { get; set; }
-
- public string BaseURL { get; set; }
-
- public string LineupURL { get; set; }
-
- public int TunerCount { get; set; }
-
- public bool SupportsTranscoding
- {
- get
- {
- var model = ModelNumber ?? string.Empty;
-
- if (model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1)
- {
- return true;
- }
-
- return false;
- }
- }
- }
-
public async Task<List<TunerHostInfo>> DiscoverDevices(int discoveryDurationMs, CancellationToken cancellationToken)
{
lock (_modelCache)
diff --git a/Emby.Server.Implementations/Properties/AssemblyInfo.cs b/Emby.Server.Implementations/Properties/AssemblyInfo.cs
index a1933f66e..cb7972173 100644
--- a/Emby.Server.Implementations/Properties/AssemblyInfo.cs
+++ b/Emby.Server.Implementations/Properties/AssemblyInfo.cs
@@ -1,5 +1,6 @@
using System.Reflection;
using System.Resources;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
@@ -14,6 +15,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en")]
+[assembly: InternalsVisibleTo("Jellyfin.Server.Implementations.Tests")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs
index 65de81d7a..e828a0801 100644
--- a/Jellyfin.Api/Controllers/ImageController.cs
+++ b/Jellyfin.Api/Controllers/ImageController.cs
@@ -98,7 +98,7 @@ namespace Jellyfin.Api.Controllers
{
if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true))
{
- return Forbid("User is not allowed to update the image.");
+ return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the image.");
}
var user = _userManager.GetUserById(userId);
@@ -144,7 +144,7 @@ namespace Jellyfin.Api.Controllers
{
if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true))
{
- return Forbid("User is not allowed to update the image.");
+ return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the image.");
}
var user = _userManager.GetUserById(userId);
@@ -190,7 +190,7 @@ namespace Jellyfin.Api.Controllers
{
if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true))
{
- return Forbid("User is not allowed to delete the image.");
+ return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to delete the image.");
}
var user = _userManager.GetUserById(userId);
@@ -229,7 +229,7 @@ namespace Jellyfin.Api.Controllers
{
if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true))
{
- return Forbid("User is not allowed to delete the image.");
+ return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to delete the image.");
}
var user = _userManager.GetUserById(userId);
diff --git a/Jellyfin.Api/Controllers/QuickConnectController.cs b/Jellyfin.Api/Controllers/QuickConnectController.cs
index 73da2f906..4ac849181 100644
--- a/Jellyfin.Api/Controllers/QuickConnectController.cs
+++ b/Jellyfin.Api/Controllers/QuickConnectController.cs
@@ -88,7 +88,7 @@ namespace Jellyfin.Api.Controllers
{
if (_quickConnect.State == QuickConnectState.Unavailable)
{
- return Forbid("Quick connect is unavailable");
+ return StatusCode(StatusCodes.Status403Forbidden, "Quick connect is unavailable");
}
_quickConnect.Activate();
@@ -126,7 +126,7 @@ namespace Jellyfin.Api.Controllers
var userId = ClaimHelpers.GetUserId(Request.HttpContext.User);
if (!userId.HasValue)
{
- return Forbid("Unknown user id");
+ return StatusCode(StatusCodes.Status403Forbidden, "Unknown user id");
}
return _quickConnect.AuthorizeRequest(userId.Value, code);
diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs
index 9805b84b1..8256e3782 100644
--- a/Jellyfin.Api/Controllers/UserController.cs
+++ b/Jellyfin.Api/Controllers/UserController.cs
@@ -169,7 +169,7 @@ namespace Jellyfin.Api.Controllers
if (!string.IsNullOrEmpty(password) && string.IsNullOrEmpty(pw))
{
- return Forbid("Only sha1 password is not allowed.");
+ return StatusCode(StatusCodes.Status403Forbidden, "Only sha1 password is not allowed.");
}
// Password should always be null
@@ -267,11 +267,11 @@ namespace Jellyfin.Api.Controllers
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> UpdateUserPassword(
[FromRoute, Required] Guid userId,
- [FromBody] UpdateUserPassword request)
+ [FromBody, Required] UpdateUserPassword request)
{
if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true))
{
- return Forbid("User is not allowed to update the password.");
+ return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the password.");
}
var user = _userManager.GetUserById(userId);
@@ -296,7 +296,7 @@ namespace Jellyfin.Api.Controllers
if (success == null)
{
- return Forbid("Invalid user or password entered.");
+ return StatusCode(StatusCodes.Status403Forbidden, "Invalid user or password entered.");
}
await _userManager.ChangePassword(user, request.NewPw).ConfigureAwait(false);
@@ -325,11 +325,11 @@ namespace Jellyfin.Api.Controllers
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult UpdateUserEasyPassword(
[FromRoute, Required] Guid userId,
- [FromBody] UpdateUserEasyPassword request)
+ [FromBody, Required] UpdateUserEasyPassword request)
{
if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true))
{
- return Forbid("User is not allowed to update the easy password.");
+ return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the easy password.");
}
var user = _userManager.GetUserById(userId);
@@ -367,16 +367,11 @@ namespace Jellyfin.Api.Controllers
[ProducesResponseType(StatusCodes.Status403Forbidden)]
public async Task<ActionResult> UpdateUser(
[FromRoute, Required] Guid userId,
- [FromBody] UserDto updateUser)
+ [FromBody, Required] UserDto updateUser)
{
- if (updateUser == null)
- {
- return BadRequest();
- }
-
if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, false))
{
- return Forbid("User update not allowed.");
+ return StatusCode(StatusCodes.Status403Forbidden, "User update not allowed.");
}
var user = _userManager.GetUserById(userId);
@@ -407,13 +402,8 @@ namespace Jellyfin.Api.Controllers
[ProducesResponseType(StatusCodes.Status403Forbidden)]
public async Task<ActionResult> UpdateUserPolicy(
[FromRoute, Required] Guid userId,
- [FromBody] UserPolicy newPolicy)
+ [FromBody, Required] UserPolicy newPolicy)
{
- if (newPolicy == null)
- {
- return BadRequest();
- }
-
var user = _userManager.GetUserById(userId);
// If removing admin access
@@ -421,14 +411,14 @@ namespace Jellyfin.Api.Controllers
{
if (_userManager.Users.Count(i => i.HasPermission(PermissionKind.IsAdministrator)) == 1)
{
- return Forbid("There must be at least one user in the system with administrative access.");
+ return StatusCode(StatusCodes.Status403Forbidden, "There must be at least one user in the system with administrative access.");
}
}
// If disabling
if (newPolicy.IsDisabled && user.HasPermission(PermissionKind.IsAdministrator))
{
- return Forbid("Administrators cannot be disabled.");
+ return StatusCode(StatusCodes.Status403Forbidden, "Administrators cannot be disabled.");
}
// If disabling
@@ -436,7 +426,7 @@ namespace Jellyfin.Api.Controllers
{
if (_userManager.Users.Count(i => !i.HasPermission(PermissionKind.IsDisabled)) == 1)
{
- return Forbid("There must be at least one enabled user in the system.");
+ return StatusCode(StatusCodes.Status403Forbidden, "There must be at least one enabled user in the system.");
}
var currentToken = _authContext.GetAuthorizationInfo(Request).Token;
@@ -462,11 +452,11 @@ namespace Jellyfin.Api.Controllers
[ProducesResponseType(StatusCodes.Status403Forbidden)]
public async Task<ActionResult> UpdateUserConfiguration(
[FromRoute, Required] Guid userId,
- [FromBody] UserConfiguration userConfig)
+ [FromBody, Required] UserConfiguration userConfig)
{
if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, false))
{
- return Forbid("User configuration update not allowed");
+ return StatusCode(StatusCodes.Status403Forbidden, "User configuration update not allowed");
}
await _userManager.UpdateConfigurationAsync(userId, userConfig).ConfigureAwait(false);
@@ -483,7 +473,7 @@ namespace Jellyfin.Api.Controllers
[HttpPost("New")]
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status200OK)]
- public async Task<ActionResult<UserDto>> CreateUserByName([FromBody] CreateUserByName request)
+ public async Task<ActionResult<UserDto>> CreateUserByName([FromBody, Required] CreateUserByName request)
{
var newUser = await _userManager.CreateUserAsync(request.Name).ConfigureAwait(false);
diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs
index 85da927fb..43f2f7add 100644
--- a/Jellyfin.Networking/Manager/NetworkManager.cs
+++ b/Jellyfin.Networking/Manager/NetworkManager.cs
@@ -1314,9 +1314,7 @@ namespace Jellyfin.Networking.Manager
return true;
}
- // Have to return something, so return an internal address
-
- _logger.LogWarning("{Source}: External request received, however, no WAN interface found.", source);
+ _logger.LogDebug("{Source}: External request received, but no WAN interface found. Need to route through internal network.", source);
return false;
}
}
diff --git a/MediaBrowser.Common/Json/Converters/JsonBoolNumberConverter.cs b/MediaBrowser.Common/Json/Converters/JsonBoolNumberConverter.cs
new file mode 100644
index 000000000..b29e6a71a
--- /dev/null
+++ b/MediaBrowser.Common/Json/Converters/JsonBoolNumberConverter.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace MediaBrowser.Common.Json.Converters
+{
+ /// <summary>
+ /// Converts a number to a boolean.
+ /// This is needed for HDHomerun.
+ /// </summary>
+ public class JsonBoolNumberConverter : JsonConverter<bool>
+ {
+ /// <inheritdoc />
+ public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ if (reader.TokenType == JsonTokenType.Number)
+ {
+ return Convert.ToBoolean(reader.GetInt32());
+ }
+
+ return reader.GetBoolean();
+ }
+
+ /// <inheritdoc />
+ public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options)
+ {
+ writer.WriteBooleanValue(value);
+ }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Common/Json/Converters/JsonGuidConverter.cs b/MediaBrowser.Common/Json/Converters/JsonGuidConverter.cs
index 52e08d071..ccf214e3c 100644
--- a/MediaBrowser.Common/Json/Converters/JsonGuidConverter.cs
+++ b/MediaBrowser.Common/Json/Converters/JsonGuidConverter.cs
@@ -13,21 +13,13 @@ namespace MediaBrowser.Common.Json.Converters
public override Guid Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var guidStr = reader.GetString();
-
return guidStr == null ? Guid.Empty : new Guid(guidStr);
}
/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, Guid value, JsonSerializerOptions options)
{
- if (value == Guid.Empty)
- {
- writer.WriteNullValue();
- }
- else
- {
- writer.WriteStringValue(value);
- }
+ writer.WriteStringValue(value);
}
}
}
diff --git a/MediaBrowser.Common/Json/JsonDefaults.cs b/MediaBrowser.Common/Json/JsonDefaults.cs
index c5050a21d..b76edd2bc 100644
--- a/MediaBrowser.Common/Json/JsonDefaults.cs
+++ b/MediaBrowser.Common/Json/JsonDefaults.cs
@@ -43,6 +43,7 @@ namespace MediaBrowser.Common.Json
options.Converters.Add(new JsonVersionConverter());
options.Converters.Add(new JsonStringEnumConverter());
options.Converters.Add(new JsonNullableStructConverterFactory());
+ options.Converters.Add(new JsonBoolNumberConverter());
return options;
}
diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs
index fac754177..3f7aac9cd 100644
--- a/MediaBrowser.Model/Dto/BaseItemDto.cs
+++ b/MediaBrowser.Model/Dto/BaseItemDto.cs
@@ -152,7 +152,7 @@ namespace MediaBrowser.Model.Dto
/// Gets or sets the channel identifier.
/// </summary>
/// <value>The channel identifier.</value>
- public Guid ChannelId { get; set; }
+ public Guid? ChannelId { get; set; }
public string ChannelName { get; set; }
@@ -270,7 +270,7 @@ namespace MediaBrowser.Model.Dto
/// Gets or sets the parent id.
/// </summary>
/// <value>The parent id.</value>
- public Guid ParentId { get; set; }
+ public Guid? ParentId { get; set; }
/// <summary>
/// Gets or sets the type.
@@ -344,13 +344,13 @@ namespace MediaBrowser.Model.Dto
/// Gets or sets the series id.
/// </summary>
/// <value>The series id.</value>
- public Guid SeriesId { get; set; }
+ public Guid? SeriesId { get; set; }
/// <summary>
/// Gets or sets the season identifier.
/// </summary>
/// <value>The season identifier.</value>
- public Guid SeasonId { get; set; }
+ public Guid? SeasonId { get; set; }
/// <summary>
/// Gets or sets the special feature count.
@@ -428,7 +428,7 @@ namespace MediaBrowser.Model.Dto
/// Gets or sets the album id.
/// </summary>
/// <value>The album id.</value>
- public Guid AlbumId { get; set; }
+ public Guid? AlbumId { get; set; }
/// <summary>
/// Gets or sets the album image tag.
diff --git a/tests/Jellyfin.Common.Tests/Json/JsonBoolNumberTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonBoolNumberTests.cs
new file mode 100644
index 000000000..9ded01f2b
--- /dev/null
+++ b/tests/Jellyfin.Common.Tests/Json/JsonBoolNumberTests.cs
@@ -0,0 +1,34 @@
+using System.Text.Json;
+using MediaBrowser.Common.Json.Converters;
+using Xunit;
+
+namespace Jellyfin.Common.Tests.Json
+{
+ public static class JsonBoolNumberTests
+ {
+ [Theory]
+ [InlineData("1", true)]
+ [InlineData("0", false)]
+ [InlineData("2", true)]
+ [InlineData("true", true)]
+ [InlineData("false", false)]
+ public static void Deserialize_Number_Valid_Success(string input, bool? output)
+ {
+ var options = new JsonSerializerOptions();
+ options.Converters.Add(new JsonBoolNumberConverter());
+ var value = JsonSerializer.Deserialize<bool>(input, options);
+ Assert.Equal(value, output);
+ }
+
+ [Theory]
+ [InlineData(true, "true")]
+ [InlineData(false, "false")]
+ public static void Serialize_Bool_Success(bool input, string output)
+ {
+ var options = new JsonSerializerOptions();
+ options.Converters.Add(new JsonBoolNumberConverter());
+ var value = JsonSerializer.Serialize(input, options);
+ Assert.Equal(value, output);
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs
index 3c94db491..663cc3c78 100644
--- a/tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs
+++ b/tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs
@@ -3,7 +3,7 @@ using System.Text.Json;
using MediaBrowser.Common.Json.Converters;
using Xunit;
-namespace Jellyfin.Common.Tests.Extensions
+namespace Jellyfin.Common.Tests.Json
{
public class JsonGuidConverterTests
{
@@ -44,9 +44,9 @@ namespace Jellyfin.Common.Tests.Extensions
}
[Fact]
- public void Serialize_EmptyGuid_Null()
+ public void Serialize_EmptyGuid_EmptyGuid()
{
- Assert.Equal("null", JsonSerializer.Serialize(Guid.Empty, _options));
+ Assert.Equal($"\"{Guid.Empty}\"", JsonSerializer.Serialize(Guid.Empty, _options));
}
}
}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
index fffbc6212..08392b25e 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
+++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
@@ -35,6 +35,10 @@
<ProjectReference Include="..\..\Emby.Server.Implementations\Emby.Server.Implementations.csproj" />
</ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="LiveTv\discover.json" />
+ </ItemGroup>
+
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
diff --git a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunHostTests.cs b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunHostTests.cs
new file mode 100644
index 000000000..c38d9ea4d
--- /dev/null
+++ b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunHostTests.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Net;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+using AutoFixture;
+using AutoFixture.AutoMoq;
+using Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun;
+using MediaBrowser.Model.LiveTv;
+using Moq;
+using Moq.Protected;
+using Xunit;
+
+namespace Jellyfin.Server.Implementations.Tests.LiveTv
+{
+ public class HdHomerunHostTests
+ {
+ private const string TestIp = "http://192.168.1.182";
+
+ private readonly Fixture _fixture;
+ private readonly HdHomerunHost _hdHomerunHost;
+
+ public HdHomerunHostTests()
+ {
+ const string ResourceName = "Jellyfin.Server.Implementations.Tests.LiveTv.discover.json";
+
+ var messageHandler = new Mock<HttpMessageHandler>();
+ messageHandler.Protected().Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
+ .Returns(
+ () => Task.FromResult(new HttpResponseMessage()
+ {
+ Content = new StreamContent(typeof(HdHomerunHostTests).Assembly.GetManifestResourceStream(ResourceName)!)
+ }));
+
+ var http = new Mock<IHttpClientFactory>();
+ http.Setup(x => x.CreateClient(It.IsAny<string>()))
+ .Returns(new HttpClient(messageHandler.Object));
+ _fixture = new Fixture();
+ _fixture.Customize(new AutoMoqCustomization
+ {
+ ConfigureMembers = true
+ }).Inject(http);
+ _hdHomerunHost = _fixture.Create<HdHomerunHost>();
+ }
+
+ [Fact]
+ public async Task GetModelInfo_Valid_Success()
+ {
+ var host = new TunerHostInfo()
+ {
+ Url = TestIp
+ };
+
+ var modelInfo = await _hdHomerunHost.GetModelInfo(host, true, CancellationToken.None).ConfigureAwait(false);
+ Assert.Equal("HDHomeRun PRIME", modelInfo.FriendlyName);
+ Assert.Equal("HDHR3-CC", modelInfo.ModelNumber);
+ Assert.Equal("hdhomerun3_cablecard", modelInfo.FirmwareName);
+ Assert.Equal("20160630atest2", modelInfo.FirmwareVersion);
+ Assert.Equal("FFFFFFFF", modelInfo.DeviceID);
+ Assert.Equal("FFFFFFFF", modelInfo.DeviceAuth);
+ Assert.Equal(3, modelInfo.TunerCount);
+ Assert.Equal("http://192.168.1.182:80", modelInfo.BaseURL);
+ Assert.Equal("http://192.168.1.182:80/lineup.json", modelInfo.LineupURL);
+ }
+
+ [Fact]
+ public async Task GetModelInfo_EmptyUrl_ArgumentException()
+ {
+ var host = new TunerHostInfo()
+ {
+ Url = string.Empty
+ };
+
+ await Assert.ThrowsAsync<ArgumentException>(() => _hdHomerunHost.GetModelInfo(host, true, CancellationToken.None));
+ }
+ }
+}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/discover.json b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/discover.json
new file mode 100644
index 000000000..851f17bb2
--- /dev/null
+++ b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/discover.json
@@ -0,0 +1 @@
+{"FriendlyName":"HDHomeRun PRIME","ModelNumber":"HDHR3-CC","FirmwareName":"hdhomerun3_cablecard","FirmwareVersion":"20160630atest2","DeviceID":"FFFFFFFF","DeviceAuth":"FFFFFFFF","TunerCount":3,"ConditionalAccess":1,"BaseURL":"http://192.168.1.182:80","LineupURL":"http://192.168.1.182:80/lineup.json"}