aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Emby.Dlna/Eventing/DlnaEventManager.cs3
-rw-r--r--Emby.Dlna/PlayTo/PlayToManager.cs24
-rw-r--r--Emby.Dlna/Properties/AssemblyInfo.cs2
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj4
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthService.cs9
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs85
-rw-r--r--Emby.Server.Implementations/Localization/Core/da.json7
-rw-r--r--Emby.Server.Implementations/Localization/Core/el.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt-PT.json7
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt.json7
-rw-r--r--Emby.Server.Implementations/Localization/Core/ro.json5
-rw-r--r--Emby.Server.Implementations/Localization/Core/tr.json6
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-TW.json5
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs62
-rw-r--r--Emby.Server.Implementations/TV/TVSeriesManager.cs34
-rw-r--r--Jellyfin.Api/Auth/CustomAuthenticationHandler.cs5
-rw-r--r--Jellyfin.Api/Controllers/AudioController.cs12
-rw-r--r--Jellyfin.Api/Controllers/DynamicHlsController.cs36
-rw-r--r--Jellyfin.Api/Controllers/VideoHlsController.cs6
-rw-r--r--Jellyfin.Api/Controllers/VideosController.cs12
-rw-r--r--Jellyfin.Server.Implementations/JellyfinDb.cs2
-rw-r--r--Jellyfin.Server.Implementations/ModelBuilderExtensions.cs48
-rw-r--r--Jellyfin.Server.Implementations/ValueConverters/DateTimeKindValueConverter.cs21
-rw-r--r--Jellyfin.Server/Startup.cs2
-rw-r--r--MediaBrowser.Common/Json/Converters/JsonDateTimeIso8601Converter.cs24
-rw-r--r--MediaBrowser.Common/Json/Converters/JsonGuidConverter.cs6
-rw-r--r--MediaBrowser.Common/Json/JsonDefaults.cs1
-rw-r--r--MediaBrowser.Common/Plugins/LocalPlugin.cs10
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs1
-rw-r--r--MediaBrowser.Controller/Net/AuthorizationInfo.cs5
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs19
-rw-r--r--MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs6
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs2
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs7
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs2
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs7
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/Configuration/PluginConfiguration.cs2
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html14
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs12
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs2
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html16
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs12
-rw-r--r--MediaBrowser.sln9
-rw-r--r--tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs4
-rw-r--r--tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj2
-rw-r--r--tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs42
-rw-r--r--tests/Jellyfin.Dlna.Tests/GetUuidTests.cs17
-rw-r--r--tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj33
-rw-r--r--tests/Jellyfin.Networking.Tests/NetworkTesting/Jellyfin.Networking.Tests.csproj2
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj2
51 files changed, 398 insertions, 271 deletions
diff --git a/Emby.Dlna/Eventing/DlnaEventManager.cs b/Emby.Dlna/Eventing/DlnaEventManager.cs
index b6e45c50e..ff81e83b5 100644
--- a/Emby.Dlna/Eventing/DlnaEventManager.cs
+++ b/Emby.Dlna/Eventing/DlnaEventManager.cs
@@ -72,7 +72,8 @@ namespace Emby.Dlna.Eventing
Id = id,
CallbackUrl = callbackUrl,
SubscriptionTime = DateTime.UtcNow,
- TimeoutSeconds = timeout
+ TimeoutSeconds = timeout,
+ NotificationType = notificationType
});
return GetEventSubscriptionResponse(id, requestedTimeoutString, timeout);
diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs
index 854b6a1a2..cb183ce71 100644
--- a/Emby.Dlna/PlayTo/PlayToManager.cs
+++ b/Emby.Dlna/PlayTo/PlayToManager.cs
@@ -3,13 +3,11 @@
using System;
using System.Globalization;
using System.Linq;
-using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Events;
using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
@@ -130,24 +128,36 @@ namespace Emby.Dlna.PlayTo
}
}
- private static string GetUuid(string usn)
+ internal static string GetUuid(string usn)
{
const string UuidStr = "uuid:";
const string UuidColonStr = "::";
var index = usn.IndexOf(UuidStr, StringComparison.OrdinalIgnoreCase);
+ if (index == -1)
+ {
+ return usn.GetMD5().ToString("N", CultureInfo.InvariantCulture);
+ }
+
+ ReadOnlySpan<char> tmp = usn.AsSpan()[(index + UuidStr.Length)..];
+
+ index = tmp.IndexOf(UuidColonStr, StringComparison.OrdinalIgnoreCase);
if (index != -1)
{
- return usn.Substring(index + UuidStr.Length);
+ tmp = tmp[..index];
}
- index = usn.IndexOf(UuidColonStr, StringComparison.OrdinalIgnoreCase);
+ index = tmp.IndexOf('{');
if (index != -1)
{
- usn = usn.Substring(0, index + UuidColonStr.Length);
+ int endIndex = tmp.IndexOf('}');
+ if (endIndex != -1)
+ {
+ tmp = tmp[(index + 1)..endIndex];
+ }
}
- return usn.GetMD5().ToString("N", CultureInfo.InvariantCulture);
+ return tmp.ToString();
}
private async Task AddDevice(UpnpDeviceInfo info, string location, CancellationToken cancellationToken)
diff --git a/Emby.Dlna/Properties/AssemblyInfo.cs b/Emby.Dlna/Properties/AssemblyInfo.cs
index a2c1e0db8..606ffcf4f 100644
--- a/Emby.Dlna/Properties/AssemblyInfo.cs
+++ b/Emby.Dlna/Properties/AssemblyInfo.cs
@@ -1,5 +1,6 @@
using System.Reflection;
using System.Resources;
+using System.Runtime.CompilerServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
@@ -13,6 +14,7 @@ using System.Resources;
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en")]
+[assembly: InternalsVisibleTo("Jellyfin.Dlna.Tests")]
// Version information for an assembly consists of the following four values:
//
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index dab4ec5a6..91c4648c6 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -36,8 +36,8 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
<PackageReference Include="Mono.Nat" Version="3.0.1" />
- <PackageReference Include="prometheus-net.DotNetRuntime" Version="3.4.0" />
- <PackageReference Include="ServiceStack.Text.Core" Version="5.10.0" />
+ <PackageReference Include="prometheus-net.DotNetRuntime" Version="3.4.1" />
+ <PackageReference Include="ServiceStack.Text.Core" Version="5.10.2" />
<PackageReference Include="sharpcompress" Version="0.26.0" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
<PackageReference Include="DotNet.Glob" Version="3.1.0" />
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
index df7a034e8..4a0fc8239 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
@@ -1,5 +1,6 @@
#pragma warning disable CS1591
+using System;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Net;
@@ -20,9 +21,15 @@ namespace Emby.Server.Implementations.HttpServer.Security
public AuthorizationInfo Authenticate(HttpRequest request)
{
var auth = _authorizationContext.GetAuthorizationInfo(request);
+
+ if (!auth.HasToken)
+ {
+ throw new AuthenticationException("Request does not contain a token.");
+ }
+
if (!auth.IsAuthenticated)
{
- throw new AuthenticationException("Invalid token.");
+ throw new SecurityException("Invalid token.");
}
if (auth.User?.HasPermission(PermissionKind.IsDisabled) ?? false)
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
index fdf2e3908..d62e2eefe 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
@@ -102,7 +102,8 @@ namespace Emby.Server.Implementations.HttpServer.Security
DeviceId = deviceId,
Version = version,
Token = token,
- IsAuthenticated = false
+ IsAuthenticated = false,
+ HasToken = false
};
if (string.IsNullOrWhiteSpace(token))
@@ -111,6 +112,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
return authInfo;
}
+ authInfo.HasToken = true;
var result = _authRepo.Get(new AuthenticationInfoQuery
{
AccessToken = token
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index f181eb7a0..1084ddf74 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -4,7 +4,6 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
-using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
@@ -19,7 +18,6 @@ using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv;
-using MediaBrowser.Model.Net;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
@@ -36,6 +34,9 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private readonly IApplicationHost _appHost;
private readonly ICryptoProvider _cryptoProvider;
+ private readonly ConcurrentDictionary<string, NameValuePair> _tokens = new ConcurrentDictionary<string, NameValuePair>();
+ private DateTime _lastErrorResponse;
+
public SchedulesDirect(
ILogger<SchedulesDirect> logger,
IJsonSerializer jsonSerializer,
@@ -50,8 +51,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
_cryptoProvider = cryptoProvider;
}
- private string UserAgent => _appHost.ApplicationUserAgent;
-
/// <inheritdoc />
public string Name => "Schedules Direct";
@@ -307,7 +306,8 @@ namespace Emby.Server.Implementations.LiveTv.Listings
if (details.contentRating != null && details.contentRating.Count > 0)
{
- info.OfficialRating = details.contentRating[0].code.Replace("TV", "TV-").Replace("--", "-");
+ info.OfficialRating = details.contentRating[0].code.Replace("TV", "TV-", StringComparison.Ordinal)
+ .Replace("--", "-", StringComparison.Ordinal);
var invalid = new[] { "N/A", "Approved", "Not Rated", "Passed" };
if (invalid.Contains(info.OfficialRating, StringComparer.OrdinalIgnoreCase))
@@ -450,7 +450,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private async Task<List<ScheduleDirect.ShowImages>> GetImageForPrograms(
ListingsProviderInfo info,
- List<string> programIds,
+ IReadOnlyList<string> programIds,
CancellationToken cancellationToken)
{
if (programIds.Count == 0)
@@ -458,23 +458,21 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return new List<ScheduleDirect.ShowImages>();
}
- var imageIdString = "[";
-
- foreach (var i in programIds)
+ StringBuilder str = new StringBuilder("[", 1 + (programIds.Count * 13));
+ foreach (ReadOnlySpan<char> i in programIds)
{
- var imageId = i.Substring(0, 10);
-
- if (!imageIdString.Contains(imageId, StringComparison.Ordinal))
- {
- imageIdString += "\"" + imageId + "\",";
- }
+ str.Append('"')
+ .Append(i.Slice(0, 10))
+ .Append("\",");
}
- imageIdString = imageIdString.TrimEnd(',') + "]";
+ // Remove last ,
+ str.Length--;
+ str.Append(']');
using var message = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/metadata/programs")
{
- Content = new StringContent(imageIdString, Encoding.UTF8, MediaTypeNames.Application.Json)
+ Content = new StringContent(str.ToString(), Encoding.UTF8, MediaTypeNames.Application.Json)
};
try
@@ -539,9 +537,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return lineups;
}
- private readonly ConcurrentDictionary<string, NameValuePair> _tokens = new ConcurrentDictionary<string, NameValuePair>();
- private DateTime _lastErrorResponse;
-
private async Task<string> GetToken(ListingsProviderInfo info, CancellationToken cancellationToken)
{
var username = info.Username;
@@ -564,8 +559,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return null;
}
- NameValuePair savedToken;
- if (!_tokens.TryGetValue(username, out savedToken))
+ if (!_tokens.TryGetValue(username, out NameValuePair savedToken))
{
savedToken = new NameValuePair();
_tokens.TryAdd(username, savedToken);
@@ -655,7 +649,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
using var response = await Send(options, false, null, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
var root = await _jsonSerializer.DeserializeFromStreamAsync<ScheduleDirect.Token>(stream).ConfigureAwait(false);
- if (root.message == "OK")
+ if (string.Equals(root.message, "OK", StringComparison.Ordinal))
{
_logger.LogInformation("Authenticated with Schedules Direct token: " + root.token);
return root.token;
@@ -779,24 +773,28 @@ namespace Emby.Server.Implementations.LiveTv.Listings
using var options = new HttpRequestMessage(HttpMethod.Get, ApiUrl + "/lineups/" + listingsId);
options.Headers.TryAddWithoutValidation("token", token);
- var list = new List<ChannelInfo>();
-
using var httpResponse = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
var root = await _jsonSerializer.DeserializeFromStreamAsync<ScheduleDirect.Channel>(stream).ConfigureAwait(false);
_logger.LogInformation("Found {ChannelCount} channels on the lineup on ScheduleDirect", root.map.Count);
_logger.LogInformation("Mapping Stations to Channel");
- var allStations = root.stations ?? Enumerable.Empty<ScheduleDirect.Station>();
+ var allStations = root.stations ?? new List<ScheduleDirect.Station>();
- foreach (ScheduleDirect.Map map in root.map)
+ var map = root.map;
+ int len = map.Count;
+ var array = new List<ChannelInfo>(len);
+ for (int i = 0; i < len; i++)
{
- var channelNumber = GetChannelNumber(map);
+ var channelNumber = GetChannelNumber(map[i]);
- var station = allStations.FirstOrDefault(item => string.Equals(item.stationID, map.stationID, StringComparison.OrdinalIgnoreCase));
+ var station = allStations.Find(item => string.Equals(item.stationID, map[i].stationID, StringComparison.OrdinalIgnoreCase));
if (station == null)
{
- station = new ScheduleDirect.Station { stationID = map.stationID };
+ station = new ScheduleDirect.Station
+ {
+ stationID = map[i].stationID
+ };
}
var channelInfo = new ChannelInfo
@@ -812,32 +810,10 @@ namespace Emby.Server.Implementations.LiveTv.Listings
channelInfo.ImageUrl = station.logo.URL;
}
- list.Add(channelInfo);
- }
-
- return list;
- }
-
- private ScheduleDirect.Station GetStation(List<ScheduleDirect.Station> allStations, string channelNumber, string channelName)
- {
- if (!string.IsNullOrWhiteSpace(channelName))
- {
- channelName = NormalizeName(channelName);
-
- var result = allStations.FirstOrDefault(i => string.Equals(NormalizeName(i.callsign ?? string.Empty), channelName, StringComparison.OrdinalIgnoreCase));
-
- if (result != null)
- {
- return result;
- }
- }
-
- if (!string.IsNullOrWhiteSpace(channelNumber))
- {
- return allStations.FirstOrDefault(i => string.Equals(NormalizeName(i.stationID ?? string.Empty), channelNumber, StringComparison.OrdinalIgnoreCase));
+ array[i] = channelInfo;
}
- return null;
+ return array;
}
private static string NormalizeName(string value)
@@ -1046,7 +1022,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
}
- //
public class Title
{
public string title120 { get; set; }
diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json
index b29ad94ef..4ee4eb989 100644
--- a/Emby.Server.Implementations/Localization/Core/da.json
+++ b/Emby.Server.Implementations/Localization/Core/da.json
@@ -113,5 +113,10 @@
"TaskCleanTranscodeDescription": "Fjern transcode filer som er mere end en dag gammel.",
"TaskCleanTranscode": "Rengør Transcode Mappen",
"TaskRefreshPeople": "Genopfrisk Personer",
- "TaskRefreshPeopleDescription": "Opdatere metadata for skuespillere og instruktører i dit bibliotek."
+ "TaskRefreshPeopleDescription": "Opdatere metadata for skuespillere og instruktører i dit bibliotek.",
+ "TaskCleanActivityLogDescription": "Sletter linjer i aktivitetsloggen ældre end den konfigureret alder.",
+ "TaskCleanActivityLog": "Ryd Aktivitetslog",
+ "Undefined": "Udefineret",
+ "Forced": "Tvunget",
+ "Default": "Standard"
}
diff --git a/Emby.Server.Implementations/Localization/Core/el.json b/Emby.Server.Implementations/Localization/Core/el.json
index dcf3311cd..23d45b473 100644
--- a/Emby.Server.Implementations/Localization/Core/el.json
+++ b/Emby.Server.Implementations/Localization/Core/el.json
@@ -114,7 +114,7 @@
"TaskUpdatePluginsDescription": "Κατεβάζει και εγκαθιστά ενημερώσεις για τις προσθήκες που έχουν ρυθμιστεί για αυτόματη ενημέρωση.",
"TaskUpdatePlugins": "Ενημέρωση Προσθηκών",
"TaskRefreshPeopleDescription": "Ενημερώνει μεταδεδομένα για ηθοποιούς και σκηνοθέτες στην βιβλιοθήκη των πολυμέσων σας.",
- "TaskCleanActivityLogDescription": "Διαγράφει καταχωρήσεις απο το αρχείο καταγραφής δραστηριοτήτων παλαιότερες από την ηλικία που έχει διαμορφωθεί.",
+ "TaskCleanActivityLogDescription": "Διαγράφει καταχωρήσεις απο το αρχείο καταγραφής παλαιότερες από την επιλεγμένη ηλικία.",
"TaskCleanActivityLog": "Καθαρό Αρχείο Καταγραφής Δραστηριοτήτων",
"Undefined": "Απροσδιόριστο",
"Forced": "Εξαναγκασμένο",
diff --git a/Emby.Server.Implementations/Localization/Core/pt-PT.json b/Emby.Server.Implementations/Localization/Core/pt-PT.json
index 90a4941c5..8c41edf96 100644
--- a/Emby.Server.Implementations/Localization/Core/pt-PT.json
+++ b/Emby.Server.Implementations/Localization/Core/pt-PT.json
@@ -113,5 +113,10 @@
"TasksChannelsCategory": "Canais da Internet",
"TasksApplicationCategory": "Aplicação",
"TasksLibraryCategory": "Biblioteca",
- "TasksMaintenanceCategory": "Manutenção"
+ "TasksMaintenanceCategory": "Manutenção",
+ "TaskCleanActivityLogDescription": "Apaga as entradas do registo de atividade anteriores à data configurada.",
+ "TaskCleanActivityLog": "Limpar registo de atividade",
+ "Undefined": "Indefinido",
+ "Forced": "Forçado",
+ "Default": "Padrão"
}
diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json
index 2079940cd..f1a78b2d3 100644
--- a/Emby.Server.Implementations/Localization/Core/pt.json
+++ b/Emby.Server.Implementations/Localization/Core/pt.json
@@ -112,5 +112,10 @@
"TaskUpdatePluginsDescription": "Download e instala as atualizações para plug-ins configurados para atualização automática.",
"TaskRefreshPeopleDescription": "Atualiza os metadados para atores e diretores na tua biblioteca de media.",
"TaskRefreshPeople": "Atualizar pessoas",
- "TaskRefreshLibraryDescription": "Pesquisa a tua biblioteca de media por novos ficheiros e atualiza os metadados."
+ "TaskRefreshLibraryDescription": "Pesquisa a tua biblioteca de media por novos ficheiros e atualiza os metadados.",
+ "TaskCleanActivityLog": "Limpar registo de atividade",
+ "Undefined": "Indefinido",
+ "Forced": "Forçado",
+ "Default": "Predefinição",
+ "TaskCleanActivityLogDescription": "Apaga itens no registro com idade acima do que é configurado."
}
diff --git a/Emby.Server.Implementations/Localization/Core/ro.json b/Emby.Server.Implementations/Localization/Core/ro.json
index 5e4022292..510aac11c 100644
--- a/Emby.Server.Implementations/Localization/Core/ro.json
+++ b/Emby.Server.Implementations/Localization/Core/ro.json
@@ -114,5 +114,8 @@
"TasksLibraryCategory": "Librărie",
"TasksMaintenanceCategory": "Mentenanță",
"TaskCleanActivityLogDescription": "Șterge intrările din jurnalul de activitate mai vechi de data configurată.",
- "TaskCleanActivityLog": "Curăță Jurnalul de Activitate"
+ "TaskCleanActivityLog": "Curăță Jurnalul de Activitate",
+ "Undefined": "Nedefinit",
+ "Forced": "Forțat",
+ "Default": "Implicit"
}
diff --git a/Emby.Server.Implementations/Localization/Core/tr.json b/Emby.Server.Implementations/Localization/Core/tr.json
index 54d3a65f0..885663eed 100644
--- a/Emby.Server.Implementations/Localization/Core/tr.json
+++ b/Emby.Server.Implementations/Localization/Core/tr.json
@@ -12,7 +12,7 @@
"DeviceOfflineWithName": "{0} bağlantısı kesildi",
"DeviceOnlineWithName": "{0} bağlı",
"FailedLoginAttemptWithUserName": "{0} adresinden giriş başarısız oldu",
- "Favorites": "Favoriler",
+ "Favorites": "Favorilerim",
"Folders": "Klasörler",
"Genres": "Türler",
"HeaderAlbumArtists": "Albüm Sanatçıları",
@@ -115,5 +115,7 @@
"TaskRefreshChapterImages": "Bölüm Resimlerini Çıkar",
"TaskCleanCacheDescription": "Sistem tarafından artık ihtiyaç duyulmayan önbellek dosyalarını siler.",
"TaskCleanActivityLog": "İşlem Günlüğünü Temizle",
- "TaskCleanActivityLogDescription": "Belirtilen sureden daha eski etkinlik log kayıtları silindi."
+ "TaskCleanActivityLogDescription": "Belirtilen sureden daha eski etkinlik log kayıtları silindi.",
+ "Undefined": "Bilinmeyen",
+ "Default": "Varsayılan"
}
diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json
index d2e3d77a3..6494c0b54 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-TW.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json
@@ -114,5 +114,8 @@
"TasksApplicationCategory": "應用程式",
"TasksMaintenanceCategory": "維護",
"TaskCleanActivityLogDescription": "刪除超過所設時間的活動紀錄。",
- "TaskCleanActivityLog": "清除活動紀錄"
+ "TaskCleanActivityLog": "清除活動紀錄",
+ "Undefined": "未定義的",
+ "Forced": "強制",
+ "Default": "原本"
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
index 26ef19354..b13fc7fc6 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
@@ -5,10 +5,10 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
+using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
-using MediaBrowser.Model.Globalization;
namespace Emby.Server.Implementations.ScheduledTasks.Tasks
{
@@ -23,8 +23,12 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
private readonly ILocalizationManager _localization;
/// <summary>
- /// Initializes a new instance of the <see cref="DeleteTranscodeFileTask" /> class.
+ /// Initializes a new instance of the <see cref="DeleteTranscodeFileTask"/> class.
/// </summary>
+ /// <param name="logger">Instance of the <see cref="ILogger{DeleteTranscodeFileTask}"/> interface.</param>
+ /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
+ /// <param name="configurationManager">Instance of the <see cref="IConfigurationManager"/> interface.</param>
+ /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
public DeleteTranscodeFileTask(
ILogger<DeleteTranscodeFileTask> logger,
IFileSystem fileSystem,
@@ -37,11 +41,42 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
_localization = localization;
}
+ /// <inheritdoc />
+ public string Name => _localization.GetLocalizedString("TaskCleanTranscode");
+
+ /// <inheritdoc />
+ public string Description => _localization.GetLocalizedString("TaskCleanTranscodeDescription");
+
+ /// <inheritdoc />
+ public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
+
+ /// <inheritdoc />
+ public string Key => "DeleteTranscodeFiles";
+
+ /// <inheritdoc />
+ public bool IsHidden => false;
+
+ /// <inheritdoc />
+ public bool IsEnabled => true;
+
+ /// <inheritdoc />
+ public bool IsLogged => true;
+
/// <summary>
/// Creates the triggers that define when the task will run.
/// </summary>
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
- public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() => new List<TaskTriggerInfo>();
+ public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
+ {
+ return new[]
+ {
+ new TaskTriggerInfo
+ {
+ Type = TaskTriggerInfo.TriggerInterval,
+ IntervalTicks = TimeSpan.FromHours(24).Ticks
+ }
+ };
+ }
/// <summary>
/// Returns the task to be executed.
@@ -131,26 +166,5 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
_logger.LogError(ex, "Error deleting file {path}", path);
}
}
-
- /// <inheritdoc />
- public string Name => _localization.GetLocalizedString("TaskCleanTranscode");
-
- /// <inheritdoc />
- public string Description => _localization.GetLocalizedString("TaskCleanTranscodeDescription");
-
- /// <inheritdoc />
- public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
-
- /// <inheritdoc />
- public string Key => "DeleteTranscodeFiles";
-
- /// <inheritdoc />
- public bool IsHidden => false;
-
- /// <inheritdoc />
- public bool IsEnabled => true;
-
- /// <inheritdoc />
- public bool IsLogged => true;
}
}
diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs
index ccd1446dd..3cfcecbd1 100644
--- a/Emby.Server.Implementations/TV/TVSeriesManager.cs
+++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs
@@ -146,28 +146,10 @@ namespace Emby.Server.Implementations.TV
var allNextUp = seriesKeys
.Select(i => GetNextUp(i, currentUser, dtoOptions));
- // allNextUp = allNextUp.OrderByDescending(i => i.Item1);
-
- // If viewing all next up for all series, remove first episodes
- // But if that returns empty, keep those first episodes (avoid completely empty view)
- var alwaysEnableFirstEpisode = !string.IsNullOrEmpty(request.SeriesId);
- var anyFound = false;
-
return allNextUp
.Where(i =>
{
- if (alwaysEnableFirstEpisode || i.Item1 != DateTime.MinValue)
- {
- anyFound = true;
- return true;
- }
-
- if (!anyFound && i.Item1 == DateTime.MinValue)
- {
- return true;
- }
-
- return false;
+ return i.Item1 != DateTime.MinValue;
})
.Select(i => i.Item2())
.Where(i => i != null);
@@ -210,7 +192,7 @@ namespace Emby.Server.Implementations.TV
Func<Episode> getEpisode = () =>
{
- return _libraryManager.GetItemList(new InternalItemsQuery(user)
+ var nextEpisode = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
AncestorWithPresentationUniqueKey = null,
SeriesPresentationUniqueKey = seriesKey,
@@ -223,6 +205,18 @@ namespace Emby.Server.Implementations.TV
MinSortName = lastWatchedEpisode?.SortName,
DtoOptions = dtoOptions
}).Cast<Episode>().FirstOrDefault();
+
+ if (nextEpisode != null)
+ {
+ var userData = _userDataManager.GetUserData(user, nextEpisode);
+
+ if (userData.PlaybackPositionTicks > 0)
+ {
+ return null;
+ }
+ }
+
+ return nextEpisode;
};
if (lastWatchedEpisode != null)
diff --git a/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs b/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs
index 27a1f61be..c56233794 100644
--- a/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs
+++ b/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs
@@ -18,6 +18,7 @@ namespace Jellyfin.Api.Auth
public class CustomAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
private readonly IAuthService _authService;
+ private readonly ILogger<CustomAuthenticationHandler> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="CustomAuthenticationHandler" /> class.
@@ -35,6 +36,7 @@ namespace Jellyfin.Api.Auth
ISystemClock clock) : base(options, logger, encoder, clock)
{
_authService = authService;
+ _logger = logger.CreateLogger<CustomAuthenticationHandler>();
}
/// <inheritdoc />
@@ -70,7 +72,8 @@ namespace Jellyfin.Api.Auth
}
catch (AuthenticationException ex)
{
- return Task.FromResult(AuthenticateResult.Fail(ex));
+ _logger.LogDebug(ex, "Error authenticating with {Handler}", nameof(CustomAuthenticationHandler));
+ return Task.FromResult(AuthenticateResult.NoResult());
}
catch (SecurityException ex)
{
diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs
index c22979495..616fe5b91 100644
--- a/Jellyfin.Api/Controllers/AudioController.cs
+++ b/Jellyfin.Api/Controllers/AudioController.cs
@@ -78,7 +78,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
/// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
/// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
- /// <param name="transcodingReasons">Optional. The transcoding reason.</param>
+ /// <param name="transcodeReasons">Optional. The transcoding reason.</param>
/// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
/// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
/// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
@@ -134,7 +134,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? enableMpegtsM2TsMode,
[FromQuery] string? videoCodec,
[FromQuery] string? subtitleCodec,
- [FromQuery] string? transcodingReasons,
+ [FromQuery] string? transcodeReasons,
[FromQuery] int? audioStreamIndex,
[FromQuery] int? videoStreamIndex,
[FromQuery] EncodingContext? context,
@@ -186,7 +186,7 @@ namespace Jellyfin.Api.Controllers
EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true,
VideoCodec = videoCodec,
SubtitleCodec = subtitleCodec,
- TranscodeReasons = transcodingReasons,
+ TranscodeReasons = transcodeReasons,
AudioStreamIndex = audioStreamIndex,
VideoStreamIndex = videoStreamIndex,
Context = context ?? EncodingContext.Static,
@@ -243,7 +243,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
/// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
/// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
- /// <param name="transcodingReasons">Optional. The transcoding reason.</param>
+ /// <param name="transcodeReasons">Optional. The transcoding reason.</param>
/// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
/// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
/// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
@@ -299,7 +299,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? enableMpegtsM2TsMode,
[FromQuery] string? videoCodec,
[FromQuery] string? subtitleCodec,
- [FromQuery] string? transcodingReasons,
+ [FromQuery] string? transcodeReasons,
[FromQuery] int? audioStreamIndex,
[FromQuery] int? videoStreamIndex,
[FromQuery] EncodingContext? context,
@@ -351,7 +351,7 @@ namespace Jellyfin.Api.Controllers
EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true,
VideoCodec = videoCodec,
SubtitleCodec = subtitleCodec,
- TranscodeReasons = transcodingReasons,
+ TranscodeReasons = transcodeReasons,
AudioStreamIndex = audioStreamIndex,
VideoStreamIndex = videoStreamIndex,
Context = context ?? EncodingContext.Static,
diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs
index eff5bd54a..6e85737d2 100644
--- a/Jellyfin.Api/Controllers/DynamicHlsController.cs
+++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs
@@ -160,7 +160,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
/// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
/// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
- /// <param name="transcodingReasons">Optional. The transcoding reason.</param>
+ /// <param name="transcodeReasons">Optional. The transcoding reason.</param>
/// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
/// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
/// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
@@ -216,7 +216,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? enableMpegtsM2TsMode,
[FromQuery] string? videoCodec,
[FromQuery] string? subtitleCodec,
- [FromQuery] string? transcodingReasons,
+ [FromQuery] string? transcodeReasons,
[FromQuery] int? audioStreamIndex,
[FromQuery] int? videoStreamIndex,
[FromQuery] EncodingContext context,
@@ -268,7 +268,7 @@ namespace Jellyfin.Api.Controllers
EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true,
VideoCodec = videoCodec,
SubtitleCodec = subtitleCodec,
- TranscodeReasons = transcodingReasons,
+ TranscodeReasons = transcodeReasons,
AudioStreamIndex = audioStreamIndex,
VideoStreamIndex = videoStreamIndex,
Context = context,
@@ -326,7 +326,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
/// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
/// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
- /// <param name="transcodingReasons">Optional. The transcoding reason.</param>
+ /// <param name="transcodeReasons">Optional. The transcoding reason.</param>
/// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
/// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
/// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
@@ -383,7 +383,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? enableMpegtsM2TsMode,
[FromQuery] string? videoCodec,
[FromQuery] string? subtitleCodec,
- [FromQuery] string? transcodingReasons,
+ [FromQuery] string? transcodeReasons,
[FromQuery] int? audioStreamIndex,
[FromQuery] int? videoStreamIndex,
[FromQuery] EncodingContext context,
@@ -435,7 +435,7 @@ namespace Jellyfin.Api.Controllers
EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true,
VideoCodec = videoCodec,
SubtitleCodec = subtitleCodec,
- TranscodeReasons = transcodingReasons,
+ TranscodeReasons = transcodeReasons,
AudioStreamIndex = audioStreamIndex,
VideoStreamIndex = videoStreamIndex,
Context = context,
@@ -492,7 +492,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
/// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
/// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
- /// <param name="transcodingReasons">Optional. The transcoding reason.</param>
+ /// <param name="transcodeReasons">Optional. The transcoding reason.</param>
/// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
/// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
/// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
@@ -546,7 +546,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? enableMpegtsM2TsMode,
[FromQuery] string? videoCodec,
[FromQuery] string? subtitleCodec,
- [FromQuery] string? transcodingReasons,
+ [FromQuery] string? transcodeReasons,
[FromQuery] int? audioStreamIndex,
[FromQuery] int? videoStreamIndex,
[FromQuery] EncodingContext context,
@@ -598,7 +598,7 @@ namespace Jellyfin.Api.Controllers
EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true,
VideoCodec = videoCodec,
SubtitleCodec = subtitleCodec,
- TranscodeReasons = transcodingReasons,
+ TranscodeReasons = transcodeReasons,
AudioStreamIndex = audioStreamIndex,
VideoStreamIndex = videoStreamIndex,
Context = context,
@@ -656,7 +656,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
/// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
/// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
- /// <param name="transcodingReasons">Optional. The transcoding reason.</param>
+ /// <param name="transcodeReasons">Optional. The transcoding reason.</param>
/// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
/// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
/// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
@@ -711,7 +711,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? enableMpegtsM2TsMode,
[FromQuery] string? videoCodec,
[FromQuery] string? subtitleCodec,
- [FromQuery] string? transcodingReasons,
+ [FromQuery] string? transcodeReasons,
[FromQuery] int? audioStreamIndex,
[FromQuery] int? videoStreamIndex,
[FromQuery] EncodingContext context,
@@ -763,7 +763,7 @@ namespace Jellyfin.Api.Controllers
EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true,
VideoCodec = videoCodec,
SubtitleCodec = subtitleCodec,
- TranscodeReasons = transcodingReasons,
+ TranscodeReasons = transcodeReasons,
AudioStreamIndex = audioStreamIndex,
VideoStreamIndex = videoStreamIndex,
Context = context,
@@ -823,7 +823,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
/// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
/// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
- /// <param name="transcodingReasons">Optional. The transcoding reason.</param>
+ /// <param name="transcodeReasons">Optional. The transcoding reason.</param>
/// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
/// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
/// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
@@ -881,7 +881,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? enableMpegtsM2TsMode,
[FromQuery] string? videoCodec,
[FromQuery] string? subtitleCodec,
- [FromQuery] string? transcodingReasons,
+ [FromQuery] string? transcodeReasons,
[FromQuery] int? audioStreamIndex,
[FromQuery] int? videoStreamIndex,
[FromQuery] EncodingContext context,
@@ -933,7 +933,7 @@ namespace Jellyfin.Api.Controllers
EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true,
VideoCodec = videoCodec,
SubtitleCodec = subtitleCodec,
- TranscodeReasons = transcodingReasons,
+ TranscodeReasons = transcodeReasons,
AudioStreamIndex = audioStreamIndex,
VideoStreamIndex = videoStreamIndex,
Context = context,
@@ -994,7 +994,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
/// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
/// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
- /// <param name="transcodingReasons">Optional. The transcoding reason.</param>
+ /// <param name="transcodeReasons">Optional. The transcoding reason.</param>
/// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
/// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
/// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
@@ -1053,7 +1053,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? enableMpegtsM2TsMode,
[FromQuery] string? videoCodec,
[FromQuery] string? subtitleCodec,
- [FromQuery] string? transcodingReasons,
+ [FromQuery] string? transcodeReasons,
[FromQuery] int? audioStreamIndex,
[FromQuery] int? videoStreamIndex,
[FromQuery] EncodingContext context,
@@ -1105,7 +1105,7 @@ namespace Jellyfin.Api.Controllers
EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true,
VideoCodec = videoCodec,
SubtitleCodec = subtitleCodec,
- TranscodeReasons = transcodingReasons,
+ TranscodeReasons = transcodeReasons,
AudioStreamIndex = audioStreamIndex,
VideoStreamIndex = videoStreamIndex,
Context = context,
diff --git a/Jellyfin.Api/Controllers/VideoHlsController.cs b/Jellyfin.Api/Controllers/VideoHlsController.cs
index 2ac16de6b..7e743ee0c 100644
--- a/Jellyfin.Api/Controllers/VideoHlsController.cs
+++ b/Jellyfin.Api/Controllers/VideoHlsController.cs
@@ -153,7 +153,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
/// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
/// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
- /// <param name="transcodingReasons">Optional. The transcoding reason.</param>
+ /// <param name="transcodeReasons">Optional. The transcoding reason.</param>
/// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
/// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
/// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
@@ -211,7 +211,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? enableMpegtsM2TsMode,
[FromQuery] string? videoCodec,
[FromQuery] string? subtitleCodec,
- [FromQuery] string? transcodingReasons,
+ [FromQuery] string? transcodeReasons,
[FromQuery] int? audioStreamIndex,
[FromQuery] int? videoStreamIndex,
[FromQuery] EncodingContext context,
@@ -266,7 +266,7 @@ namespace Jellyfin.Api.Controllers
EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true,
VideoCodec = videoCodec,
SubtitleCodec = subtitleCodec,
- TranscodeReasons = transcodingReasons,
+ TranscodeReasons = transcodeReasons,
AudioStreamIndex = audioStreamIndex,
VideoStreamIndex = videoStreamIndex,
Context = context,
diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs
index 8e17b843a..d8bc9df1f 100644
--- a/Jellyfin.Api/Controllers/VideosController.cs
+++ b/Jellyfin.Api/Controllers/VideosController.cs
@@ -320,7 +320,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
/// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
/// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
- /// <param name="transcodingReasons">Optional. The transcoding reason.</param>
+ /// <param name="transcodeReasons">Optional. The transcoding reason.</param>
/// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
/// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
/// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
@@ -376,7 +376,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? enableMpegtsM2TsMode,
[FromQuery] string? videoCodec,
[FromQuery] string? subtitleCodec,
- [FromQuery] string? transcodingReasons,
+ [FromQuery] string? transcodeReasons,
[FromQuery] int? audioStreamIndex,
[FromQuery] int? videoStreamIndex,
[FromQuery] EncodingContext context,
@@ -430,7 +430,7 @@ namespace Jellyfin.Api.Controllers
EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true,
VideoCodec = videoCodec,
SubtitleCodec = subtitleCodec,
- TranscodeReasons = transcodingReasons,
+ TranscodeReasons = transcodeReasons,
AudioStreamIndex = audioStreamIndex,
VideoStreamIndex = videoStreamIndex,
Context = context,
@@ -576,7 +576,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
/// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
/// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
- /// <param name="transcodingReasons">Optional. The transcoding reason.</param>
+ /// <param name="transcodeReasons">Optional. The transcoding reason.</param>
/// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
/// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
/// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
@@ -632,7 +632,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? enableMpegtsM2TsMode,
[FromQuery] string? videoCodec,
[FromQuery] string? subtitleCodec,
- [FromQuery] string? transcodingReasons,
+ [FromQuery] string? transcodeReasons,
[FromQuery] int? audioStreamIndex,
[FromQuery] int? videoStreamIndex,
[FromQuery] EncodingContext context,
@@ -683,7 +683,7 @@ namespace Jellyfin.Api.Controllers
enableMpegtsM2TsMode,
videoCodec,
subtitleCodec,
- transcodingReasons,
+ transcodeReasons,
audioStreamIndex,
videoStreamIndex,
context,
diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs
index 45e71f16e..bf8818f8d 100644
--- a/Jellyfin.Server.Implementations/JellyfinDb.cs
+++ b/Jellyfin.Server.Implementations/JellyfinDb.cs
@@ -1,5 +1,6 @@
#pragma warning disable CS1591
+using System;
using System.Linq;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Interfaces;
@@ -140,6 +141,7 @@ namespace Jellyfin.Server.Implementations
/// <inheritdoc />
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
+ modelBuilder.SetDefaultDateTimeKind(DateTimeKind.Utc);
base.OnModelCreating(modelBuilder);
modelBuilder.HasDefaultSchema("jellyfin");
diff --git a/Jellyfin.Server.Implementations/ModelBuilderExtensions.cs b/Jellyfin.Server.Implementations/ModelBuilderExtensions.cs
new file mode 100644
index 000000000..80ad65a42
--- /dev/null
+++ b/Jellyfin.Server.Implementations/ModelBuilderExtensions.cs
@@ -0,0 +1,48 @@
+using System;
+using Jellyfin.Server.Implementations.ValueConverters;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+namespace Jellyfin.Server.Implementations
+{
+ /// <summary>
+ /// Model builder extensions.
+ /// </summary>
+ public static class ModelBuilderExtensions
+ {
+ /// <summary>
+ /// Specify value converter for the object type.
+ /// </summary>
+ /// <param name="modelBuilder">The model builder.</param>
+ /// <param name="converter">The <see cref="ValueConverter{TModel,TProvider}"/>.</param>
+ /// <typeparam name="T">The type to convert.</typeparam>
+ /// <returns>The modified <see cref="ModelBuilder"/>.</returns>
+ public static ModelBuilder UseValueConverterForType<T>(this ModelBuilder modelBuilder, ValueConverter converter)
+ {
+ var type = typeof(T);
+ foreach (var entityType in modelBuilder.Model.GetEntityTypes())
+ {
+ foreach (var property in entityType.GetProperties())
+ {
+ if (property.ClrType == type)
+ {
+ property.SetValueConverter(converter);
+ }
+ }
+ }
+
+ return modelBuilder;
+ }
+
+ /// <summary>
+ /// Specify the default <see cref="DateTimeKind"/>.
+ /// </summary>
+ /// <param name="modelBuilder">The model builder to extend.</param>
+ /// <param name="kind">The <see cref="DateTimeKind"/> to specify.</param>
+ public static void SetDefaultDateTimeKind(this ModelBuilder modelBuilder, DateTimeKind kind)
+ {
+ modelBuilder.UseValueConverterForType<DateTime>(new DateTimeKindValueConverter(kind));
+ modelBuilder.UseValueConverterForType<DateTime?>(new DateTimeKindValueConverter(kind));
+ }
+ }
+} \ No newline at end of file
diff --git a/Jellyfin.Server.Implementations/ValueConverters/DateTimeKindValueConverter.cs b/Jellyfin.Server.Implementations/ValueConverters/DateTimeKindValueConverter.cs
new file mode 100644
index 000000000..8a510898b
--- /dev/null
+++ b/Jellyfin.Server.Implementations/ValueConverters/DateTimeKindValueConverter.cs
@@ -0,0 +1,21 @@
+using System;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+namespace Jellyfin.Server.Implementations.ValueConverters
+{
+ /// <summary>
+ /// ValueConverter to specify kind.
+ /// </summary>
+ public class DateTimeKindValueConverter : ValueConverter<DateTime, DateTime>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DateTimeKindValueConverter"/> class.
+ /// </summary>
+ /// <param name="kind">The kind to specify.</param>
+ /// <param name="mappingHints">The mapping hints.</param>
+ public DateTimeKindValueConverter(DateTimeKind kind, ConverterMappingHints mappingHints = null)
+ : base(v => v.ToUniversalTime(), v => DateTime.SpecifyKind(v, kind), mappingHints)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs
index 4ffcb940f..f144dd12e 100644
--- a/Jellyfin.Server/Startup.cs
+++ b/Jellyfin.Server/Startup.cs
@@ -81,7 +81,7 @@ namespace Jellyfin.Server
{
c.DefaultRequestHeaders.UserAgent.Add(productHeader);
c.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue($"({_serverApplicationHost.ApplicationUserAgentAddress})"));
- c.DefaultRequestHeaders.Accept.Add(acceptJsonHeader);
+ c.DefaultRequestHeaders.Accept.Add(acceptXmlHeader);
c.DefaultRequestHeaders.Accept.Add(acceptAnyHeader);
})
.ConfigurePrimaryHttpMessageHandler(x => new DefaultHttpClientHandler());
diff --git a/MediaBrowser.Common/Json/Converters/JsonDateTimeIso8601Converter.cs b/MediaBrowser.Common/Json/Converters/JsonDateTimeIso8601Converter.cs
deleted file mode 100644
index 63b344a9d..000000000
--- a/MediaBrowser.Common/Json/Converters/JsonDateTimeIso8601Converter.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System;
-using System.Globalization;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace MediaBrowser.Common.Json.Converters
-{
- /// <summary>
- /// Returns an ISO8601 formatted datetime.
- /// </summary>
- /// <remarks>
- /// Used for legacy compatibility.
- /// </remarks>
- public class JsonDateTimeIso8601Converter : JsonConverter<DateTime>
- {
- /// <inheritdoc />
- public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
- => reader.GetDateTime();
-
- /// <inheritdoc />
- public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
- => writer.WriteStringValue(value.ToString("O", CultureInfo.InvariantCulture));
- }
-}
diff --git a/MediaBrowser.Common/Json/Converters/JsonGuidConverter.cs b/MediaBrowser.Common/Json/Converters/JsonGuidConverter.cs
index 54325a02b..52e08d071 100644
--- a/MediaBrowser.Common/Json/Converters/JsonGuidConverter.cs
+++ b/MediaBrowser.Common/Json/Converters/JsonGuidConverter.cs
@@ -11,7 +11,11 @@ namespace MediaBrowser.Common.Json.Converters
{
/// <inheritdoc />
public override Guid Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
- => new Guid(reader.GetString());
+ {
+ var guidStr = reader.GetString();
+
+ return guidStr == null ? Guid.Empty : new Guid(guidStr);
+ }
/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, Guid value, JsonSerializerOptions options)
diff --git a/MediaBrowser.Common/Json/JsonDefaults.cs b/MediaBrowser.Common/Json/JsonDefaults.cs
index 1aaf4a43b..c5050a21d 100644
--- a/MediaBrowser.Common/Json/JsonDefaults.cs
+++ b/MediaBrowser.Common/Json/JsonDefaults.cs
@@ -43,7 +43,6 @@ namespace MediaBrowser.Common.Json
options.Converters.Add(new JsonVersionConverter());
options.Converters.Add(new JsonStringEnumConverter());
options.Converters.Add(new JsonNullableStructConverterFactory());
- options.Converters.Add(new JsonDateTimeIso8601Converter());
return options;
}
diff --git a/MediaBrowser.Common/Plugins/LocalPlugin.cs b/MediaBrowser.Common/Plugins/LocalPlugin.cs
index 7927c663d..c97e75a3b 100644
--- a/MediaBrowser.Common/Plugins/LocalPlugin.cs
+++ b/MediaBrowser.Common/Plugins/LocalPlugin.cs
@@ -1,11 +1,11 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Globalization;
namespace MediaBrowser.Common.Plugins
{
/// <summary>
- /// Local plugin struct.
+ /// Local plugin class.
/// </summary>
public class LocalPlugin : IEquatable<LocalPlugin>
{
@@ -106,6 +106,12 @@ namespace MediaBrowser.Common.Plugins
/// <inheritdoc />
public bool Equals(LocalPlugin other)
{
+ // Do not use == or != for comparison as this class overrides the operators.
+ if (object.ReferenceEquals(other, null))
+ {
+ return false;
+ }
+
return Name.Equals(other.Name, StringComparison.OrdinalIgnoreCase)
&& Id.Equals(other.Id);
}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 3baa59cae..9c6bbb916 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -494,7 +494,6 @@ namespace MediaBrowser.Controller.MediaEncoding
.Append(encodingOptions.VaapiDevice)
.Append(' ')
.Append("-init_hw_device opencl=ocl@va ")
- .Append("-hwaccel vaapi ")
.Append("-hwaccel_device va ")
.Append("-hwaccel_output_format vaapi ")
.Append("-filter_hw_device ocl ");
diff --git a/MediaBrowser.Controller/Net/AuthorizationInfo.cs b/MediaBrowser.Controller/Net/AuthorizationInfo.cs
index 0194c596f..93573e08e 100644
--- a/MediaBrowser.Controller/Net/AuthorizationInfo.cs
+++ b/MediaBrowser.Controller/Net/AuthorizationInfo.cs
@@ -58,5 +58,10 @@ namespace MediaBrowser.Controller.Net
/// Gets or sets a value indicating whether the token is authenticated.
/// </summary>
public bool IsAuthenticated { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the request has a token.
+ /// </summary>
+ public bool HasToken { get; set; }
}
}
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index 5f60c09ae..753da46a6 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -36,7 +36,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <summary>
/// The default image extraction timeout in milliseconds.
/// </summary>
- internal const int DefaultImageExtractionTimeout = 5000;
+ internal const int DefaultImageExtractionTimeout = 10000;
/// <summary>
/// The us culture.
@@ -553,12 +553,21 @@ namespace MediaBrowser.MediaEncoding.Encoder
var mapArg = imageStreamIndex.HasValue ? (" -map 0:v:" + imageStreamIndex.Value.ToString(CultureInfo.InvariantCulture)) : string.Empty;
- var enableThumbnail = !new List<string> { "wtv" }.Contains(container ?? string.Empty, StringComparer.OrdinalIgnoreCase);
// Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case.
- var thumbnail = enableThumbnail ? ",thumbnail=24" : string.Empty;
+ var enableThumbnail = useIFrame && !string.Equals("wtv", container, StringComparison.OrdinalIgnoreCase);
+ if (enableThumbnail)
+ {
+ if (string.IsNullOrEmpty(vf))
+ {
+ vf = "-vf thumbnail=24";
+ }
+ else
+ {
+ vf += ",thumbnail=24";
+ }
+ }
- var args = useIFrame ? string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads {5} -v quiet -vframes 1 {2}{4} -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, thumbnail, threads) :
- string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads {4} -v quiet -vframes 1 {2} -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, threads);
+ var args = string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads {4} -v quiet -vframes 1 {2} -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, threads);
var probeSizeArgument = EncodingHelper.GetProbeSizeArgument(1);
var analyzeDurationArgument = EncodingHelper.GetAnalyzeDurationArgument(1);
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index 97d61441c..bd026bce1 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -778,7 +778,11 @@ namespace MediaBrowser.MediaEncoding.Probing
}
}
- if (bitrate == 0 && formatInfo != null && !string.IsNullOrEmpty(formatInfo.BitRate) && stream.Type == MediaStreamType.Video)
+ // The bitrate info of FLAC musics and some videos is included in formatInfo.
+ if (bitrate == 0
+ && formatInfo != null
+ && !string.IsNullOrEmpty(formatInfo.BitRate)
+ && (stream.Type == MediaStreamType.Video || (isAudio && stream.Type == MediaStreamType.Audio)))
{
// If the stream info doesn't have a bitrate get the value from the media format info
if (int.TryParse(formatInfo.BitRate, NumberStyles.Any, _usCulture, out var value))
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs
index e3a1decb8..293087da7 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs
@@ -103,6 +103,6 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
/// <inheritdoc />
public bool Supports(BaseItem item)
- => Plugin.Instance.Configuration.Enable && item is MusicAlbum;
+ => item is MusicAlbum;
}
}
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs
index 6536b303f..97bba10ba 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs
@@ -56,13 +56,6 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
public async Task<MetadataResult<MusicAlbum>> GetMetadata(AlbumInfo info, CancellationToken cancellationToken)
{
var result = new MetadataResult<MusicAlbum>();
-
- // TODO maybe remove when artist metadata can be disabled
- if (!Plugin.Instance.Configuration.Enable)
- {
- return result;
- }
-
var id = info.GetReleaseGroupId();
if (!string.IsNullOrWhiteSpace(id))
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs
index 54851c4d0..d250acfa8 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs
@@ -144,6 +144,6 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
/// <inheritdoc />
public bool Supports(BaseItem item)
- => Plugin.Instance.Configuration.Enable && item is MusicArtist;
+ => item is MusicArtist;
}
}
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs
index 85c92fa7b..a2a03e1f9 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs
@@ -57,13 +57,6 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
public async Task<MetadataResult<MusicArtist>> GetMetadata(ArtistInfo info, CancellationToken cancellationToken)
{
var result = new MetadataResult<MusicArtist>();
-
- // TODO maybe remove when artist metadata can be disabled
- if (!Plugin.Instance.Configuration.Enable)
- {
- return result;
- }
-
var id = info.GetMusicBrainzArtistId();
if (!string.IsNullOrWhiteSpace(id))
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/Configuration/PluginConfiguration.cs b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/PluginConfiguration.cs
index 9657a290f..664474dcd 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/Configuration/PluginConfiguration.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/PluginConfiguration.cs
@@ -6,8 +6,6 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
{
public class PluginConfiguration : BasePluginConfiguration
{
- public bool Enable { get; set; }
-
public bool ReplaceAlbumName { get; set; }
}
}
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html
index 82f26a8f2..eab252005 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html
+++ b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html
@@ -9,10 +9,6 @@
<div class="content-primary">
<form class="configForm">
<label class="checkboxContainer">
- <input is="emby-checkbox" type="checkbox" id="enable" />
- <span>Enable this provider for metadata searches on artists and albums.</span>
- </label>
- <label class="checkboxContainer">
<input is="emby-checkbox" type="checkbox" id="replaceAlbumName" />
<span>When an album is found during a metadata search, replace the name with the value on the server.</span>
</label>
@@ -32,9 +28,8 @@
.addEventListener('pageshow', function () {
Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) {
- document.querySelector('#enable').checked = config.Enable;
document.querySelector('#replaceAlbumName').checked = config.ReplaceAlbumName;
-
+
Dashboard.hideLoadingMsg();
});
});
@@ -42,14 +37,13 @@
document.querySelector('.configForm')
.addEventListener('submit', function (e) {
Dashboard.showLoadingMsg();
-
+
ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) {
- config.Enable = document.querySelector('#enable').checked;
config.ReplaceAlbumName = document.querySelector('#replaceAlbumName').checked;
-
+
ApiClient.updatePluginConfiguration(PluginConfig.pluginId, config).then(Dashboard.processPluginConfigurationUpdateResult);
});
-
+
e.preventDefault();
return false;
});
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs
index dc755b600..ce9392402 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs
@@ -25,12 +25,6 @@ namespace MediaBrowser.Providers.Music
/// <inheritdoc />
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken)
{
- // TODO maybe remove when artist metadata can be disabled
- if (!Plugin.Instance.Configuration.Enable)
- {
- return Enumerable.Empty<RemoteSearchResult>();
- }
-
var musicBrainzId = searchInfo.GetMusicBrainzArtistId();
if (!string.IsNullOrWhiteSpace(musicBrainzId))
@@ -236,12 +230,6 @@ namespace MediaBrowser.Providers.Music
Item = new MusicArtist()
};
- // TODO maybe remove when artist metadata can be disabled
- if (!Plugin.Instance.Configuration.Enable)
- {
- return result;
- }
-
var musicBrainzId = id.GetMusicBrainzArtistId();
if (string.IsNullOrWhiteSpace(musicBrainzId))
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs
index 980da9a01..0cec9e359 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs
@@ -43,8 +43,6 @@ namespace MediaBrowser.Providers.Plugins.MusicBrainz
}
}
- public bool Enable { get; set; }
-
public bool ReplaceArtistName { get; set; }
}
}
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html
index 1945e6cb4..6f1296bb7 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html
@@ -17,10 +17,6 @@
<div class="fieldDescription">Span of time between requests in milliseconds. The official server is limited to one request every two seconds.</div>
</div>
<label class="checkboxContainer">
- <input is="emby-checkbox" type="checkbox" id="enable" />
- <span>Enable this provider for metadata searches on artists and albums.</span>
- </label>
- <label class="checkboxContainer">
<input is="emby-checkbox" type="checkbox" id="replaceArtistName" />
<span>When an artist is found during a metadata search, replace the artist name with the value on the server.</span>
</label>
@@ -46,7 +42,7 @@
bubbles: true,
cancelable: false
}));
-
+
var rateLimit = document.querySelector('#rateLimit');
rateLimit.value = config.RateLimit;
rateLimit.dispatchEvent(new Event('change', {
@@ -54,26 +50,24 @@
cancelable: false
}));
- document.querySelector('#enable').checked = config.Enable;
document.querySelector('#replaceArtistName').checked = config.ReplaceArtistName;
Dashboard.hideLoadingMsg();
});
});
-
+
document.querySelector('.musicBrainzConfigForm')
.addEventListener('submit', function (e) {
Dashboard.showLoadingMsg();
-
+
ApiClient.getPluginConfiguration(MusicBrainzPluginConfig.uniquePluginId).then(function (config) {
config.Server = document.querySelector('#server').value;
config.RateLimit = document.querySelector('#rateLimit').value;
- config.Enable = document.querySelector('#enable').checked;
config.ReplaceArtistName = document.querySelector('#replaceArtistName').checked;
-
+
ApiClient.updatePluginConfiguration(MusicBrainzPluginConfig.uniquePluginId, config).then(Dashboard.processPluginConfigurationUpdateResult);
});
-
+
e.preventDefault();
return false;
});
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
index 8951cb62e..ef7933b1a 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
@@ -78,12 +78,6 @@ namespace MediaBrowser.Providers.Music
/// <inheritdoc />
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(AlbumInfo searchInfo, CancellationToken cancellationToken)
{
- // TODO maybe remove when artist metadata can be disabled
- if (!Plugin.Instance.Configuration.Enable)
- {
- return Enumerable.Empty<RemoteSearchResult>();
- }
-
var releaseId = searchInfo.GetReleaseId();
var releaseGroupId = searchInfo.GetReleaseGroupId();
@@ -194,12 +188,6 @@ namespace MediaBrowser.Providers.Music
Item = new MusicAlbum()
};
- // TODO maybe remove when artist metadata can be disabled
- if (!Plugin.Instance.Configuration.Enable)
- {
- return result;
- }
-
// If we have a release group Id but not a release Id...
if (string.IsNullOrWhiteSpace(releaseId) && !string.IsNullOrWhiteSpace(releaseGroupId))
{
diff --git a/MediaBrowser.sln b/MediaBrowser.sln
index cb204137b..5a807372d 100644
--- a/MediaBrowser.sln
+++ b/MediaBrowser.sln
@@ -1,4 +1,4 @@
-Microsoft Visual Studio Solution File, Format Version 12.00
+Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30503.244
MinimumVisualStudioVersion = 10.0.40219.1
@@ -70,6 +70,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Networking", "Jell
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Networking.Tests", "tests\Jellyfin.Networking.Tests\NetworkTesting\Jellyfin.Networking.Tests.csproj", "{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Dlna.Tests", "tests\Jellyfin.Dlna.Tests\Jellyfin.Dlna.Tests.csproj", "{B8AE4B9D-E8D3-4B03-A95E-7FD8CECECC50}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -188,6 +190,10 @@ Global
{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B8AE4B9D-E8D3-4B03-A95E-7FD8CECECC50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B8AE4B9D-E8D3-4B03-A95E-7FD8CECECC50}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B8AE4B9D-E8D3-4B03-A95E-7FD8CECECC50}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B8AE4B9D-E8D3-4B03-A95E-7FD8CECECC50}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -200,6 +206,7 @@ Global
{2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
{462584F7-5023-4019-9EAC-B98CA458C0A0} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
+ {B8AE4B9D-E8D3-4B03-A95E-7FD8CECECC50} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3448830C-EBDC-426C-85CD-7BBB9651A7FE}
diff --git a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs
index 90c491666..ee20cc573 100644
--- a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs
+++ b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs
@@ -69,7 +69,7 @@ namespace Jellyfin.Api.Tests.Auth
}
[Fact]
- public async Task HandleAuthenticateAsyncShouldFailOnAuthenticationException()
+ public async Task HandleAuthenticateAsyncShouldProvideNoResultOnAuthenticationException()
{
var errorMessage = _fixture.Create<string>();
@@ -81,7 +81,7 @@ namespace Jellyfin.Api.Tests.Auth
var authenticateResult = await _sut.AuthenticateAsync();
Assert.False(authenticateResult.Succeeded);
- Assert.Equal(errorMessage, authenticateResult.Failure?.Message);
+ Assert.True(authenticateResult.None);
}
[Fact]
diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
index 14eed30e0..7c552ec06 100644
--- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
+++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
@@ -22,7 +22,7 @@
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="coverlet.collector" Version="1.3.0" />
- <PackageReference Include="Moq" Version="4.15.1" />
+ <PackageReference Include="Moq" Version="4.15.2" />
</ItemGroup>
<!-- Code Analyzers -->
diff --git a/tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs
index d9e66d677..3c94db491 100644
--- a/tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs
+++ b/tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs
@@ -5,28 +5,48 @@ using Xunit;
namespace Jellyfin.Common.Tests.Extensions
{
- public static class JsonGuidConverterTests
+ public class JsonGuidConverterTests
{
+ private readonly JsonSerializerOptions _options;
+
+ public JsonGuidConverterTests()
+ {
+ _options = new JsonSerializerOptions();
+ _options.Converters.Add(new JsonGuidConverter());
+ }
+
[Fact]
- public static void Deserialize_Valid_Success()
+ public void Deserialize_Valid_Success()
{
- var options = new JsonSerializerOptions();
- options.Converters.Add(new JsonGuidConverter());
- Guid value = JsonSerializer.Deserialize<Guid>(@"""a852a27afe324084ae66db579ee3ee18""", options);
+ Guid value = JsonSerializer.Deserialize<Guid>(@"""a852a27afe324084ae66db579ee3ee18""", _options);
Assert.Equal(new Guid("a852a27afe324084ae66db579ee3ee18"), value);
+ }
- value = JsonSerializer.Deserialize<Guid>(@"""e9b2dcaa-529c-426e-9433-5e9981f27f2e""", options);
+ [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 static void Roundtrip_Valid_Success()
+ public void Roundtrip_Valid_Success()
{
- var options = new JsonSerializerOptions();
- options.Converters.Add(new JsonGuidConverter());
Guid guid = new Guid("a852a27afe324084ae66db579ee3ee18");
- string value = JsonSerializer.Serialize(guid, options);
- Assert.Equal(guid, JsonSerializer.Deserialize<Guid>(value, options));
+ string value = JsonSerializer.Serialize(guid, _options);
+ Assert.Equal(guid, JsonSerializer.Deserialize<Guid>(value, _options));
+ }
+
+ [Fact]
+ public void Deserialize_Null_EmptyGuid()
+ {
+ Assert.Equal(Guid.Empty, JsonSerializer.Deserialize<Guid>("null", _options));
+ }
+
+ [Fact]
+ public void Serialize_EmptyGuid_Null()
+ {
+ Assert.Equal("null", JsonSerializer.Serialize(Guid.Empty, _options));
}
}
}
diff --git a/tests/Jellyfin.Dlna.Tests/GetUuidTests.cs b/tests/Jellyfin.Dlna.Tests/GetUuidTests.cs
new file mode 100644
index 000000000..7655e3f7c
--- /dev/null
+++ b/tests/Jellyfin.Dlna.Tests/GetUuidTests.cs
@@ -0,0 +1,17 @@
+using Emby.Dlna.PlayTo;
+using Xunit;
+
+namespace Jellyfin.Dlna.Tests
+{
+ public static class GetUuidTests
+ {
+ [Theory]
+ [InlineData("uuid:fc4ec57e-b051-11db-88f8-0060085db3f6::urn:schemas-upnp-org:device:WANDevice:1", "fc4ec57e-b051-11db-88f8-0060085db3f6")]
+ [InlineData("uuid:IGD{8c80f73f-4ba0-45fa-835d-042505d052be}000000000000", "8c80f73f-4ba0-45fa-835d-042505d052be")]
+ [InlineData("uuid:IGD{8c80f73f-4ba0-45fa-835d-042505d052be}000000000000::urn:schemas-upnp-org:device:InternetGatewayDevice:1", "8c80f73f-4ba0-45fa-835d-042505d052be")]
+ [InlineData("uuid:00000000-0000-0000-0000-000000000000::upnp:rootdevice", "00000000-0000-0000-0000-000000000000")]
+ [InlineData("uuid:fc4ec57e-b051-11db-88f8-0060085db3f6", "fc4ec57e-b051-11db-88f8-0060085db3f6")]
+ public static void GetUuid_Valid_Success(string usn, string uuid)
+ => Assert.Equal(uuid, PlayToManager.GetUuid(usn));
+ }
+}
diff --git a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj
new file mode 100644
index 000000000..f91db6744
--- /dev/null
+++ b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj
@@ -0,0 +1,33 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net5.0</TargetFramework>
+ <IsPackable>false</IsPackable>
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <Nullable>enable</Nullable>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" />
+ <PackageReference Include="xunit" Version="2.4.1" />
+ <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
+ <PackageReference Include="coverlet.collector" Version="1.3.0" />
+ </ItemGroup>
+
+ <!-- Code Analyzers -->
+ <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
+ <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
+ <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
+ <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="../../Emby.Dlna/Emby.Dlna.csproj" />
+ </ItemGroup>
+
+ <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+
+</Project>
diff --git a/tests/Jellyfin.Networking.Tests/NetworkTesting/Jellyfin.Networking.Tests.csproj b/tests/Jellyfin.Networking.Tests/NetworkTesting/Jellyfin.Networking.Tests.csproj
index 703d43210..48b0b4c7d 100644
--- a/tests/Jellyfin.Networking.Tests/NetworkTesting/Jellyfin.Networking.Tests.csproj
+++ b/tests/Jellyfin.Networking.Tests/NetworkTesting/Jellyfin.Networking.Tests.csproj
@@ -17,7 +17,7 @@
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Include="coverlet.collector" Version="1.3.0" />
- <PackageReference Include="Moq" Version="4.14.5" />
+ <PackageReference Include="Moq" Version="4.15.2" />
</ItemGroup>
<!-- Code Analyzers-->
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 547f80ed9..fffbc6212 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
+++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
@@ -17,7 +17,7 @@
<PackageReference Include="AutoFixture" Version="4.14.0" />
<PackageReference Include="AutoFixture.AutoMoq" Version="4.14.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" />
- <PackageReference Include="Moq" Version="4.15.1" />
+ <PackageReference Include="Moq" Version="4.15.2" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="coverlet.collector" Version="1.3.0" />