aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Emby.Dlna/Main/DlnaEntryPoint.cs23
-rw-r--r--Emby.Dlna/Service/BaseControlHandler.cs2
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj2
-rw-r--r--Emby.Server.Implementations/Library/SearchEngine.cs2
-rw-r--r--Emby.Server.Implementations/Library/UserDataManager.cs20
-rw-r--r--Jellyfin.Api/Controllers/SystemController.cs2
-rw-r--r--Jellyfin.Networking/Manager/NetworkManager.cs2
-rw-r--r--Jellyfin.Server/Program.cs3
-rw-r--r--MediaBrowser.Common/Json/Converters/JsonNullableGuidConverter.cs33
-rw-r--r--MediaBrowser.Common/Json/JsonDefaults.cs1
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs12
-rw-r--r--tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj2
-rw-r--r--tests/Jellyfin.Common.Tests/Json/JsonNullableGuidConverterTests.cs69
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj2
14 files changed, 164 insertions, 11 deletions
diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs
index 175a1ad2b..3f7b558f6 100644
--- a/Emby.Dlna/Main/DlnaEntryPoint.cs
+++ b/Emby.Dlna/Main/DlnaEntryPoint.cs
@@ -9,6 +9,7 @@ using System.Threading;
using System.Threading.Tasks;
using Emby.Dlna.PlayTo;
using Emby.Dlna.Ssdp;
+using Jellyfin.Networking.Configuration;
using Jellyfin.Networking.Manager;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
@@ -52,6 +53,8 @@ namespace Emby.Dlna.Main
private readonly ISocketFactory _socketFactory;
private readonly INetworkManager _networkManager;
private readonly object _syncLock = new object();
+ private readonly NetworkConfiguration _netConfig;
+ private readonly bool _disabled;
private PlayToManager _manager;
private SsdpDevicePublisher _publisher;
@@ -122,6 +125,13 @@ namespace Emby.Dlna.Main
httpClientFactory,
config);
Current = this;
+
+ _netConfig = config.GetConfiguration<NetworkConfiguration>("network");
+ _disabled = appHost.ListenWithHttps && _netConfig.RequireHttps;
+ if (_disabled)
+ {
+ _logger.LogError("The DLNA specification does not support HTTPS.");
+ }
}
public static DlnaEntryPoint Current { get; private set; }
@@ -141,6 +151,12 @@ namespace Emby.Dlna.Main
{
await ((DlnaManager)_dlnaManager).InitProfilesAsync().ConfigureAwait(false);
+ if (_disabled)
+ {
+ // No use starting as dlna won't work, as we're running purely on HTTPS.
+ return;
+ }
+
ReloadComponents();
_config.NamedConfigurationUpdated += OnNamedConfigurationUpdated;
@@ -296,12 +312,15 @@ namespace Emby.Dlna.Main
_logger.LogInformation("Registering publisher for {0} on {1}", fullService, address);
- var uri = new Uri(_appHost.GetSmartApiUrl(address.Address) + descriptorUri);
+ var uri = new UriBuilder(_appHost.GetSmartApiUrl(address.Address) + descriptorUri);
+ // DLNA will only work over http, so we must reset to http:// : {port}
+ uri.Scheme = "http://";
+ uri.Port = _netConfig.PublicPort;
var device = new SsdpRootDevice
{
CacheLifetime = TimeSpan.FromSeconds(1800), // How long SSDP clients can cache this info.
- Location = uri, // Must point to the URL that serves your devices UPnP description document.
+ Location = uri.Uri, // Must point to the URL that serves your devices UPnP description document.
Address = address.Address,
PrefixLength = address.PrefixLength,
FriendlyName = "Jellyfin",
diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs
index 198852ec1..8d2486fee 100644
--- a/Emby.Dlna/Service/BaseControlHandler.cs
+++ b/Emby.Dlna/Service/BaseControlHandler.cs
@@ -49,7 +49,7 @@ namespace Emby.Dlna.Service
{
ControlRequestInfo requestInfo = null;
- using (var streamReader = new StreamReader(request.InputXml))
+ using (var streamReader = new StreamReader(request.InputXml, Encoding.UTF8))
{
var readerSettings = new XmlReaderSettings()
{
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 1e54c3b33..592873fe4 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -31,7 +31,7 @@
<PackageReference Include="prometheus-net.DotNetRuntime" Version="3.4.1" />
<PackageReference Include="sharpcompress" Version="0.26.0" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
- <PackageReference Include="DotNet.Glob" Version="3.1.0" />
+ <PackageReference Include="DotNet.Glob" Version="3.1.2" />
</ItemGroup>
<ItemGroup>
diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs
index 1d9529dff..94602582b 100644
--- a/Emby.Server.Implementations/Library/SearchEngine.cs
+++ b/Emby.Server.Implementations/Library/SearchEngine.cs
@@ -47,7 +47,7 @@ namespace Emby.Server.Implementations.Library
if (query.Limit.HasValue)
{
- results = results.GetRange(0, query.Limit.Value);
+ results = results.GetRange(0, Math.Min(query.Limit.Value, results.Count));
}
return new QueryResult<SearchHintInfo>
diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs
index f9e5e6bbc..d16275b19 100644
--- a/Emby.Server.Implementations/Library/UserDataManager.cs
+++ b/Emby.Server.Implementations/Library/UserDataManager.cs
@@ -14,6 +14,7 @@ using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using Book = MediaBrowser.Controller.Entities.Book;
+using AudioBook = MediaBrowser.Controller.Entities.AudioBook;
namespace Emby.Server.Implementations.Library
{
@@ -219,7 +220,7 @@ namespace Emby.Server.Implementations.Library
var hasRuntime = runtimeTicks > 0;
// If a position has been reported, and if we know the duration
- if (positionTicks > 0 && hasRuntime)
+ if (positionTicks > 0 && hasRuntime && !(item is AudioBook))
{
var pctIn = decimal.Divide(positionTicks, runtimeTicks) * 100;
@@ -245,6 +246,23 @@ namespace Emby.Server.Implementations.Library
}
}
}
+ else if (positionTicks > 0 && hasRuntime && item is AudioBook)
+ {
+ var minIn = TimeSpan.FromTicks(positionTicks).TotalMinutes;
+ var minOut = TimeSpan.FromTicks(runtimeTicks - positionTicks).TotalMinutes;
+
+ if (minIn > _config.Configuration.MinAudiobookResume)
+ {
+ // ignore progress during the beginning
+ positionTicks = 0;
+ }
+ else if (minOut < _config.Configuration.MaxAudiobookResume || positionTicks >= runtimeTicks)
+ {
+ // mark as completed close to the end
+ positionTicks = 0;
+ data.Played = playedToCompletion = true;
+ }
+ }
else if (!hasRuntime)
{
// If we don't know the runtime we'll just have to assume it was fully played
diff --git a/Jellyfin.Api/Controllers/SystemController.cs b/Jellyfin.Api/Controllers/SystemController.cs
index d79bea985..e67a27ae3 100644
--- a/Jellyfin.Api/Controllers/SystemController.cs
+++ b/Jellyfin.Api/Controllers/SystemController.cs
@@ -203,7 +203,7 @@ namespace Jellyfin.Api.Controllers
// For older files, assume fully static
var fileShare = file.LastWriteTimeUtc < DateTime.UtcNow.AddHours(-1) ? FileShare.Read : FileShare.ReadWrite;
FileStream stream = new FileStream(file.FullName, FileMode.Open, FileAccess.Read, fileShare);
- return File(stream, "text/plain");
+ return File(stream, "text/plain; charset=utf-8");
}
/// <summary>
diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs
index 258535541..60b899519 100644
--- a/Jellyfin.Networking/Manager/NetworkManager.cs
+++ b/Jellyfin.Networking/Manager/NetworkManager.cs
@@ -784,7 +784,7 @@ namespace Jellyfin.Networking.Manager
}
else
{
- _logger.LogDebug("Invalid or unknown network {Token}.", token);
+ _logger.LogDebug("Invalid or unknown object {Token}.", token);
}
}
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index a1a7a3053..f05cdfe9b 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -598,7 +598,8 @@ namespace Jellyfin.Server
.WriteTo.Async(x => x.File(
Path.Combine(appPaths.LogDirectoryPath, "log_.log"),
rollingInterval: RollingInterval.Day,
- outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message}{NewLine}{Exception}"))
+ outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message}{NewLine}{Exception}",
+ encoding: Encoding.UTF8))
.Enrich.FromLogContext()
.Enrich.WithThreadId()
.CreateLogger();
diff --git a/MediaBrowser.Common/Json/Converters/JsonNullableGuidConverter.cs b/MediaBrowser.Common/Json/Converters/JsonNullableGuidConverter.cs
new file mode 100644
index 000000000..6d96d5496
--- /dev/null
+++ b/MediaBrowser.Common/Json/Converters/JsonNullableGuidConverter.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Globalization;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace MediaBrowser.Common.Json.Converters
+{
+ /// <summary>
+ /// Converts a GUID object or value to/from JSON.
+ /// </summary>
+ public class JsonNullableGuidConverter : JsonConverter<Guid?>
+ {
+ /// <inheritdoc />
+ public override Guid? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ var guidStr = reader.GetString();
+ return guidStr == null ? null : new Guid(guidStr);
+ }
+
+ /// <inheritdoc />
+ public override void Write(Utf8JsonWriter writer, Guid? value, JsonSerializerOptions options)
+ {
+ if (value == null || value == Guid.Empty)
+ {
+ writer.WriteNullValue();
+ }
+ else
+ {
+ writer.WriteStringValue(value.Value.ToString("N", CultureInfo.InvariantCulture));
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Common/Json/JsonDefaults.cs b/MediaBrowser.Common/Json/JsonDefaults.cs
index 1ec2a87c5..2ef24a884 100644
--- a/MediaBrowser.Common/Json/JsonDefaults.cs
+++ b/MediaBrowser.Common/Json/JsonDefaults.cs
@@ -34,6 +34,7 @@ namespace MediaBrowser.Common.Json
Converters =
{
new JsonGuidConverter(),
+ new JsonNullableGuidConverter(),
new JsonVersionConverter(),
new JsonStringEnumConverter(),
new JsonNullableStructConverterFactory(),
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index 7013cb300..9fb978e9b 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -299,6 +299,18 @@ namespace MediaBrowser.Model.Configuration
public int MinResumeDurationSeconds { get; set; } = 300;
/// <summary>
+ /// Gets or sets the minimum minutes of a book that must be played in order for playstate to be updated.
+ /// </summary>
+ /// <value>The min resume in minutes.</value>
+ public int MinAudiobookResume { get; set; } = 5;
+
+ /// <summary>
+ /// Gets or sets the remaining minutes of a book that can be played while still saving playstate. If this percentage is crossed playstate will be reset to the beginning and the item will be marked watched.
+ /// </summary>
+ /// <value>The remaining time in minutes.</value>
+ public int MaxAudiobookResume { get; set; } = 5;
+
+ /// <summary>
/// Gets or sets the delay in seconds that we will wait after a file system change to try and discover what has been added/removed
/// Some delay is necessary with some items because their creation is not atomic. It involves the creation of several
/// different directories and files.
diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
index b5e8e521c..559289f1e 100644
--- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
+++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
@@ -13,7 +13,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="AutoFixture" Version="4.14.0" />
+ <PackageReference Include="AutoFixture" Version="4.15.0" />
<PackageReference Include="AutoFixture.AutoMoq" Version="4.14.0" />
<PackageReference Include="AutoFixture.Xunit2" Version="4.14.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.1" />
diff --git a/tests/Jellyfin.Common.Tests/Json/JsonNullableGuidConverterTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonNullableGuidConverterTests.cs
new file mode 100644
index 000000000..efc0c4af9
--- /dev/null
+++ b/tests/Jellyfin.Common.Tests/Json/JsonNullableGuidConverterTests.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Globalization;
+using System.Text.Json;
+using MediaBrowser.Common.Json.Converters;
+using Xunit;
+
+namespace Jellyfin.Common.Tests.Json
+{
+ public class JsonNullableGuidConverterTests
+ {
+ private readonly JsonSerializerOptions _options;
+
+ public JsonNullableGuidConverterTests()
+ {
+ _options = new JsonSerializerOptions();
+ _options.Converters.Add(new JsonNullableGuidConverter());
+ }
+
+ [Fact]
+ public void Deserialize_Valid_Success()
+ {
+ Guid? value = JsonSerializer.Deserialize<Guid?>(@"""a852a27afe324084ae66db579ee3ee18""", _options);
+ Assert.Equal(new Guid("a852a27afe324084ae66db579ee3ee18"), value);
+ }
+
+ [Fact]
+ public void Deserialize_ValidDashed_Success()
+ {
+ Guid? value = JsonSerializer.Deserialize<Guid?>(@"""e9b2dcaa-529c-426e-9433-5e9981f27f2e""", _options);
+ Assert.Equal(new Guid("e9b2dcaa-529c-426e-9433-5e9981f27f2e"), value);
+ }
+
+ [Fact]
+ public void Roundtrip_Valid_Success()
+ {
+ Guid guid = new Guid("a852a27afe324084ae66db579ee3ee18");
+ string value = JsonSerializer.Serialize(guid, _options);
+ Assert.Equal(guid, JsonSerializer.Deserialize<Guid?>(value, _options));
+ }
+
+ [Fact]
+ public void Deserialize_Null_EmptyGuid()
+ {
+ Assert.Null(JsonSerializer.Deserialize<Guid?>("null", _options));
+ }
+
+ [Fact]
+ public void Serialize_EmptyGuid_EmptyGuid()
+ {
+ Assert.Equal("null", JsonSerializer.Serialize((Guid?)Guid.Empty, _options));
+ }
+
+ [Fact]
+ public void Serialize_Valid_NoDash_Success()
+ {
+ var guid = (Guid?)new Guid("531797E9-9457-40E0-88BC-B1D6D38752FA");
+ var str = JsonSerializer.Serialize(guid, _options);
+ Assert.Equal($"\"{guid:N}\"", str);
+ }
+
+ [Fact]
+ public void Serialize_Nullable_Success()
+ {
+ Guid? guid = new Guid("531797E9-9457-40E0-88BC-B1D6D38752FA");
+ var str = JsonSerializer.Serialize(guid, _options);
+ Assert.Equal($"\"{guid:N}\"", str);
+ }
+ }
+}
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 bcd12deaf..b7a006070 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
+++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
@@ -14,7 +14,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="AutoFixture" Version="4.14.0" />
+ <PackageReference Include="AutoFixture" Version="4.15.0" />
<PackageReference Include="AutoFixture.AutoMoq" Version="4.14.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="Moq" Version="4.15.2" />