From 58ed50c9d01c4e4e2c38d62025e1820e43b538d3 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Wed, 15 Feb 2023 21:58:49 -0500 Subject: [PATCH 01/41] Catch Exception when disposing connection --- MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs index fc9ea37d1e..48ee78a7cc 100644 --- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs +++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs @@ -232,6 +232,11 @@ namespace MediaBrowser.Controller.Net // TODO Investigate and properly fix. Logger.LogError(ex, "Object Disposed"); } + catch (Exception ex) + { + // TODO Investigate and properly fix. + Logger.LogError(ex, "Object Disposed Exception"); + } lock (_activeConnections) { From 62204dce00a41a866b89acaf17f2e689c10b8326 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Wed, 15 Feb 2023 22:06:14 -0500 Subject: [PATCH 02/41] add contributor --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 20ea7a4ab7..c9430b235f 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -163,6 +163,7 @@ - [vgambier](https://github.com/vgambier) - [MinecraftPlaye](https://github.com/MinecraftPlaye) - [RealGreenDragon](https://github.com/RealGreenDragon) + - [ipitio](https://github.com/ipitio) # Emby Contributors From ed2280a0605c7e971d151b9b2239d332ee204579 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sun, 9 Oct 2022 22:56:23 +0200 Subject: [PATCH 03/41] Overhaul content ratings --- .../Localization/LocalizationManager.cs | 18 ++++- .../Localization/Ratings/0-prefer.csv | 11 +++ .../Localization/Ratings/au.csv | 20 +++-- .../Localization/Ratings/be.csv | 17 +++-- .../Localization/Ratings/br.csv | 14 ++-- .../Localization/Ratings/ca.csv | 26 +++++-- .../Localization/Ratings/co.csv | 15 ++-- .../Localization/Ratings/de.csv | 22 +++--- .../Localization/Ratings/dk.csv | 11 ++- .../Localization/Ratings/es.csv | 30 ++++++-- .../Localization/Ratings/fi.csv | 20 ++--- .../Localization/Ratings/fr.csv | 17 +++-- .../Localization/Ratings/gb.csv | 29 ++++++-- .../Localization/Ratings/ie.csv | 15 ++-- .../Localization/Ratings/jp.csv | 15 +++- .../Localization/Ratings/kz.csv | 13 ++-- .../Localization/Ratings/mx.csv | 12 +-- .../Localization/Ratings/nl.csv | 14 ++-- .../Localization/Ratings/no.csv | 15 ++-- .../Localization/Ratings/nz.csv | 26 ++++--- .../Localization/Ratings/ro.csv | 7 +- .../Localization/Ratings/ru.csv | 11 +-- .../Localization/Ratings/se.csv | 15 ++-- .../Localization/Ratings/uk.csv | 29 ++++++-- .../Localization/Ratings/us.csv | 73 +++++++++++++------ .../Localization/LocalizationManagerTests.cs | 25 ++++--- 26 files changed, 346 insertions(+), 174 deletions(-) create mode 100644 Emby.Server.Implementations/Localization/Ratings/0-prefer.csv diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index b418c7877e..5e22e78867 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -3,6 +3,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.IO; +using System.Linq; using System.Reflection; using System.Text.Json; using System.Threading.Tasks; @@ -25,7 +26,7 @@ namespace Emby.Server.Implementations.Localization private const string CulturesPath = "Emby.Server.Implementations.Localization.iso6392.txt"; private const string CountriesPath = "Emby.Server.Implementations.Localization.countries.json"; private static readonly Assembly _assembly = typeof(LocalizationManager).Assembly; - private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" }; + private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated", "nr" }; private readonly IServerConfigurationManager _configurationManager; private readonly ILogger _logger; @@ -194,6 +195,7 @@ namespace Emby.Server.Implementations.Localization { var countryCode = _configurationManager.Configuration.MetadataCountryCode; + // Fall back to US ratings if no country code is specified or country code does not exist. if (string.IsNullOrEmpty(countryCode)) { countryCode = "us"; @@ -221,12 +223,14 @@ namespace Emby.Server.Implementations.Localization { ArgumentException.ThrowIfNullOrEmpty(rating); + // Handle unrated content if (_unratedValues.Contains(rating.AsSpan(), StringComparison.OrdinalIgnoreCase)) { return null; } // Fairly common for some users to have "Rated R" in their rating field + rating = rating.Replace("Rated :", string.Empty, StringComparison.OrdinalIgnoreCase); rating = rating.Replace("Rated ", string.Empty, StringComparison.OrdinalIgnoreCase); var ratingsDictionary = GetParentalRatingsDictionary(); @@ -257,6 +261,18 @@ namespace Emby.Server.Implementations.Localization } } + // Remove prefix country code to handle "DE-18" + index = rating.IndexOf('-', StringComparison.Ordinal); + if (index != -1) + { + var trimmedRating = rating.AsSpan(index).TrimStart('-').Trim(); + + if (!trimmedRating.IsEmpty) + { + return GetRatingLevel(trimmedRating.ToString()); + } + } + // TODO: Further improve by normalizing out all spaces and dashes return null; } diff --git a/Emby.Server.Implementations/Localization/Ratings/0-prefer.csv b/Emby.Server.Implementations/Localization/Ratings/0-prefer.csv new file mode 100644 index 0000000000..36886ba760 --- /dev/null +++ b/Emby.Server.Implementations/Localization/Ratings/0-prefer.csv @@ -0,0 +1,11 @@ +E,0 +EC,0 +T,7 +M,18 +AO,18 +UR,18 +RP,18 +X,1000 +XX,1000 +XXX,1000 +XXXX,1000 diff --git a/Emby.Server.Implementations/Localization/Ratings/au.csv b/Emby.Server.Implementations/Localization/Ratings/au.csv index 11f4ed94cd..4ab808ae9a 100644 --- a/Emby.Server.Implementations/Localization/Ratings/au.csv +++ b/Emby.Server.Implementations/Localization/Ratings/au.csv @@ -1,7 +1,13 @@ -AU-G,1 -AU-PG,5 -AU-M,6 -AU-MA15+,7 -AU-R18+,9 -AU-X18+,10 -AU-RC,11 +Exempt,0 +G,0 +7+,7 +M,15 +MA,15 +MA15+,15 +PG,16 +16+,16 +R,18 +R18+,18 +X18+,18 +18+,18 +X,1000 diff --git a/Emby.Server.Implementations/Localization/Ratings/be.csv b/Emby.Server.Implementations/Localization/Ratings/be.csv index d3937caf78..5588f63b45 100644 --- a/Emby.Server.Implementations/Localization/Ratings/be.csv +++ b/Emby.Server.Implementations/Localization/Ratings/be.csv @@ -1,6 +1,11 @@ -BE-AL,1 -BE-MG6,2 -BE-6,3 -BE-9,5 -BE-12,6 -BE-16,8 +AL,0 +KT,0 +TOUS,0 +MG6,6 +6,6 +9,9 +KNT,12 +12,12 +BE_14,14 +16,16 +18,18 diff --git a/Emby.Server.Implementations/Localization/Ratings/br.csv b/Emby.Server.Implementations/Localization/Ratings/br.csv index e5edaf62cf..5ec1eb2627 100644 --- a/Emby.Server.Implementations/Localization/Ratings/br.csv +++ b/Emby.Server.Implementations/Localization/Ratings/br.csv @@ -1,6 +1,8 @@ -BR-L,1 -BR-10,5 -BR-12,7 -BR-14,8 -BR-16,8 -BR-18,9 +Livre,0 +L,0 +ER,9 +10,10 +12,12 +14,14 +16,16 +18,18 diff --git a/Emby.Server.Implementations/Localization/Ratings/ca.csv b/Emby.Server.Implementations/Localization/Ratings/ca.csv index 5aef0580f8..336ee28067 100644 --- a/Emby.Server.Implementations/Localization/Ratings/ca.csv +++ b/Emby.Server.Implementations/Localization/Ratings/ca.csv @@ -1,6 +1,20 @@ -CA-G,1 -CA-PG,5 -CA-14A,7 -CA-A,8 -CA-18A,9 -CA-R,10 +E,0 +G,0 +TV-Y,0 +TV-G,0 +TV-Y7,7 +TV-Y7-FV,7 +PG,9 +TV-PG,9 +PG-13,13 +13+,13 +TV-14,14 +14A,14 +16+,16 +NC-17,17 +R,18 +TV-MA,18 +18A,18 +18+,18 +A,1000 +Prohibited,1001 diff --git a/Emby.Server.Implementations/Localization/Ratings/co.csv b/Emby.Server.Implementations/Localization/Ratings/co.csv index 9684fa0524..e1e96c5909 100644 --- a/Emby.Server.Implementations/Localization/Ratings/co.csv +++ b/Emby.Server.Implementations/Localization/Ratings/co.csv @@ -1,8 +1,7 @@ -CO-T,1 -CO-7,5 -CO-12,7 -CO-15,8 -CO-18,10 -CO-X,100 -CO-BANNED,15 -CO-E,15 +T,0 +7,7 +12,12 +15,15 +18,18 +X,1000 +Prohibited,1001 diff --git a/Emby.Server.Implementations/Localization/Ratings/de.csv b/Emby.Server.Implementations/Localization/Ratings/de.csv index f944a140d0..d633a5dab7 100644 --- a/Emby.Server.Implementations/Localization/Ratings/de.csv +++ b/Emby.Server.Implementations/Localization/Ratings/de.csv @@ -1,10 +1,12 @@ -DE-0,1 -FSK-0,1 -DE-6,5 -FSK-6,5 -DE-12,7 -FSK-12,7 -DE-16,8 -FSK-16,8 -DE-18,9 -FSK-18,9 +Educational,0 +Infoprogramm,0 +FSK-0,0 +0,0 +FSK-6,6 +6,6 +FSK-12,12 +12,12 +FSK-16,16 +16,16 +FSK-18,18 +18,18 diff --git a/Emby.Server.Implementations/Localization/Ratings/dk.csv b/Emby.Server.Implementations/Localization/Ratings/dk.csv index 5364ae1f27..4ef63b2eac 100644 --- a/Emby.Server.Implementations/Localization/Ratings/dk.csv +++ b/Emby.Server.Implementations/Localization/Ratings/dk.csv @@ -1,4 +1,7 @@ -DA-A,1 -DA-7,5 -DA-11,6 -DA-15,8 +F,0 +A,0 +7,7 +11,11 +12,12 +15,15 +16,16 diff --git a/Emby.Server.Implementations/Localization/Ratings/es.csv b/Emby.Server.Implementations/Localization/Ratings/es.csv index 887d91ba63..0bc1d3f7d0 100644 --- a/Emby.Server.Implementations/Localization/Ratings/es.csv +++ b/Emby.Server.Implementations/Localization/Ratings/es.csv @@ -1,6 +1,24 @@ -ES-A,1 -ES-APTA,1 -ES-7,3 -ES-12,6 -ES-16,8 -ES-18,11 +A,0 +A/fig,0 +A/i,0 +A/fig/i,0 +APTA,0 +TP,0 +0+,0 +6+,6 +7/fig,7 +7/i,7 +7/i/fig,7 +7,7 +9+,9 +10,10 +12,12 +12/fig,12 +13,13 +14,14 +16,16 +16/fig,16 +18,18 +18/fig,18 +X,1000 +Banned,1001 diff --git a/Emby.Server.Implementations/Localization/Ratings/fi.csv b/Emby.Server.Implementations/Localization/Ratings/fi.csv index 782785890f..7ff92f259b 100644 --- a/Emby.Server.Implementations/Localization/Ratings/fi.csv +++ b/Emby.Server.Implementations/Localization/Ratings/fi.csv @@ -1,10 +1,10 @@ -FI-S,1 -FI-T,1 -FI-7,4 -FI-12,5 -FI-16,8 -FI-18,9 -FI-K7,4 -FI-K12,5 -FI-K16,8 -FI-K18,9 +S,0 +T,0 +K7,7 +7,7 +K12,12 +12,12 +K16,16 +16,16 +K18,18 +18,18 diff --git a/Emby.Server.Implementations/Localization/Ratings/fr.csv b/Emby.Server.Implementations/Localization/Ratings/fr.csv index f586a3fa91..774a705891 100644 --- a/Emby.Server.Implementations/Localization/Ratings/fr.csv +++ b/Emby.Server.Implementations/Localization/Ratings/fr.csv @@ -1,5 +1,12 @@ -FR-U,1 -FR-10,5 -FR-12,7 -FR-16,9 -FR-18,10 +Public Averti,0 +Tous Publics,0 +U,0 +0+,0 +6+,6 +9+,9 +10,10 +12,12 +14+,14 +16,16 +18,18 +X,1000 diff --git a/Emby.Server.Implementations/Localization/Ratings/gb.csv b/Emby.Server.Implementations/Localization/Ratings/gb.csv index c1f7d04529..75b1c20589 100644 --- a/Emby.Server.Implementations/Localization/Ratings/gb.csv +++ b/Emby.Server.Implementations/Localization/Ratings/gb.csv @@ -1,7 +1,22 @@ -GB-U,1 -GB-PG,5 -GB-12,6 -GB-12A,7 -GB-15,8 -GB-18,9 -GB-R18,15 +All,0 +E,0 +G,0 +U,0 +0+,0 +6+,6 +7+,7 +PG,8 +9+,9 +12,12 +12+,12 +12A,12 +Teen,13 +13+,13 +14+,14 +15,15 +16,16 +Caution,18 +18,18 +Mature,1000 +Adult,1000 +R18,1000 diff --git a/Emby.Server.Implementations/Localization/Ratings/ie.csv b/Emby.Server.Implementations/Localization/Ratings/ie.csv index e42be5cd49..6ef2e50128 100644 --- a/Emby.Server.Implementations/Localization/Ratings/ie.csv +++ b/Emby.Server.Implementations/Localization/Ratings/ie.csv @@ -1,6 +1,9 @@ -IE-G,1 -IE-PG,5 -IE-12A,7 -IE-15A,8 -IE-16,9 -IE-18,10 +G,4 +PG,12 +12,12 +12A,12 +12PG,12 +15,15 +15A,15 +16,16 +18,18 diff --git a/Emby.Server.Implementations/Localization/Ratings/jp.csv b/Emby.Server.Implementations/Localization/Ratings/jp.csv index a8fc2d1431..bfb5fdaae9 100644 --- a/Emby.Server.Implementations/Localization/Ratings/jp.csv +++ b/Emby.Server.Implementations/Localization/Ratings/jp.csv @@ -1,4 +1,11 @@ -JP-G,1 -JP-PG12,7 -JP-15+,8 -JP-18+,10 +A,0 +G,0 +B,12 +PG12,12 +C,15 +15+,15 +R15+,15 +16+,16 +D,17 +Z,18 +18+,18 diff --git a/Emby.Server.Implementations/Localization/Ratings/kz.csv b/Emby.Server.Implementations/Localization/Ratings/kz.csv index d546bff53d..e26b32b67e 100644 --- a/Emby.Server.Implementations/Localization/Ratings/kz.csv +++ b/Emby.Server.Implementations/Localization/Ratings/kz.csv @@ -1,7 +1,6 @@ -KZ-6-,0 -KZ-6+,6 -KZ-12+,12 -KZ-14+,14 -KZ-16+,16 -KZ-18+,18 -KZ-21+,21 +K,0 +БА,12 +Б14,14 +E16,16 +E18,18 +HA,18 diff --git a/Emby.Server.Implementations/Localization/Ratings/mx.csv b/Emby.Server.Implementations/Localization/Ratings/mx.csv index 785a8ba227..305912f239 100644 --- a/Emby.Server.Implementations/Localization/Ratings/mx.csv +++ b/Emby.Server.Implementations/Localization/Ratings/mx.csv @@ -1,6 +1,6 @@ -MX-AA,1 -MX-A,5 -MX-B,7 -MX-B-15,8 -MX-C,9 -MX-D,10 +A,0 +AA,0 +B,12 +B-15,15 +C,18 +D,1000 diff --git a/Emby.Server.Implementations/Localization/Ratings/nl.csv b/Emby.Server.Implementations/Localization/Ratings/nl.csv index 8c005092e4..44f372b2d6 100644 --- a/Emby.Server.Implementations/Localization/Ratings/nl.csv +++ b/Emby.Server.Implementations/Localization/Ratings/nl.csv @@ -1,6 +1,8 @@ -NL-AL,1 -NL-MG6,2 -NL-6,3 -NL-9,5 -NL-12,6 -NL-16,8 +AL,0 +MG6,6 +6,6 +9,9 +12,12 +14,14 +16,16 +18,18 diff --git a/Emby.Server.Implementations/Localization/Ratings/no.csv b/Emby.Server.Implementations/Localization/Ratings/no.csv index 127407be86..c8f8e93db7 100644 --- a/Emby.Server.Implementations/Localization/Ratings/no.csv +++ b/Emby.Server.Implementations/Localization/Ratings/no.csv @@ -1,6 +1,9 @@ -NO-A,1 -NO-6,3 -NO-9,4 -NO-12,5 -NO-15,8 -NO-18,9 +A,0 +6,6 +7,7 +9,9 +11,11 +12,12 +15,15 +18,18 +Not approved,1001 diff --git a/Emby.Server.Implementations/Localization/Ratings/nz.csv b/Emby.Server.Implementations/Localization/Ratings/nz.csv index bba99b764a..f617f0c39d 100644 --- a/Emby.Server.Implementations/Localization/Ratings/nz.csv +++ b/Emby.Server.Implementations/Localization/Ratings/nz.csv @@ -1,11 +1,15 @@ -NZ-G,1 -NZ-PG,5 -NZ-M,6 -NZ-R13,7 -NZ-RP13,7 -NZ-R15,8 -NZ-RP16,9 -NZ-R16,9 -NZ-R18,10 -NZ-R,10 -NZ-MA,10 +Exempt,0 +G,0 +GY,13 +PG,13 +R13,13 +RP13,13 +R15,15 +M,16 +R16,16 +RP16,16 +GA,18 +R18,18 +MA,1000 +R,1001 +Objectionable,1001 diff --git a/Emby.Server.Implementations/Localization/Ratings/ro.csv b/Emby.Server.Implementations/Localization/Ratings/ro.csv index 4089b282f0..44c23e2486 100644 --- a/Emby.Server.Implementations/Localization/Ratings/ro.csv +++ b/Emby.Server.Implementations/Localization/Ratings/ro.csv @@ -1 +1,6 @@ -RO-AG,1 +AG,0 +AP-12,12 +N-15,15 +IM-18,18 +IM-18-XXX,1000 +IC,1001 diff --git a/Emby.Server.Implementations/Localization/Ratings/ru.csv b/Emby.Server.Implementations/Localization/Ratings/ru.csv index 1bc94affd6..8b264070ba 100644 --- a/Emby.Server.Implementations/Localization/Ratings/ru.csv +++ b/Emby.Server.Implementations/Localization/Ratings/ru.csv @@ -1,5 +1,6 @@ -RU-0+,1 -RU-6+,3 -RU-12+,7 -RU-16+,9 -RU-18+,10 +0+,0 +6+,6 +12+,12 +16+,16 +18+,18 +Refused classification,1001 diff --git a/Emby.Server.Implementations/Localization/Ratings/se.csv b/Emby.Server.Implementations/Localization/Ratings/se.csv index 1443c07df7..e129c35617 100644 --- a/Emby.Server.Implementations/Localization/Ratings/se.csv +++ b/Emby.Server.Implementations/Localization/Ratings/se.csv @@ -1,5 +1,10 @@ -SE-Btl,1 -SE-Barntillåten,1 -SE-7,3 -SE-11,5 -SE-15,8 +Alla,0 +Barntillåten,0 +Btl,0 +0+,0 +7,7 +9+,9 +10+,10 +11,11 +14,14 +15,15 diff --git a/Emby.Server.Implementations/Localization/Ratings/uk.csv b/Emby.Server.Implementations/Localization/Ratings/uk.csv index 6c8005b3f3..75b1c20589 100644 --- a/Emby.Server.Implementations/Localization/Ratings/uk.csv +++ b/Emby.Server.Implementations/Localization/Ratings/uk.csv @@ -1,7 +1,22 @@ -UK-U,1 -UK-PG,5 -UK-12,7 -UK-12A,7 -UK-15,9 -UK-18,10 -UK-R18,15 +All,0 +E,0 +G,0 +U,0 +0+,0 +6+,6 +7+,7 +PG,8 +9+,9 +12,12 +12+,12 +12A,12 +Teen,13 +13+,13 +14+,14 +15,15 +16,16 +Caution,18 +18,18 +Mature,1000 +Adult,1000 +R18,1000 diff --git a/Emby.Server.Implementations/Localization/Ratings/us.csv b/Emby.Server.Implementations/Localization/Ratings/us.csv index 34c897fe3f..d103ddf42d 100644 --- a/Emby.Server.Implementations/Localization/Ratings/us.csv +++ b/Emby.Server.Implementations/Localization/Ratings/us.csv @@ -1,23 +1,50 @@ -TV-Y,1 -APPROVED,1 -G,1 -E,1 -EC,1 -TV-G,1 -TV-Y7,3 -TV-Y7-FV,4 -PG,5 -TV-PG,5 -PG-13,7 -T,7 -TV-14,8 -R,9 -M,9 -TV-MA,9 -NC-17,10 -AO,15 -RP,15 -UR,15 -NR,15 -X,15 -XXX,100 +Approved,0 +G,0 +TV-G,0 +TV-Y,0 +TV-Y7,7 +TV-Y7-FV,7 +PG,10 +PG-13,13 +TV-PG,13 +TV-PG-D,13 +TV-PG-L,13 +TV-PG-S,13 +TV-PG-V,13 +TV-PG-DL,13 +TV-PG-DS,13 +TV-PG-DV,13 +TV-PG-LS,13 +TV-PG-LV,13 +TV-PG-SV,13 +TV-PG-DLS,13 +TV-PG-DLV,13 +TV-PG-DSV,13 +TV-PG-LSV,13 +TV-PG-DLSV,13 +TV-14,14 +TV-14-D,14 +TV-14-L,14 +TV-14-S,14 +TV-14-V,14 +TV-14-DL,14 +TV-14-DS,14 +TV-14-DV,14 +TV-14-LS,14 +TV-14-LV,14 +TV-14-SV,14 +TV-14-DLS,14 +TV-14-DLV,14 +TV-14-DSV,14 +TV-14-LSV,14 +TV-14-DLSV,14 +NC-17,17 +R,17 +TV-MA,17 +TV-MA-L,17 +TV-MA-S,17 +TV-MA-V,17 +TV-MA-LS,17 +TV-MA-LV,17 +TV-MA-SV,17 +TV-MA-LSV,17 diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs index 16eb7a75c6..256bde8194 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs @@ -83,11 +83,11 @@ namespace Jellyfin.Server.Implementations.Tests.Localization await localizationManager.LoadAll(); var ratings = localizationManager.GetParentalRatings().ToList(); - Assert.Equal(23, ratings.Count); + Assert.Equal(50, ratings.Count); var tvma = ratings.FirstOrDefault(x => x.Name.Equals("TV-MA", StringComparison.Ordinal)); Assert.NotNull(tvma); - Assert.Equal(9, tvma!.Value); + Assert.Equal(17, tvma!.Value); } [Fact] @@ -100,21 +100,21 @@ namespace Jellyfin.Server.Implementations.Tests.Localization await localizationManager.LoadAll(); var ratings = localizationManager.GetParentalRatings().ToList(); - Assert.Equal(10, ratings.Count); + Assert.Equal(12, ratings.Count); var fsk = ratings.FirstOrDefault(x => x.Name.Equals("FSK-12", StringComparison.Ordinal)); Assert.NotNull(fsk); - Assert.Equal(7, fsk!.Value); + Assert.Equal(12, fsk!.Value); } [Theory] - [InlineData("CA-R", "CA", 10)] - [InlineData("FSK-16", "DE", 8)] - [InlineData("FSK-18", "DE", 9)] - [InlineData("FSK-18", "US", 9)] - [InlineData("TV-MA", "US", 9)] - [InlineData("XXX", "asdf", 100)] - [InlineData("Germany: FSK-18", "DE", 9)] + [InlineData("CA-R", "CA", 18)] + [InlineData("FSK-16", "DE", 16)] + [InlineData("FSK-18", "DE", 18)] + [InlineData("FSK-18", "US", 18)] + [InlineData("TV-MA", "US", 17)] + [InlineData("XXX", "asdf", 1000)] + [InlineData("Germany: FSK-18", "DE", 18)] public async Task GetRatingLevel_GivenValidString_Success(string value, string countryCode, int expectedLevel) { var localizationManager = Setup(new ServerConfiguration() @@ -135,6 +135,9 @@ namespace Jellyfin.Server.Implementations.Tests.Localization UICulture = "de-DE" }); await localizationManager.LoadAll(); + Assert.Null(localizationManager.GetRatingLevel("NR")); + Assert.Null(localizationManager.GetRatingLevel("unrated")); + Assert.Null(localizationManager.GetRatingLevel("Not Rated")); Assert.Null(localizationManager.GetRatingLevel("n/a")); } From c8d80450e09c2a0c475e71c8f1d312b3a8ccff26 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sun, 27 Nov 2022 15:37:34 +0100 Subject: [PATCH 04/41] Recursively update rating --- .../Controllers/ItemUpdateController.cs | 47 +++++++++++++++++-- MediaBrowser.Controller/Entities/BaseItem.cs | 4 +- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/Jellyfin.Api/Controllers/ItemUpdateController.cs b/Jellyfin.Api/Controllers/ItemUpdateController.cs index 230fbfb2cd..57d446dbd0 100644 --- a/Jellyfin.Api/Controllers/ItemUpdateController.cs +++ b/Jellyfin.Api/Controllers/ItemUpdateController.cs @@ -98,7 +98,7 @@ public class ItemUpdateController : BaseJellyfinApiController }).ToList()); } - UpdateItem(request, item); + await UpdateItem(request, item).ConfigureAwait(false); item.OnMetadataChanged(); @@ -224,7 +224,7 @@ public class ItemUpdateController : BaseJellyfinApiController return NoContent(); } - private void UpdateItem(BaseItemDto request, BaseItem item) + private async Task UpdateItem(BaseItemDto request, BaseItem item) { item.Name = request.Name; item.ForcedSortName = request.ForcedSortName; @@ -266,9 +266,50 @@ public class ItemUpdateController : BaseJellyfinApiController item.EndDate = request.EndDate.HasValue ? NormalizeDateTime(request.EndDate.Value) : null; item.PremiereDate = request.PremiereDate.HasValue ? NormalizeDateTime(request.PremiereDate.Value) : null; item.ProductionYear = request.ProductionYear; - item.OfficialRating = string.IsNullOrWhiteSpace(request.OfficialRating) ? null : request.OfficialRating; + + request.OfficialRating = string.IsNullOrWhiteSpace(request.OfficialRating) ? null : request.OfficialRating; + item.OfficialRating = request.OfficialRating; item.CustomRating = request.CustomRating; + if (item is Series rseries) + { + foreach (Season season in rseries.Children) + { + season.OfficialRating = request.OfficialRating; + season.CustomRating = request.CustomRating; + season.OnMetadataChanged(); + await season.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); + + foreach (Episode ep in season.Children) + { + ep.OfficialRating = request.OfficialRating; + ep.CustomRating = request.CustomRating; + ep.OnMetadataChanged(); + await ep.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); + } + } + } + else if (item is Season season) + { + foreach (Episode ep in season.Children) + { + ep.OfficialRating = request.OfficialRating; + ep.CustomRating = request.CustomRating; + ep.OnMetadataChanged(); + await ep.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); + } + } + else if (item is MusicAlbum album) + { + foreach (BaseItem track in album.Children) + { + track.OfficialRating = request.OfficialRating; + track.CustomRating = request.CustomRating; + track.OnMetadataChanged(); + await track.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); + } + } + if (request.ProductionLocations is not null) { item.ProductionLocations = request.ProductionLocations; diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 3d683052fe..cf369e84b2 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -554,7 +554,7 @@ namespace MediaBrowser.Controller.Entities public string OfficialRating { get; set; } [JsonIgnore] - public int InheritedParentalRatingValue { get; set; } + public int? InheritedParentalRatingValue { get; set; } /// /// Gets or sets the critic rating. @@ -2517,7 +2517,7 @@ namespace MediaBrowser.Controller.Entities var item = this; - var inheritedParentalRatingValue = item.GetInheritedParentalRatingValue() ?? 0; + var inheritedParentalRatingValue = item.GetInheritedParentalRatingValue() ?? null; if (inheritedParentalRatingValue != item.InheritedParentalRatingValue) { item.InheritedParentalRatingValue = inheritedParentalRatingValue; From a6cfe75d6e422eb1343a1f4bc2410fabb425266b Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Mon, 28 Nov 2022 13:06:55 +0100 Subject: [PATCH 05/41] Add database migration for rating schema change --- Jellyfin.Server/Migrations/MigrationRunner.cs | 3 +- .../Routines/MigrateRatingLevels.cs | 85 +++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index 8a6ab79323..2b15a6a1b5 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -39,7 +39,8 @@ namespace Jellyfin.Server.Migrations typeof(Routines.ReaddDefaultPluginRepository), typeof(Routines.MigrateDisplayPreferencesDb), typeof(Routines.RemoveDownloadImagesInAdvance), - typeof(Routines.MigrateAuthenticationDb) + typeof(Routines.MigrateAuthenticationDb), + typeof(Routines.MigrateRatingLevels) }; /// diff --git a/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs b/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs new file mode 100644 index 0000000000..5b27681f61 --- /dev/null +++ b/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs @@ -0,0 +1,85 @@ +using System; +using System.Globalization; +using System.IO; + +using MediaBrowser.Controller; +using Microsoft.Extensions.Logging; +using SQLitePCL.pretty; + +namespace Jellyfin.Server.Migrations.Routines +{ + /// + /// Remove duplicate entries which were caused by a bug where a file was considered to be an "Extra" to itself. + /// + internal class MigrateRatingLevels : IMigrationRoutine + { + private const string DbFilename = "library.db"; + private readonly ILogger _logger; + private readonly IServerApplicationPaths _paths; + + public MigrateRatingLevels(ILogger logger, IServerApplicationPaths paths) + { + _logger = logger; + _paths = paths; + } + + /// + public Guid Id => Guid.Parse("{67445D54-B895-4B24-9F4C-35CE0690EA07}"); + + /// + public string Name => "RemoveDuplicateExtras"; + + /// + public bool PerformOnNewInstall => false; + + /// + public void Perform() + { + var dataPath = _paths.DataPath; + var dbPath = Path.Combine(dataPath, DbFilename); + using (var connection = SQLite3.Open( + dbPath, + ConnectionFlags.ReadWrite, + null)) + { + // Back up the database before deleting any entries + for (int i = 1; ; i++) + { + var bakPath = string.Format(CultureInfo.InvariantCulture, "{0}.bak{1}", dbPath, i); + if (!File.Exists(bakPath)) + { + try + { + File.Copy(dbPath, bakPath); + _logger.LogInformation("Library database backed up to {BackupPath}", bakPath); + break; + } + catch (Exception ex) + { + _logger.LogError(ex, "Cannot make a backup of {Library} at path {BackupPath}", DbFilename, bakPath); + throw; + } + } + } + + // Migrate parental rating levels to new schema + _logger.LogInformation("Migrating parental rating levels."); + connection.Execute("UPDATE TypedBaseItems SET InheritedParentalRatingValue = NULL WHERE OfficialRating = 'NR'"); + connection.Execute("UPDATE TypedBaseItems SET InheritedParentalRatingValue = NULL WHERE InheritedParentalRatingValue = ''"); + connection.Execute("UPDATE TypedBaseItems SET InheritedParentalRatingValue = NULL WHERE InheritedParentalRatingValue = 0"); + connection.Execute("UPDATE TypedBaseItems SET InheritedParentalRatingValue = 1000 WHERE InheritedParentalRatingValue = 100"); + connection.Execute("UPDATE TypedBaseItems SET InheritedParentalRatingValue = 1000 WHERE InheritedParentalRatingValue = 15"); + connection.Execute("UPDATE TypedBaseItems SET InheritedParentalRatingValue = 18 WHERE InheritedParentalRatingValue = 10"); + connection.Execute("UPDATE TypedBaseItems SET InheritedParentalRatingValue = 18 WHERE InheritedParentalRatingValue = 9"); + connection.Execute("UPDATE TypedBaseItems SET InheritedParentalRatingValue = 16 WHERE InheritedParentalRatingValue = 8"); + connection.Execute("UPDATE TypedBaseItems SET InheritedParentalRatingValue = 12 WHERE InheritedParentalRatingValue = 7"); + connection.Execute("UPDATE TypedBaseItems SET InheritedParentalRatingValue = 12 WHERE InheritedParentalRatingValue = 6"); + connection.Execute("UPDATE TypedBaseItems SET InheritedParentalRatingValue = 12 WHERE InheritedParentalRatingValue = 5"); + connection.Execute("UPDATE TypedBaseItems SET InheritedParentalRatingValue = 7 WHERE InheritedParentalRatingValue = 4"); + connection.Execute("UPDATE TypedBaseItems SET InheritedParentalRatingValue = 6 WHERE InheritedParentalRatingValue = 3"); + connection.Execute("UPDATE TypedBaseItems SET InheritedParentalRatingValue = 6 WHERE InheritedParentalRatingValue = 2"); + connection.Execute("UPDATE TypedBaseItems SET InheritedParentalRatingValue = 0 WHERE InheritedParentalRatingValue = 1"); + } + } + } +} From 2e315b7f085ca0b9a2f61a7ac71a46ca84750f9f Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Mon, 28 Nov 2022 13:07:57 +0100 Subject: [PATCH 06/41] Properly build where clause for rating checks --- .../Data/SqliteItemRepository.cs | 133 +++++++++++++----- .../Localization/LocalizationManager.cs | 2 +- .../Controllers/ItemUpdateController.cs | 2 +- Jellyfin.Api/Controllers/ItemsController.cs | 7 + 4 files changed, 107 insertions(+), 37 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 90f03995e8..797990d296 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -4020,34 +4020,116 @@ namespace Emby.Server.Implementations.Data whereClauses.Add(clause); } - if (query.MinParentalRating.HasValue) + var ratingClause = "("; + if (query.HasParentalRating.HasValue && query.HasParentalRating.Value) { - whereClauses.Add("InheritedParentalRatingValue>=@MinParentalRating"); - if (statement is not null) + ratingClause += "InheritedParentalRatingValue not null"; + if (query.MinParentalRating.HasValue) { - statement.TryBind("@MinParentalRating", query.MinParentalRating.Value); + ratingClause += " AND InheritedParentalRatingValue >= @MinParentalRating"; + if (statement is not null) + { + statement.TryBind("@MinParentalRating", query.MinParentalRating.Value); + } } - } - if (query.MaxParentalRating.HasValue) - { - whereClauses.Add("InheritedParentalRatingValue<=@MaxParentalRating"); - if (statement is not null) + if (query.MaxParentalRating.HasValue) { - statement.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); + ratingClause += " AND InheritedParentalRatingValue <= @MaxParentalRating"; + if (statement is not null) + { + statement.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); + } } } + else if (query.BlockUnratedItems.Length > 0) + { + var paramName = "@UnratedType"; + var index = 0; + string blockedUnratedItems = string.Join(',', query.BlockUnratedItems.Select(_ => paramName + index++)); + ratingClause += "(InheritedParentalRatingValue is null AND UnratedType not in (" + blockedUnratedItems + "))"; + + if (statement != null) + { + for (var ind = 0; ind < query.BlockUnratedItems.Length; ind++) + { + statement.TryBind(paramName + ind, query.BlockUnratedItems[ind].ToString()); + } + } + + if (query.MinParentalRating.HasValue || query.MaxParentalRating.HasValue) + { + ratingClause += " OR ("; + } - if (query.HasParentalRating.HasValue) + if (query.MinParentalRating.HasValue) + { + ratingClause += "InheritedParentalRatingValue >= @MinParentalRating"; + if (statement != null) + { + statement.TryBind("@MinParentalRating", query.MinParentalRating.Value); + } + } + + if (query.MaxParentalRating.HasValue) + { + if (query.MinParentalRating.HasValue) + { + ratingClause += " AND "; + } + + ratingClause += "InheritedParentalRatingValue <= @MaxParentalRating"; + if (statement != null) + { + statement.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); + } + } + + if (query.MinParentalRating.HasValue || query.MaxParentalRating.HasValue) + { + ratingClause += ")"; + } + + if (!(query.MinParentalRating.HasValue || query.MaxParentalRating.HasValue)) + { + ratingClause += " OR InheritedParentalRatingValue not null"; + } + } + else if (query.MinParentalRating.HasValue) { - if (query.HasParentalRating.Value) + ratingClause += "InheritedParentalRatingValue is null OR (InheritedParentalRatingValue >= @MinParentalRating"; + if (statement != null) { - whereClauses.Add("InheritedParentalRatingValue > 0"); + statement.TryBind("@MinParentalRating", query.MinParentalRating.Value); } - else + + if (query.MaxParentalRating.HasValue) { - whereClauses.Add("InheritedParentalRatingValue = 0"); + ratingClause += " AND InheritedParentalRatingValue <= @MaxParentalRating"; + if (statement != null) + { + statement.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); + } } + + ratingClause += ")"; + } + else if (query.MaxParentalRating.HasValue) + { + ratingClause += "InheritedParentalRatingValue is null OR InheritedParentalRatingValue <= @MaxParentalRating"; + if (statement != null) + { + statement.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); + } + } + else if (query.HasParentalRating.HasValue && !query.HasParentalRating.Value) + { + ratingClause += "InheritedParentalRatingValue is null"; + } + + if (!string.Equals(ratingClause, "(", StringComparison.OrdinalIgnoreCase)) + { + whereClauses.Add(ratingClause + ")"); } if (query.HasOfficialRating.HasValue) @@ -4312,7 +4394,7 @@ namespace Emby.Server.Implementations.Data } // TODO this seems to be an idea for a better schema where ProviderIds are their own table - // buut this is not implemented + // but this is not implemented // hasProviderIds.Add("(COALESCE((select value from ProviderIds where ItemId=Guid and Name = '" + pair.Key + "'), '') <> " + paramName + ")"); // TODO this is a really BAD way to do it since the pair: @@ -4440,25 +4522,6 @@ namespace Emby.Server.Implementations.Data } } - if (query.BlockUnratedItems.Length == 1) - { - whereClauses.Add("(InheritedParentalRatingValue > 0 or UnratedType <> @UnratedType)"); - if (statement is not null) - { - statement.TryBind("@UnratedType", query.BlockUnratedItems[0].ToString()); - } - } - - if (query.BlockUnratedItems.Length > 1) - { - var inClause = string.Join(',', query.BlockUnratedItems.Select(i => "'" + i.ToString() + "'")); - whereClauses.Add( - string.Format( - CultureInfo.InvariantCulture, - "(InheritedParentalRatingValue > 0 or UnratedType not in ({0}))", - inClause)); - } - if (query.ExcludeInheritedTags.Length > 0) { var paramName = "@ExcludeInheritedTags"; diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 5e22e78867..0e19e80a88 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -273,7 +273,7 @@ namespace Emby.Server.Implementations.Localization } } - // TODO: Further improve by normalizing out all spaces and dashes + // TODO: Further improve when necessary return null; } diff --git a/Jellyfin.Api/Controllers/ItemUpdateController.cs b/Jellyfin.Api/Controllers/ItemUpdateController.cs index 57d446dbd0..9c71482413 100644 --- a/Jellyfin.Api/Controllers/ItemUpdateController.cs +++ b/Jellyfin.Api/Controllers/ItemUpdateController.cs @@ -147,7 +147,7 @@ public class ItemUpdateController : BaseJellyfinApiController var info = new MetadataEditorInfo { - ParentalRatingOptions = _localizationManager.GetParentalRatings().ToArray(), + ParentalRatingOptions = _localizationManager.GetParentalRatings().ToList(), ExternalIdInfos = _providerManager.GetExternalIdInfos(item).ToArray(), Countries = _localizationManager.GetCountries().ToArray(), Cultures = _localizationManager.GetCultures().ToArray() diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 728e628109..377526729a 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -411,6 +411,13 @@ public class ItemsController : BaseJellyfinApiController query.SeriesStatuses = seriesStatus; } + // Exclude Blocked Unrated Items + var blockedUnratedItems = user?.GetPreferenceValues(PreferenceKind.BlockUnratedItems); + if (blockedUnratedItems is not null) + { + query.BlockUnratedItems = blockedUnratedItems; + } + // ExcludeLocationTypes if (excludeLocationTypes.Any(t => t == LocationType.Virtual)) { From e014522dff2e9ba83d27b9d874559c9cd52fd5f5 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Mon, 28 Nov 2022 18:06:56 +0100 Subject: [PATCH 07/41] Add default for MaxParentalRating to UserPolicy --- MediaBrowser.Model/Users/UserPolicy.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs index cc7d57eb0a..80f5e2c37e 100644 --- a/MediaBrowser.Model/Users/UserPolicy.cs +++ b/MediaBrowser.Model/Users/UserPolicy.cs @@ -46,6 +46,7 @@ namespace MediaBrowser.Model.Users LoginAttemptsBeforeLockout = -1; MaxActiveSessions = 0; + MaxParentalRating = null; EnableAllChannels = true; EnabledChannels = Array.Empty(); From 4ed97a459339f79b0864bf4db24ffe84f9b22768 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Tue, 29 Nov 2022 18:00:56 +0100 Subject: [PATCH 08/41] Properly check for item visibility in UserLibraryController --- .../Controllers/UserLibraryController.cs | 159 +++++++++++++++--- 1 file changed, 139 insertions(+), 20 deletions(-) diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 1233995b4c..2c4fe91862 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Extensions; using Jellyfin.Api.ModelBinders; +using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; @@ -72,7 +73,7 @@ public class UserLibraryController : BaseJellyfinApiController /// User id. /// Item id. /// Item returned. - /// An containing the d item. + /// An containing the item. [HttpGet("Users/{userId}/Items/{itemId}")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> GetItem([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) @@ -86,11 +87,19 @@ public class UserLibraryController : BaseJellyfinApiController var item = itemId.Equals(default) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId); + if (item is null) { return NotFound(); } + if (item is not UserRootFolder + // Check the item is visible for the user + && !item.IsVisible(user)) + { + return Unauthorized($"{user.Username} is not permitted to access item {item.Name}."); + } + await RefreshItemOnDemandIfNeeded(item).ConfigureAwait(false); var dtoOptions = new DtoOptions().AddClientFields(User); @@ -139,11 +148,19 @@ public class UserLibraryController : BaseJellyfinApiController var item = itemId.Equals(default) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId); + if (item is null) { return NotFound(); } + if (item is not UserRootFolder + // Check the item is visible for the user + && !item.IsVisible(user)) + { + return Unauthorized($"{user.Username} is not permitted to access item {item.Name}."); + } + var items = await _libraryManager.GetIntros(item, user).ConfigureAwait(false); var dtoOptions = new DtoOptions().AddClientFields(User); var dtos = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray(); @@ -162,7 +179,29 @@ public class UserLibraryController : BaseJellyfinApiController [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult MarkFavoriteItem([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { - return MarkFavorite(userId, itemId, true); + var user = _userManager.GetUserById(userId); + if (user is null) + { + return NotFound(); + } + + var item = itemId.Equals(default) + ? _libraryManager.GetUserRootFolder() + : _libraryManager.GetItemById(itemId); + + if (item is null) + { + return NotFound(); + } + + if (item is not UserRootFolder + // Check the item is visible for the user + && !item.IsVisible(user)) + { + return Unauthorized($"{user.Username} is not permitted to access item {item.Name}."); + } + + return MarkFavorite(user, item, true); } /// @@ -176,7 +215,29 @@ public class UserLibraryController : BaseJellyfinApiController [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult UnmarkFavoriteItem([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { - return MarkFavorite(userId, itemId, false); + var user = _userManager.GetUserById(userId); + if (user is null) + { + return NotFound(); + } + + var item = itemId.Equals(default) + ? _libraryManager.GetUserRootFolder() + : _libraryManager.GetItemById(itemId); + + if (item is null) + { + return NotFound(); + } + + if (item is not UserRootFolder + // Check the item is visible for the user + && !item.IsVisible(user)) + { + return Unauthorized($"{user.Username} is not permitted to access item {item.Name}."); + } + + return MarkFavorite(user, item, false); } /// @@ -190,7 +251,29 @@ public class UserLibraryController : BaseJellyfinApiController [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult DeleteUserItemRating([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { - return UpdateUserItemRatingInternal(userId, itemId, null); + var user = _userManager.GetUserById(userId); + if (user is null) + { + return NotFound(); + } + + var item = itemId.Equals(default) + ? _libraryManager.GetUserRootFolder() + : _libraryManager.GetItemById(itemId); + + if (item is null) + { + return NotFound(); + } + + if (item is not UserRootFolder + // Check the item is visible for the user + && !item.IsVisible(user)) + { + return Unauthorized($"{user.Username} is not permitted to access item {item.Name}."); + } + + return UpdateUserItemRatingInternal(user, item, null); } /// @@ -205,7 +288,29 @@ public class UserLibraryController : BaseJellyfinApiController [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult UpdateUserItemRating([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId, [FromQuery] bool? likes) { - return UpdateUserItemRatingInternal(userId, itemId, likes); + var user = _userManager.GetUserById(userId); + if (user is null) + { + return NotFound(); + } + + var item = itemId.Equals(default) + ? _libraryManager.GetUserRootFolder() + : _libraryManager.GetItemById(itemId); + + if (item is null) + { + return NotFound(); + } + + if (item is not UserRootFolder + // Check the item is visible for the user + && !item.IsVisible(user)) + { + return Unauthorized($"{user.Username} is not permitted to access item {item.Name}."); + } + + return UpdateUserItemRatingInternal(user, item, likes); } /// @@ -228,13 +333,20 @@ public class UserLibraryController : BaseJellyfinApiController var item = itemId.Equals(default) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId); + if (item is null) { return NotFound(); } - var dtoOptions = new DtoOptions().AddClientFields(User); + if (item is not UserRootFolder + // Check the item is visible for the user + && !item.IsVisible(user)) + { + return Unauthorized($"{user.Username} is not permitted to access item {item.Name}."); + } + var dtoOptions = new DtoOptions().AddClientFields(User); if (item is IHasTrailers hasTrailers) { var trailers = hasTrailers.LocalTrailers; @@ -266,11 +378,19 @@ public class UserLibraryController : BaseJellyfinApiController var item = itemId.Equals(default) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId); + if (item is null) { return NotFound(); } + if (item is not UserRootFolder + // Check the item is visible for the user + && !item.IsVisible(user)) + { + return Unauthorized($"{user.Username} is not permitted to access item {item.Name}."); + } + var dtoOptions = new DtoOptions().AddClientFields(User); return Ok(item @@ -385,15 +505,11 @@ public class UserLibraryController : BaseJellyfinApiController /// /// Marks the favorite. /// - /// The user id. - /// The item id. + /// The user. + /// The item. /// if set to true [is favorite]. - private UserItemDataDto MarkFavorite(Guid userId, Guid itemId, bool isFavorite) + private UserItemDataDto MarkFavorite(User user, BaseItem item, bool isFavorite) { - var user = _userManager.GetUserById(userId); - - var item = itemId.Equals(default) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId); - // Get the user data for this item var data = _userDataRepository.GetUserData(user, item); @@ -408,15 +524,11 @@ public class UserLibraryController : BaseJellyfinApiController /// /// Updates the user item rating. /// - /// The user id. - /// The item id. + /// The user. + /// The item. /// if set to true [likes]. - private UserItemDataDto UpdateUserItemRatingInternal(Guid userId, Guid itemId, bool? likes) + private UserItemDataDto UpdateUserItemRatingInternal(User user, BaseItem item, bool? likes) { - var user = _userManager.GetUserById(userId); - - var item = itemId.Equals(default) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId); - // Get the user data for this item var data = _userDataRepository.GetUserData(user, item); @@ -455,6 +567,13 @@ public class UserLibraryController : BaseJellyfinApiController return NotFound(); } + if (item is not UserRootFolder + // Check the item is visible for the user + && !item.IsVisible(user)) + { + return Unauthorized($"{user.Username} is not permitted to access item {item.Name}."); + } + var result = await _lyricManager.GetLyrics(item).ConfigureAwait(false); if (result is not null) { From 9d21f078c71373d956893d2d298cc4d7fcc0adf1 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Tue, 29 Nov 2022 22:04:28 +0100 Subject: [PATCH 09/41] Add default rating selections --- .../Localization/LocalizationManager.cs | 59 +++++++++++++++++-- MediaBrowser.Model/Entities/ParentalRating.cs | 4 +- .../Localization/LocalizationManagerTests.cs | 4 +- 3 files changed, 57 insertions(+), 10 deletions(-) diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 0e19e80a88..ca7c2c21f1 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -87,12 +87,10 @@ namespace Emby.Server.Implementations.Localization var name = parts[0]; dict.Add(name, new ParentalRating(name, value)); } -#if DEBUG else { _logger.LogWarning("Malformed line in ratings file for country {CountryCode}", countryCode); } -#endif } _allParentalRatings[countryCode] = dict; @@ -185,7 +183,56 @@ namespace Emby.Server.Implementations.Localization /// public IEnumerable GetParentalRatings() - => GetParentalRatingsDictionary().Values; + { + var ratings = GetParentalRatingsDictionary().Values.ToList(); + + // Add common ratings to ensure them being available for selection. + // Based on the US rating system due to it being the main source of rating in the metadata providers + // Minimum rating possible + if (!ratings.Any(x => x.Value == 0)) + { + ratings.Add(new ParentalRating("Approved", 0)); + } + + // Matches PG (this has differnet age restrictions depending on country) + if (!ratings.Any(x => x.Value == 10)) + { + ratings.Add(new ParentalRating("10", 10)); + } + + // Matches PG-13 + if (!ratings.Any(x => x.Value == 13)) + { + ratings.Add(new ParentalRating("13", 13)); + } + + // Matches TV-14 + if (!ratings.Any(x => x.Value == 14)) + { + ratings.Add(new ParentalRating("14", 14)); + } + + // Catchall if max rating of country is less than 21 + // Using 21 instead of 18 to be sure to allow access to all rated content except adult and banned + if (!ratings.Any(x => x.Value >= 21)) + { + ratings.Add(new ParentalRating("21", 21)); + } + + // A lot of countries don't excplicitly have a seperate rating for adult content + if (!ratings.Any(x => x.Value == 1000)) + { + ratings.Add(new ParentalRating("XXX", 1000)); + } + + // A lot of countries don't excplicitly have a seperate rating for banned content + if (!ratings.Any(x => x.Value == 1001)) + { + ratings.Add(new ParentalRating("Banned", 1001)); + } + + return ratings.OrderBy(r => r.Value); + } /// /// Gets the parental ratings dictionary. @@ -207,15 +254,15 @@ namespace Emby.Server.Implementations.Localization } /// - /// Gets the ratings. + /// Gets the ratings for a country. /// /// The country code. /// The ratings. private Dictionary? GetRatings(string countryCode) { - _allParentalRatings.TryGetValue(countryCode, out var value); + _allParentalRatings.TryGetValue(countryCode, out var countryValue); - return value; + return countryValue; } /// diff --git a/MediaBrowser.Model/Entities/ParentalRating.cs b/MediaBrowser.Model/Entities/ParentalRating.cs index 17b2868a31..c92640818c 100644 --- a/MediaBrowser.Model/Entities/ParentalRating.cs +++ b/MediaBrowser.Model/Entities/ParentalRating.cs @@ -12,7 +12,7 @@ namespace MediaBrowser.Model.Entities { } - public ParentalRating(string name, int value) + public ParentalRating(string name, int? value) { Name = name; Value = value; @@ -28,6 +28,6 @@ namespace MediaBrowser.Model.Entities /// Gets or sets the value. /// /// The value. - public int Value { get; set; } + public int? Value { get; set; } } } diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs index 256bde8194..ab3682ccf6 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs @@ -83,7 +83,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization await localizationManager.LoadAll(); var ratings = localizationManager.GetParentalRatings().ToList(); - Assert.Equal(50, ratings.Count); + Assert.Equal(53, ratings.Count); var tvma = ratings.FirstOrDefault(x => x.Name.Equals("TV-MA", StringComparison.Ordinal)); Assert.NotNull(tvma); @@ -100,7 +100,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization await localizationManager.LoadAll(); var ratings = localizationManager.GetParentalRatings().ToList(); - Assert.Equal(12, ratings.Count); + Assert.Equal(18, ratings.Count); var fsk = ratings.FirstOrDefault(x => x.Name.Equals("FSK-12", StringComparison.Ordinal)); Assert.NotNull(fsk); From 07dc163844091e3335081578ed5cc7fd427c5c0d Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Wed, 30 Nov 2022 11:55:40 +0100 Subject: [PATCH 10/41] Fix playlist parental control and no parental control skipping forbidden unrated items --- Emby.Server.Implementations/Dto/DtoService.cs | 7 ++++--- MediaBrowser.Controller/Entities/BaseItem.cs | 15 +++++---------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 5103b1fbfb..e928f1ff3a 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -83,13 +83,14 @@ namespace Emby.Server.Implementations.Dto /// public IReadOnlyList GetBaseItemDtos(IReadOnlyList items, DtoOptions options, User user = null, BaseItem owner = null) { - var returnItems = new BaseItemDto[items.Count]; + var accessibleItems = user is null ? items : items.Where(x => x.IsVisible(user)).ToList(); + var returnItems = new BaseItemDto[accessibleItems.Count]; var programTuples = new List<(BaseItem, BaseItemDto)>(); var channelTuples = new List<(BaseItemDto, LiveTvChannel)>(); - for (int index = 0; index < items.Count; index++) + for (int index = 0; index < accessibleItems.Count; index++) { - var item = items[index]; + var item = accessibleItems[index]; var dto = GetBaseItemDtoInternal(item, options, user, owner); if (item is LiveTvChannel tvChannel) diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index cf369e84b2..b8601cccd9 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1534,12 +1534,6 @@ namespace MediaBrowser.Controller.Entities } var maxAllowedRating = user.MaxParentalAgeRating; - - if (maxAllowedRating is null) - { - return true; - } - var rating = CustomRatingForComparison; if (string.IsNullOrEmpty(rating)) @@ -1549,12 +1543,13 @@ namespace MediaBrowser.Controller.Entities if (string.IsNullOrEmpty(rating)) { + Logger.LogDebug("{0} has no parental rating set.", Name); return !GetBlockUnratedValue(user); } var value = LocalizationManager.GetRatingLevel(rating); - // Could not determine the integer value + // Could not determine rating level if (!value.HasValue) { var isAllowed = !GetBlockUnratedValue(user); @@ -1567,7 +1562,7 @@ namespace MediaBrowser.Controller.Entities return isAllowed; } - return value.Value <= maxAllowedRating.Value; + return !maxAllowedRating.HasValue || value.Value <= maxAllowedRating.Value; } public int? GetInheritedParentalRatingValue() @@ -1627,10 +1622,10 @@ namespace MediaBrowser.Controller.Entities } /// - /// Gets the block unrated value. + /// Gets a bool indicating if access to the unrated item is blocked or not. /// /// The configuration. - /// true if XXXX, false otherwise. + /// true if blocked, false otherwise. protected virtual bool GetBlockUnratedValue(User user) { // Don't block plain folders that are unrated. Let the media underneath get blocked From 15efb9935c2f786c12343f158ef2c9db542e71ba Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Thu, 8 Dec 2022 16:15:26 +0100 Subject: [PATCH 11/41] Fix typo and migration description --- Emby.Server.Implementations/Localization/LocalizationManager.cs | 2 +- Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index ca7c2c21f1..8faebadd61 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -194,7 +194,7 @@ namespace Emby.Server.Implementations.Localization ratings.Add(new ParentalRating("Approved", 0)); } - // Matches PG (this has differnet age restrictions depending on country) + // Matches PG (this has different age restrictions depending on country) if (!ratings.Any(x => x.Value == 10)) { ratings.Add(new ParentalRating("10", 10)); diff --git a/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs b/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs index 5b27681f61..21f1872bc5 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs @@ -9,7 +9,7 @@ using SQLitePCL.pretty; namespace Jellyfin.Server.Migrations.Routines { /// - /// Remove duplicate entries which were caused by a bug where a file was considered to be an "Extra" to itself. + /// Migrate rating levels to new rating level system. /// internal class MigrateRatingLevels : IMigrationRoutine { From 5cdb0c7932303d2d9a59d7d21860172fd97fe031 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Fri, 27 Jan 2023 15:49:08 +0100 Subject: [PATCH 12/41] Apply review suggestions --- .../Data/SqliteItemRepository.cs | 208 ++++-------------- .../Localization/LocalizationManager.cs | 21 +- .../Localization/Ratings/be.csv | 2 +- .../Routines/MigrateRatingLevels.cs | 2 +- 4 files changed, 46 insertions(+), 187 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 797990d296..055131c8e6 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -3202,7 +3202,8 @@ namespace Emby.Server.Implementations.Data return IsAlphaNumeric(value); } - private List GetWhereClauses(InternalItemsQuery query, IStatement statement) +#nullable enable + private List GetWhereClauses(InternalItemsQuery query, IStatement? statement) { if (query.IsResumable ?? false) { @@ -3622,12 +3623,8 @@ namespace Emby.Server.Implementations.Data clauseBuilder.Append("(guid in (select itemid from People where Name = (select Name from TypedBaseItems where guid=") .Append(paramName) .Append("))) OR "); - - if (statement is not null) - { - query.PersonIds[i].TryWriteBytes(idBytes); - statement.TryBind(paramName, idBytes); - } + query.PersonIds[i].TryWriteBytes(idBytes); + statement?.TryBind(paramName, idBytes); } // Remove last " OR " @@ -3677,7 +3674,6 @@ namespace Emby.Server.Implementations.Data if (statement is not null) { nameContains = FixUnicodeChars(nameContains); - statement.TryBind("@NameContains", "%" + GetCleanValue(nameContains) + "%"); } } @@ -3803,13 +3799,8 @@ namespace Emby.Server.Implementations.Data foreach (var artistId in query.ArtistIds) { var paramName = "@ArtistIds" + index; - clauses.Add("(guid in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type<=1))"); - if (statement is not null) - { - statement.TryBind(paramName, artistId); - } - + statement?.TryBind(paramName, artistId); index++; } @@ -3824,13 +3815,8 @@ namespace Emby.Server.Implementations.Data foreach (var artistId in query.AlbumArtistIds) { var paramName = "@ArtistIds" + index; - clauses.Add("(guid in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=1))"); - if (statement is not null) - { - statement.TryBind(paramName, artistId); - } - + statement?.TryBind(paramName, artistId); index++; } @@ -3845,13 +3831,8 @@ namespace Emby.Server.Implementations.Data foreach (var artistId in query.ContributingArtistIds) { var paramName = "@ArtistIds" + index; - clauses.Add("((select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from ItemValues where ItemId=Guid and Type=0) AND (select CleanName from TypedBaseItems where guid=" + paramName + ") not in (select CleanValue from ItemValues where ItemId=Guid and Type=1))"); - if (statement is not null) - { - statement.TryBind(paramName, artistId); - } - + statement?.TryBind(paramName, artistId); index++; } @@ -3866,13 +3847,8 @@ namespace Emby.Server.Implementations.Data foreach (var albumId in query.AlbumIds) { var paramName = "@AlbumIds" + index; - clauses.Add("Album in (select Name from typedbaseitems where guid=" + paramName + ")"); - if (statement is not null) - { - statement.TryBind(paramName, albumId); - } - + statement?.TryBind(paramName, albumId); index++; } @@ -3887,13 +3863,8 @@ namespace Emby.Server.Implementations.Data foreach (var artistId in query.ExcludeArtistIds) { var paramName = "@ExcludeArtistId" + index; - clauses.Add("(guid not in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type<=1))"); - if (statement is not null) - { - statement.TryBind(paramName, artistId); - } - + statement?.TryBind(paramName, artistId); index++; } @@ -3908,13 +3879,8 @@ namespace Emby.Server.Implementations.Data foreach (var genreId in query.GenreIds) { var paramName = "@GenreId" + index; - clauses.Add("(guid in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=2))"); - if (statement is not null) - { - statement.TryBind(paramName, genreId); - } - + statement?.TryBind(paramName, genreId); index++; } @@ -3929,11 +3895,7 @@ namespace Emby.Server.Implementations.Data foreach (var item in query.Genres) { clauses.Add("@Genre" + index + " in (select CleanValue from ItemValues where ItemId=Guid and Type=2)"); - if (statement is not null) - { - statement.TryBind("@Genre" + index, GetCleanValue(item)); - } - + statement?.TryBind("@Genre" + index, GetCleanValue(item)); index++; } @@ -3948,11 +3910,7 @@ namespace Emby.Server.Implementations.Data foreach (var item in tags) { clauses.Add("@Tag" + index + " in (select CleanValue from ItemValues where ItemId=Guid and Type=4)"); - if (statement is not null) - { - statement.TryBind("@Tag" + index, GetCleanValue(item)); - } - + statement?.TryBind("@Tag" + index, GetCleanValue(item)); index++; } @@ -3967,11 +3925,7 @@ namespace Emby.Server.Implementations.Data foreach (var item in excludeTags) { clauses.Add("@ExcludeTag" + index + " not in (select CleanValue from ItemValues where ItemId=Guid and Type=4)"); - if (statement is not null) - { - statement.TryBind("@ExcludeTag" + index, GetCleanValue(item)); - } - + statement?.TryBind("@ExcludeTag" + index, GetCleanValue(item)); index++; } @@ -3986,14 +3940,8 @@ namespace Emby.Server.Implementations.Data foreach (var studioId in query.StudioIds) { var paramName = "@StudioId" + index; - clauses.Add("(guid in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=3))"); - - if (statement is not null) - { - statement.TryBind(paramName, studioId); - } - + statement?.TryBind(paramName, studioId); index++; } @@ -4008,11 +3956,7 @@ namespace Emby.Server.Implementations.Data foreach (var item in query.OfficialRatings) { clauses.Add("OfficialRating=@OfficialRating" + index); - if (statement is not null) - { - statement.TryBind("@OfficialRating" + index, item); - } - + statement?.TryBind("@OfficialRating" + index, item); index++; } @@ -4021,25 +3965,19 @@ namespace Emby.Server.Implementations.Data } var ratingClause = "("; - if (query.HasParentalRating.HasValue && query.HasParentalRating.Value) + if (query.HasParentalRating ?? false) { ratingClause += "InheritedParentalRatingValue not null"; if (query.MinParentalRating.HasValue) { ratingClause += " AND InheritedParentalRatingValue >= @MinParentalRating"; - if (statement is not null) - { - statement.TryBind("@MinParentalRating", query.MinParentalRating.Value); - } + statement?.TryBind("@MinParentalRating", query.MinParentalRating.Value); } if (query.MaxParentalRating.HasValue) { ratingClause += " AND InheritedParentalRatingValue <= @MaxParentalRating"; - if (statement is not null) - { - statement.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); - } + statement?.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); } } else if (query.BlockUnratedItems.Length > 0) @@ -4049,7 +3987,7 @@ namespace Emby.Server.Implementations.Data string blockedUnratedItems = string.Join(',', query.BlockUnratedItems.Select(_ => paramName + index++)); ratingClause += "(InheritedParentalRatingValue is null AND UnratedType not in (" + blockedUnratedItems + "))"; - if (statement != null) + if (statement is not null) { for (var ind = 0; ind < query.BlockUnratedItems.Length; ind++) { @@ -4065,10 +4003,7 @@ namespace Emby.Server.Implementations.Data if (query.MinParentalRating.HasValue) { ratingClause += "InheritedParentalRatingValue >= @MinParentalRating"; - if (statement != null) - { - statement.TryBind("@MinParentalRating", query.MinParentalRating.Value); - } + statement?.TryBind("@MinParentalRating", query.MinParentalRating.Value); } if (query.MaxParentalRating.HasValue) @@ -4079,10 +4014,7 @@ namespace Emby.Server.Implementations.Data } ratingClause += "InheritedParentalRatingValue <= @MaxParentalRating"; - if (statement != null) - { - statement.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); - } + statement?.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); } if (query.MinParentalRating.HasValue || query.MaxParentalRating.HasValue) @@ -4098,18 +4030,12 @@ namespace Emby.Server.Implementations.Data else if (query.MinParentalRating.HasValue) { ratingClause += "InheritedParentalRatingValue is null OR (InheritedParentalRatingValue >= @MinParentalRating"; - if (statement != null) - { - statement.TryBind("@MinParentalRating", query.MinParentalRating.Value); - } + statement?.TryBind("@MinParentalRating", query.MinParentalRating.Value); if (query.MaxParentalRating.HasValue) { ratingClause += " AND InheritedParentalRatingValue <= @MaxParentalRating"; - if (statement != null) - { - statement.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); - } + statement?.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); } ratingClause += ")"; @@ -4117,12 +4043,9 @@ namespace Emby.Server.Implementations.Data else if (query.MaxParentalRating.HasValue) { ratingClause += "InheritedParentalRatingValue is null OR InheritedParentalRatingValue <= @MaxParentalRating"; - if (statement != null) - { - statement.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); - } + statement?.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); } - else if (query.HasParentalRating.HasValue && !query.HasParentalRating.Value) + else if (!query.HasParentalRating ?? false) { ratingClause += "InheritedParentalRatingValue is null"; } @@ -4171,37 +4094,25 @@ namespace Emby.Server.Implementations.Data if (!string.IsNullOrWhiteSpace(query.HasNoAudioTrackWithLanguage)) { whereClauses.Add("((select language from MediaStreams where MediaStreams.ItemId=A.Guid and MediaStreams.StreamType='Audio' and MediaStreams.Language=@HasNoAudioTrackWithLanguage limit 1) is null)"); - if (statement is not null) - { - statement.TryBind("@HasNoAudioTrackWithLanguage", query.HasNoAudioTrackWithLanguage); - } + statement?.TryBind("@HasNoAudioTrackWithLanguage", query.HasNoAudioTrackWithLanguage); } if (!string.IsNullOrWhiteSpace(query.HasNoInternalSubtitleTrackWithLanguage)) { whereClauses.Add("((select language from MediaStreams where MediaStreams.ItemId=A.Guid and MediaStreams.StreamType='Subtitle' and MediaStreams.IsExternal=0 and MediaStreams.Language=@HasNoInternalSubtitleTrackWithLanguage limit 1) is null)"); - if (statement is not null) - { - statement.TryBind("@HasNoInternalSubtitleTrackWithLanguage", query.HasNoInternalSubtitleTrackWithLanguage); - } + statement?.TryBind("@HasNoInternalSubtitleTrackWithLanguage", query.HasNoInternalSubtitleTrackWithLanguage); } if (!string.IsNullOrWhiteSpace(query.HasNoExternalSubtitleTrackWithLanguage)) { whereClauses.Add("((select language from MediaStreams where MediaStreams.ItemId=A.Guid and MediaStreams.StreamType='Subtitle' and MediaStreams.IsExternal=1 and MediaStreams.Language=@HasNoExternalSubtitleTrackWithLanguage limit 1) is null)"); - if (statement is not null) - { - statement.TryBind("@HasNoExternalSubtitleTrackWithLanguage", query.HasNoExternalSubtitleTrackWithLanguage); - } + statement?.TryBind("@HasNoExternalSubtitleTrackWithLanguage", query.HasNoExternalSubtitleTrackWithLanguage); } if (!string.IsNullOrWhiteSpace(query.HasNoSubtitleTrackWithLanguage)) { whereClauses.Add("((select language from MediaStreams where MediaStreams.ItemId=A.Guid and MediaStreams.StreamType='Subtitle' and MediaStreams.Language=@HasNoSubtitleTrackWithLanguage limit 1) is null)"); - if (statement is not null) - { - statement.TryBind("@HasNoSubtitleTrackWithLanguage", query.HasNoSubtitleTrackWithLanguage); - } + statement?.TryBind("@HasNoSubtitleTrackWithLanguage", query.HasNoSubtitleTrackWithLanguage); } if (query.HasSubtitles.HasValue) @@ -4251,15 +4162,11 @@ namespace Emby.Server.Implementations.Data if (query.Years.Length == 1) { whereClauses.Add("ProductionYear=@Years"); - if (statement is not null) - { - statement.TryBind("@Years", query.Years[0].ToString(CultureInfo.InvariantCulture)); - } + statement?.TryBind("@Years", query.Years[0].ToString(CultureInfo.InvariantCulture)); } else if (query.Years.Length > 1) { var val = string.Join(',', query.Years); - whereClauses.Add("ProductionYear in (" + val + ")"); } @@ -4267,10 +4174,7 @@ namespace Emby.Server.Implementations.Data if (isVirtualItem.HasValue) { whereClauses.Add("IsVirtualItem=@IsVirtualItem"); - if (statement is not null) - { - statement.TryBind("@IsVirtualItem", isVirtualItem.Value); - } + statement?.TryBind("@IsVirtualItem", isVirtualItem.Value); } if (query.IsSpecialSeason.HasValue) @@ -4301,31 +4205,22 @@ namespace Emby.Server.Implementations.Data if (queryMediaTypes.Length == 1) { whereClauses.Add("MediaType=@MediaTypes"); - if (statement is not null) - { - statement.TryBind("@MediaTypes", queryMediaTypes[0]); - } + statement?.TryBind("@MediaTypes", queryMediaTypes[0]); } else if (queryMediaTypes.Length > 1) { var val = string.Join(',', queryMediaTypes.Select(i => "'" + i + "'")); - whereClauses.Add("MediaType in (" + val + ")"); } if (query.ItemIds.Length > 0) { var includeIds = new List(); - var index = 0; foreach (var id in query.ItemIds) { includeIds.Add("Guid = @IncludeId" + index); - if (statement is not null) - { - statement.TryBind("@IncludeId" + index, id); - } - + statement?.TryBind("@IncludeId" + index, id); index++; } @@ -4335,16 +4230,11 @@ namespace Emby.Server.Implementations.Data if (query.ExcludeItemIds.Length > 0) { var excludeIds = new List(); - var index = 0; foreach (var id in query.ExcludeItemIds) { excludeIds.Add("Guid <> @ExcludeId" + index); - if (statement is not null) - { - statement.TryBind("@ExcludeId" + index, id); - } - + statement?.TryBind("@ExcludeId" + index, id); index++; } @@ -4365,11 +4255,7 @@ namespace Emby.Server.Implementations.Data var paramName = "@ExcludeProviderId" + index; excludeIds.Add("(ProviderIds is null or ProviderIds not like " + paramName + ")"); - if (statement is not null) - { - statement.TryBind(paramName, "%" + pair.Key + "=" + pair.Value + "%"); - } - + statement?.TryBind(paramName, "%" + pair.Key + "=" + pair.Value + "%"); index++; break; @@ -4408,11 +4294,7 @@ namespace Emby.Server.Implementations.Data hasProviderIds.Add("ProviderIds like " + paramName); // this replaces the placeholder with a value, here: %key=val% - if (statement is not null) - { - statement.TryBind(paramName, "%" + pair.Key + "=" + pair.Value + "%"); - } - + statement?.TryBind(paramName, "%" + pair.Key + "=" + pair.Value + "%"); index++; break; @@ -4489,11 +4371,7 @@ namespace Emby.Server.Implementations.Data if (query.AncestorIds.Length == 1) { whereClauses.Add("Guid in (select itemId from AncestorIds where AncestorId=@AncestorId)"); - - if (statement is not null) - { - statement.TryBind("@AncestorId", query.AncestorIds[0]); - } + statement?.TryBind("@AncestorId", query.AncestorIds[0]); } if (query.AncestorIds.Length > 1) @@ -4506,20 +4384,13 @@ namespace Emby.Server.Implementations.Data { var inClause = "select guid from TypedBaseItems where PresentationUniqueKey=@AncestorWithPresentationUniqueKey"; whereClauses.Add(string.Format(CultureInfo.InvariantCulture, "Guid in (select itemId from AncestorIds where AncestorId in ({0}))", inClause)); - if (statement is not null) - { - statement.TryBind("@AncestorWithPresentationUniqueKey", query.AncestorWithPresentationUniqueKey); - } + statement?.TryBind("@AncestorWithPresentationUniqueKey", query.AncestorWithPresentationUniqueKey); } if (!string.IsNullOrWhiteSpace(query.SeriesPresentationUniqueKey)) { whereClauses.Add("SeriesPresentationUniqueKey=@SeriesPresentationUniqueKey"); - - if (statement is not null) - { - statement.TryBind("@SeriesPresentationUniqueKey", query.SeriesPresentationUniqueKey); - } + statement?.TryBind("@SeriesPresentationUniqueKey", query.SeriesPresentationUniqueKey); } if (query.ExcludeInheritedTags.Length > 0) @@ -4668,6 +4539,7 @@ namespace Emby.Server.Implementations.Data return whereClauses; } +#nullable disable /// /// Formats a where clause for the specified provider. diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 8faebadd61..6e2a33fd56 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -297,30 +297,17 @@ namespace Emby.Server.Implementations.Localization } // Try splitting by : to handle "Germany: FSK 18" - var index = rating.IndexOf(':', StringComparison.Ordinal); - if (index != -1) + if (rating.Contains(':', StringComparison.OrdinalIgnoreCase)) { - var trimmedRating = rating.AsSpan(index).TrimStart(':').Trim(); - - if (!trimmedRating.IsEmpty) - { - return GetRatingLevel(trimmedRating.ToString()); - } + return GetRatingLevel(rating.AsSpan().RightPart(':').ToString()); } // Remove prefix country code to handle "DE-18" - index = rating.IndexOf('-', StringComparison.Ordinal); - if (index != -1) + if (rating.Contains('-', StringComparison.OrdinalIgnoreCase)) { - var trimmedRating = rating.AsSpan(index).TrimStart('-').Trim(); - - if (!trimmedRating.IsEmpty) - { - return GetRatingLevel(trimmedRating.ToString()); - } + return GetRatingLevel(rating.AsSpan().RightPart('-').ToString()); } - // TODO: Further improve when necessary return null; } diff --git a/Emby.Server.Implementations/Localization/Ratings/be.csv b/Emby.Server.Implementations/Localization/Ratings/be.csv index 5588f63b45..d171a71328 100644 --- a/Emby.Server.Implementations/Localization/Ratings/be.csv +++ b/Emby.Server.Implementations/Localization/Ratings/be.csv @@ -6,6 +6,6 @@ MG6,6 9,9 KNT,12 12,12 -BE_14,14 +14,14 16,16 18,18 diff --git a/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs b/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs index 21f1872bc5..f30eb84213 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs @@ -27,7 +27,7 @@ namespace Jellyfin.Server.Migrations.Routines public Guid Id => Guid.Parse("{67445D54-B895-4B24-9F4C-35CE0690EA07}"); /// - public string Name => "RemoveDuplicateExtras"; + public string Name => "MigrateRatingLevels"; /// public bool PerformOnNewInstall => false; From f0251f86cb7d88495de0000d0ebca01fd9b8bbbe Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Mon, 20 Feb 2023 11:49:40 +0100 Subject: [PATCH 13/41] Move MigrateRatingLevels migration to preStartup --- Jellyfin.Server/Migrations/MigrationRunner.cs | 6 +++--- .../MigrateRatingLevels.cs | 13 +++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) rename Jellyfin.Server/Migrations/{Routines => PreStartupRoutines}/MigrateRatingLevels.cs (90%) diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index 2b15a6a1b5..d4bf81f10b 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -22,7 +22,8 @@ namespace Jellyfin.Server.Migrations private static readonly Type[] _preStartupMigrationTypes = { typeof(PreStartupRoutines.CreateNetworkConfiguration), - typeof(PreStartupRoutines.MigrateMusicBrainzTimeout) + typeof(PreStartupRoutines.MigrateMusicBrainzTimeout), + typeof(PreStartupRoutines.MigrateRatingLevels) }; /// @@ -39,8 +40,7 @@ namespace Jellyfin.Server.Migrations typeof(Routines.ReaddDefaultPluginRepository), typeof(Routines.MigrateDisplayPreferencesDb), typeof(Routines.RemoveDownloadImagesInAdvance), - typeof(Routines.MigrateAuthenticationDb), - typeof(Routines.MigrateRatingLevels) + typeof(Routines.MigrateAuthenticationDb) }; /// diff --git a/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs b/Jellyfin.Server/Migrations/PreStartupRoutines/MigrateRatingLevels.cs similarity index 90% rename from Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs rename to Jellyfin.Server/Migrations/PreStartupRoutines/MigrateRatingLevels.cs index f30eb84213..465bbd7fe1 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs +++ b/Jellyfin.Server/Migrations/PreStartupRoutines/MigrateRatingLevels.cs @@ -2,11 +2,12 @@ using System; using System.Globalization; using System.IO; +using Emby.Server.Implementations; using MediaBrowser.Controller; using Microsoft.Extensions.Logging; using SQLitePCL.pretty; -namespace Jellyfin.Server.Migrations.Routines +namespace Jellyfin.Server.Migrations.PreStartupRoutines { /// /// Migrate rating levels to new rating level system. @@ -15,12 +16,12 @@ namespace Jellyfin.Server.Migrations.Routines { private const string DbFilename = "library.db"; private readonly ILogger _logger; - private readonly IServerApplicationPaths _paths; + private readonly IServerApplicationPaths _applicationPaths; - public MigrateRatingLevels(ILogger logger, IServerApplicationPaths paths) + public MigrateRatingLevels(ServerApplicationPaths applicationPaths, ILoggerFactory loggerFactory) { - _logger = logger; - _paths = paths; + _applicationPaths = applicationPaths; + _logger = loggerFactory.CreateLogger(); } /// @@ -35,7 +36,7 @@ namespace Jellyfin.Server.Migrations.Routines /// public void Perform() { - var dataPath = _paths.DataPath; + var dataPath = _applicationPaths.DataPath; var dbPath = Path.Combine(dataPath, DbFilename); using (var connection = SQLite3.Open( dbPath, From 5f938de337537ac1e03633c82d2a27a34808d9d0 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Mon, 20 Feb 2023 11:49:46 +0100 Subject: [PATCH 14/41] Build ratingClause with StringBuilder --- .../Data/SqliteItemRepository.cs | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 055131c8e6..ecc2a2c910 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -3964,19 +3964,19 @@ namespace Emby.Server.Implementations.Data whereClauses.Add(clause); } - var ratingClause = "("; + var ratingClauseBuilder = new StringBuilder("("); if (query.HasParentalRating ?? false) { - ratingClause += "InheritedParentalRatingValue not null"; + ratingClauseBuilder.Append("InheritedParentalRatingValue not null"); if (query.MinParentalRating.HasValue) { - ratingClause += " AND InheritedParentalRatingValue >= @MinParentalRating"; + ratingClauseBuilder.Append(" AND InheritedParentalRatingValue >= @MinParentalRating"); statement?.TryBind("@MinParentalRating", query.MinParentalRating.Value); } if (query.MaxParentalRating.HasValue) { - ratingClause += " AND InheritedParentalRatingValue <= @MaxParentalRating"; + ratingClauseBuilder.Append(" AND InheritedParentalRatingValue <= @MaxParentalRating"); statement?.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); } } @@ -3985,7 +3985,7 @@ namespace Emby.Server.Implementations.Data var paramName = "@UnratedType"; var index = 0; string blockedUnratedItems = string.Join(',', query.BlockUnratedItems.Select(_ => paramName + index++)); - ratingClause += "(InheritedParentalRatingValue is null AND UnratedType not in (" + blockedUnratedItems + "))"; + ratingClauseBuilder.Append("(InheritedParentalRatingValue is null AND UnratedType not in (" + blockedUnratedItems + "))"); if (statement is not null) { @@ -3997,12 +3997,12 @@ namespace Emby.Server.Implementations.Data if (query.MinParentalRating.HasValue || query.MaxParentalRating.HasValue) { - ratingClause += " OR ("; + ratingClauseBuilder.Append(" OR ("); } if (query.MinParentalRating.HasValue) { - ratingClause += "InheritedParentalRatingValue >= @MinParentalRating"; + ratingClauseBuilder.Append("InheritedParentalRatingValue >= @MinParentalRating"); statement?.TryBind("@MinParentalRating", query.MinParentalRating.Value); } @@ -4010,49 +4010,50 @@ namespace Emby.Server.Implementations.Data { if (query.MinParentalRating.HasValue) { - ratingClause += " AND "; + ratingClauseBuilder.Append(" AND "); } - ratingClause += "InheritedParentalRatingValue <= @MaxParentalRating"; + ratingClauseBuilder.Append("InheritedParentalRatingValue <= @MaxParentalRating"); statement?.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); } if (query.MinParentalRating.HasValue || query.MaxParentalRating.HasValue) { - ratingClause += ")"; + ratingClauseBuilder.Append(")"); } if (!(query.MinParentalRating.HasValue || query.MaxParentalRating.HasValue)) { - ratingClause += " OR InheritedParentalRatingValue not null"; + ratingClauseBuilder.Append(" OR InheritedParentalRatingValue not null"); } } else if (query.MinParentalRating.HasValue) { - ratingClause += "InheritedParentalRatingValue is null OR (InheritedParentalRatingValue >= @MinParentalRating"; + ratingClauseBuilder.Append("InheritedParentalRatingValue is null OR (InheritedParentalRatingValue >= @MinParentalRating"); statement?.TryBind("@MinParentalRating", query.MinParentalRating.Value); if (query.MaxParentalRating.HasValue) { - ratingClause += " AND InheritedParentalRatingValue <= @MaxParentalRating"; + ratingClauseBuilder.Append(" AND InheritedParentalRatingValue <= @MaxParentalRating"); statement?.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); } - ratingClause += ")"; + ratingClauseBuilder.Append(")"); } else if (query.MaxParentalRating.HasValue) { - ratingClause += "InheritedParentalRatingValue is null OR InheritedParentalRatingValue <= @MaxParentalRating"; + ratingClauseBuilder.Append("InheritedParentalRatingValue is null OR InheritedParentalRatingValue <= @MaxParentalRating"); statement?.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); } else if (!query.HasParentalRating ?? false) { - ratingClause += "InheritedParentalRatingValue is null"; + ratingClauseBuilder.Append("InheritedParentalRatingValue is null"); } - if (!string.Equals(ratingClause, "(", StringComparison.OrdinalIgnoreCase)) + var ratingClauseString = ratingClauseBuilder.ToString(); + if (!string.Equals(ratingClauseString, "(", StringComparison.OrdinalIgnoreCase)) { - whereClauses.Add(ratingClause + ")"); + whereClauses.Add(ratingClauseString + ")"); } if (query.HasOfficialRating.HasValue) From e7a7edbac02f1cc0818b3f3614f55e579733905e Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Mon, 20 Feb 2023 16:04:30 +0100 Subject: [PATCH 15/41] Various fixes (#9361) --- .../Plugins/AudioDb/Configuration/config.html | 5 +++-- .../Configuration/PluginConfiguration.cs | 12 ++++++++--- .../MusicBrainz/Configuration/config.html | 21 ++++++++++++------- .../MusicBrainz/MusicBrainzAlbumProvider.cs | 2 +- .../MusicBrainz/MusicBrainzArtistProvider.cs | 2 +- .../Plugins/Omdb/Configuration/config.html | 9 ++++---- .../StudioImages/Configuration/config.html | 3 ++- .../Plugins/Tmdb/Configuration/config.html | 3 ++- 8 files changed, 36 insertions(+), 21 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html index eab252005f..2093effca6 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html +++ b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html @@ -1,12 +1,13 @@  - AudioDB + TheAudioDB -
+
+

TheAudioDB

public class PluginConfiguration : BasePluginConfiguration { - private const string DefaultServer = "https://musicbrainz.org"; + /// + /// The default server URL. + /// + public const string DefaultServer = "https://musicbrainz.org"; - private const double DefaultRateLimit = 1.0; + /// + /// The default rate limit. + /// + public const double DefaultRateLimit = 1.0; private string _server = DefaultServer; private double _rateLimit = DefaultRateLimit; /// - /// Gets or sets the server url. + /// Gets or sets the server URL. /// public string Server { diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html index 62d86cd8fa..24f2ac0ca9 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html @@ -1,9 +1,14 @@ -
-
-
-

MusicBrainz

- + + + + MusicBrainz + + +
+
+
+

MusicBrainz

+
This can be a mirror of the official server or even a custom server.
@@ -28,7 +33,7 @@ uniquePluginId: "8c95c4d2-e50c-4fb0-a4f3-6c06ff0f9a1a" }; - document.querySelector('.musicBrainzConfigurationPage') + document.querySelector('.configPage') .addEventListener('pageshow', function () { Dashboard.showLoadingMsg(); ApiClient.getPluginConfiguration(MusicBrainzPluginConfig.uniquePluginId).then(function (config) { @@ -52,7 +57,7 @@ }); }); - document.querySelector('.musicBrainzConfigurationForm') + document.querySelector('.configForm') .addEventListener('submit', function (e) { Dashboard.showLoadingMsg(); diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs index 3afa90baeb..4aa4269891 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs @@ -58,7 +58,7 @@ public class MusicBrainzAlbumProvider : IRemoteMetadataProviderOMDb -
+
+

OMDb

- private static readonly Regex _seriesNameRegex = new Regex(@"((?[^\._]{2,})[\._]*)|([\._](?[^\._]{2,}))"); + private static readonly Regex _seriesNameRegex = new Regex(@"((?[^\._]{2,})[\._]*)|([\._](?[^\._]{2,}))", RegexOptions.Compiled); /// /// Resolve information about series from path. diff --git a/Emby.Naming/Video/FileStackRule.cs b/Emby.Naming/Video/FileStackRule.cs index 76b487f428..be0f79d33a 100644 --- a/Emby.Naming/Video/FileStackRule.cs +++ b/Emby.Naming/Video/FileStackRule.cs @@ -17,7 +17,7 @@ public class FileStackRule /// Whether the file stack rule uses numerical or alphabetical numbering. public FileStackRule(string token, bool isNumerical) { - _tokenRegex = new Regex(token, RegexOptions.IgnoreCase); + _tokenRegex = new Regex(token, RegexOptions.IgnoreCase | RegexOptions.Compiled); IsNumerical = isNumerical; } diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs index 8247c374d0..6209cd46f4 100644 --- a/Emby.Naming/Video/VideoListResolver.cs +++ b/Emby.Naming/Video/VideoListResolver.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; using Emby.Naming.Common; +using Jellyfin.Extensions; using MediaBrowser.Model.IO; namespace Emby.Naming.Video @@ -13,6 +14,8 @@ namespace Emby.Naming.Video /// public static class VideoListResolver { + private static readonly Regex _resolutionRegex = new Regex("[0-9]{2}[0-9]+[ip]", RegexOptions.IgnoreCase | RegexOptions.Compiled); + /// /// Resolves alternative versions and extras from list of video files. /// @@ -115,19 +118,34 @@ namespace Emby.Naming.Video continue; } - if (!IsEligibleForMultiVersion(folderName, video.Files[0].Path, namingOptions)) + if (!IsEligibleForMultiVersion(folderName, video.Files[0].FileNameWithoutExtension, namingOptions)) { return videos; } - if (folderName.Equals(Path.GetFileNameWithoutExtension(video.Files[0].Path.AsSpan()), StringComparison.Ordinal)) + if (folderName.Equals(video.Files[0].FileNameWithoutExtension, StringComparison.Ordinal)) { primary = video; } } - // The list is created and overwritten in the caller, so we are allowed to do in-place sorting - videos.Sort((x, y) => string.Compare(x.Name, y.Name, StringComparison.Ordinal)); + if (videos.Count > 1) + { + var groups = videos.GroupBy(x => _resolutionRegex.IsMatch(x.Files[0].FileNameWithoutExtension)).ToList(); + videos.Clear(); + foreach (var group in groups) + { + if (group.Key) + { + videos.InsertRange(0, group.OrderByDescending(x => x.Files[0].FileNameWithoutExtension.ToString(), new AlphanumericComparator())); + } + else + { + videos.AddRange(group.OrderBy(x => x.Files[0].FileNameWithoutExtension.ToString(), new AlphanumericComparator())); + } + } + } + primary ??= videos[0]; videos.Remove(primary); @@ -161,9 +179,8 @@ namespace Emby.Naming.Video return true; } - private static bool IsEligibleForMultiVersion(ReadOnlySpan folderName, string testFilePath, NamingOptions namingOptions) + private static bool IsEligibleForMultiVersion(ReadOnlySpan folderName, ReadOnlySpan testFilename, NamingOptions namingOptions) { - var testFilename = Path.GetFileNameWithoutExtension(testFilePath.AsSpan()); if (!testFilename.StartsWith(folderName, StringComparison.OrdinalIgnoreCase)) { return false; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs index 80d9d07247..3450f971fc 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs @@ -13,8 +13,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun public LegacyHdHomerunChannelCommands(string url) { // parse url for channel and program - var regExp = new Regex(@"\/ch([0-9]+)-?([0-9]*)"); - var match = regExp.Match(url); + var match = Regex.Match(url, @"\/ch([0-9]+)-?([0-9]*)"); if (match.Success) { _channel = match.Groups[1].Value; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index 1c0ed6505c..046be7c5c7 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -308,8 +308,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts { var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); - var reg = new Regex(@"([a-z0-9\-_]+)=\""([^""]+)\""", RegexOptions.IgnoreCase); - var matches = reg.Matches(line); + var matches = Regex.Matches(line, @"([a-z0-9\-_]+)=\""([^""]+)\""", RegexOptions.IgnoreCase); remaining = line; diff --git a/src/Jellyfin.Extensions/StringExtensions.cs b/src/Jellyfin.Extensions/StringExtensions.cs index f30b639459..7c61248757 100644 --- a/src/Jellyfin.Extensions/StringExtensions.cs +++ b/src/Jellyfin.Extensions/StringExtensions.cs @@ -12,7 +12,7 @@ namespace Jellyfin.Extensions { // Matches non-conforming unicode chars // https://mnaoumov.wordpress.com/2014/06/14/stripping-invalid-characters-from-utf-16-strings/ - private static readonly Regex _nonConformingUnicode = new Regex("([\ud800-\udbff](?![\udc00-\udfff]))|((? /// Removes the diacritics character from the strings. diff --git a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs index 02e6f6368c..294f11ee74 100644 --- a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs @@ -188,8 +188,7 @@ namespace Jellyfin.Naming.Tests.Video @"/movies/Iron Man/Iron Man-bluray.mkv", @"/movies/Iron Man/Iron Man-3d.mkv", @"/movies/Iron Man/Iron Man-3d-hsbs.mkv", - @"/movies/Iron Man/Iron Man-3d.hsbs.mkv", - @"/movies/Iron Man/Iron Man[test].mkv", + @"/movies/Iron Man/Iron Man[test].mkv" }; var result = VideoListResolver.Resolve( @@ -197,10 +196,14 @@ namespace Jellyfin.Naming.Tests.Video _namingOptions).ToList(); Assert.Single(result); - Assert.Equal(7, result[0].AlternateVersions.Count); - Assert.False(result[0].AlternateVersions[2].Is3D); - Assert.True(result[0].AlternateVersions[3].Is3D); - Assert.True(result[0].AlternateVersions[4].Is3D); + Assert.Equal("/movies/Iron Man/Iron Man.mkv", result[0].Files[0].Path); + Assert.Equal(6, result[0].AlternateVersions.Count); + Assert.Equal("/movies/Iron Man/Iron Man-720p.mkv", result[0].AlternateVersions[0].Path); + Assert.Equal("/movies/Iron Man/Iron Man-3d.mkv", result[0].AlternateVersions[1].Path); + Assert.Equal("/movies/Iron Man/Iron Man-3d-hsbs.mkv", result[0].AlternateVersions[2].Path); + Assert.Equal("/movies/Iron Man/Iron Man-bluray.mkv", result[0].AlternateVersions[3].Path); + Assert.Equal("/movies/Iron Man/Iron Man-test.mkv", result[0].AlternateVersions[4].Path); + Assert.Equal("/movies/Iron Man/Iron Man[test].mkv", result[0].AlternateVersions[5].Path); } [Fact] @@ -214,7 +217,6 @@ namespace Jellyfin.Naming.Tests.Video @"/movies/Iron Man/Iron Man - bluray.mkv", @"/movies/Iron Man/Iron Man - 3d.mkv", @"/movies/Iron Man/Iron Man - 3d-hsbs.mkv", - @"/movies/Iron Man/Iron Man - 3d.hsbs.mkv", @"/movies/Iron Man/Iron Man [test].mkv" }; @@ -223,10 +225,14 @@ namespace Jellyfin.Naming.Tests.Video _namingOptions).ToList(); Assert.Single(result); - Assert.Equal(7, result[0].AlternateVersions.Count); - Assert.False(result[0].AlternateVersions[3].Is3D); - Assert.True(result[0].AlternateVersions[4].Is3D); - Assert.True(result[0].AlternateVersions[5].Is3D); + Assert.Equal("/movies/Iron Man/Iron Man.mkv", result[0].Files[0].Path); + Assert.Equal(6, result[0].AlternateVersions.Count); + Assert.Equal("/movies/Iron Man/Iron Man - 720p.mkv", result[0].AlternateVersions[0].Path); + Assert.Equal("/movies/Iron Man/Iron Man - 3d.mkv", result[0].AlternateVersions[1].Path); + Assert.Equal("/movies/Iron Man/Iron Man - 3d-hsbs.mkv", result[0].AlternateVersions[2].Path); + Assert.Equal("/movies/Iron Man/Iron Man - bluray.mkv", result[0].AlternateVersions[3].Path); + Assert.Equal("/movies/Iron Man/Iron Man - test.mkv", result[0].AlternateVersions[4].Path); + Assert.Equal("/movies/Iron Man/Iron Man [test].mkv", result[0].AlternateVersions[5].Path); } [Fact] @@ -328,8 +334,12 @@ namespace Jellyfin.Naming.Tests.Video { var files = new[] { + @"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - Theatrical Release.mkv", + @"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - Directors Cut.mkv", @"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - 1080p.mkv", - @"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016).mkv" + @"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - 2160p.mkv", + @"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - 720p.mkv", + @"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016).mkv", }; var result = VideoListResolver.Resolve( @@ -338,8 +348,12 @@ namespace Jellyfin.Naming.Tests.Video Assert.Single(result); Assert.Equal("/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016).mkv", result[0].Files[0].Path); - Assert.Single(result[0].AlternateVersions); - Assert.Equal("/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - 1080p.mkv", result[0].AlternateVersions[0].Path); + Assert.Equal(5, result[0].AlternateVersions.Count); + Assert.Equal("/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - 2160p.mkv", result[0].AlternateVersions[0].Path); + Assert.Equal("/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - 1080p.mkv", result[0].AlternateVersions[1].Path); + Assert.Equal("/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - 720p.mkv", result[0].AlternateVersions[2].Path); + Assert.Equal("/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - Directors Cut.mkv", result[0].AlternateVersions[3].Path); + Assert.Equal("/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - Theatrical Release.mkv", result[0].AlternateVersions[4].Path); } [Fact] From cf29e9a9c587055381d839d9afeb5593a5dcd683 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 20 Feb 2023 20:32:49 +0100 Subject: [PATCH 17/41] Fix #7516 --- Emby.Dlna/Server/DescriptionXmlBuilder.cs | 26 ++++++---- .../Server/DescriptionXmlBuilderTests.cs | 47 +++++++++++++++++++ 2 files changed, 65 insertions(+), 8 deletions(-) create mode 100644 tests/Jellyfin.Dlna.Tests/Server/DescriptionXmlBuilderTests.cs diff --git a/Emby.Dlna/Server/DescriptionXmlBuilder.cs b/Emby.Dlna/Server/DescriptionXmlBuilder.cs index d00df781d6..69ef6f6456 100644 --- a/Emby.Dlna/Server/DescriptionXmlBuilder.cs +++ b/Emby.Dlna/Server/DescriptionXmlBuilder.cs @@ -147,11 +147,16 @@ namespace Emby.Dlna.Server } } - private string GetFriendlyName() + internal string GetFriendlyName() { if (string.IsNullOrEmpty(_profile.FriendlyName)) { - return "Jellyfin - " + _serverName; + return _serverName; + } + + if (!_profile.FriendlyName.Contains("${HostName}", StringComparison.OrdinalIgnoreCase)) + { + return _profile.FriendlyName; } var characterList = new List(); @@ -164,13 +169,18 @@ namespace Emby.Dlna.Server } } - var characters = characterList.ToArray(); - - var serverName = new string(characters); - - var name = _profile.FriendlyName?.Replace("${HostName}", serverName, StringComparison.OrdinalIgnoreCase); + var serverName = string.Create( + characterList.Count, + characterList, + (dest, source) => + { + for (int i = 0; i < dest.Length; i++) + { + dest[i] = source[i]; + } + }); - return name ?? string.Empty; + return _profile.FriendlyName.Replace("${HostName}", serverName, StringComparison.OrdinalIgnoreCase); } private void AppendIconList(StringBuilder builder) diff --git a/tests/Jellyfin.Dlna.Tests/Server/DescriptionXmlBuilderTests.cs b/tests/Jellyfin.Dlna.Tests/Server/DescriptionXmlBuilderTests.cs new file mode 100644 index 0000000000..c9018fe2f4 --- /dev/null +++ b/tests/Jellyfin.Dlna.Tests/Server/DescriptionXmlBuilderTests.cs @@ -0,0 +1,47 @@ +using Emby.Dlna.Server; +using MediaBrowser.Model.Dlna; +using Xunit; + +namespace Jellyfin.Dlna.Server.Tests; + +public class DescriptionXmlBuilderTests +{ + [Fact] + public void GetFriendlyName_EmptyProfile_ReturnsServerName() + { + const string ServerName = "Test Server Name"; + var builder = new DescriptionXmlBuilder(new DeviceProfile(), "serverUdn", "localhost", ServerName, string.Empty); + Assert.Equal(ServerName, builder.GetFriendlyName()); + } + + [Fact] + public void GetFriendlyName_FriendlyName_ReturnsFriendlyName() + { + const string FriendlyName = "Friendly Neighborhood Test Server"; + var builder = new DescriptionXmlBuilder( + new DeviceProfile() + { + FriendlyName = FriendlyName + }, + "serverUdn", + "localhost", + "Test Server Name", + string.Empty); + Assert.Equal(FriendlyName, builder.GetFriendlyName()); + } + + [Fact] + public void GetFriendlyName_FriendlyNameInterpolation_ReturnsFriendlyName() + { + var builder = new DescriptionXmlBuilder( + new DeviceProfile() + { + FriendlyName = "Friendly Neighborhood ${HostName}" + }, + "serverUdn", + "localhost", + "Test Server Name", + string.Empty); + Assert.Equal("Friendly Neighborhood TestServerName", builder.GetFriendlyName()); + } +} From 6300d01fcceba56932741251443f5b2aa4f76de2 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Mon, 20 Feb 2023 21:58:31 +0100 Subject: [PATCH 18/41] Apply review suggestion --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index ecc2a2c910..0aa943270e 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -3623,8 +3623,12 @@ namespace Emby.Server.Implementations.Data clauseBuilder.Append("(guid in (select itemid from People where Name = (select Name from TypedBaseItems where guid=") .Append(paramName) .Append("))) OR "); - query.PersonIds[i].TryWriteBytes(idBytes); - statement?.TryBind(paramName, idBytes); + + if (statement is not null) + { + query.PersonIds[i].TryWriteBytes(idBytes); + statement.TryBind(paramName, idBytes); + } } // Remove last " OR " From 37560784670ea12b63bfc0d573c42e9599f776da Mon Sep 17 00:00:00 2001 From: Asahi Oka Date: Sun, 19 Feb 2023 20:55:16 +0000 Subject: [PATCH 19/41] Translated using Weblate (Spanish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es/ --- Emby.Server.Implementations/Localization/Core/es.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json index afffdf3bfa..5e41462db1 100644 --- a/Emby.Server.Implementations/Localization/Core/es.json +++ b/Emby.Server.Implementations/Localization/Core/es.json @@ -31,7 +31,7 @@ "ItemRemovedWithName": "{0} ha sido eliminado de la biblioteca", "LabelIpAddressValue": "Dirección IP: {0}", "LabelRunningTimeValue": "Tiempo de funcionamiento: {0}", - "Latest": "Últimos", + "Latest": "Último contenido en", "MessageApplicationUpdated": "Se ha actualizado el servidor Jellyfin", "MessageApplicationUpdatedTo": "Se ha actualizado el servidor Jellyfin a la versión {0}", "MessageNamedServerConfigurationUpdatedWithValue": "La sección {0} de configuración del servidor ha sido actualizada", From e148f9e7173b4c6aba0096f5cc91de976664c60f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 21 Feb 2023 14:29:49 +0000 Subject: [PATCH 20/41] chore(deps): update dependency microsoft.net.test.sdk to v17.5.0 --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 4024f0c057..da8f636e50 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -45,7 +45,7 @@ - + From 88ab6bfdfc010ad7b42a01ac1f65b7df4a846c2a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 21 Feb 2023 21:28:46 -0700 Subject: [PATCH 21/41] chore(deps): update dependency autofixture to v4.18.0 (#9370) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index da8f636e50..5060b3de56 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -8,7 +8,7 @@ - + From b6ce9206703543b1b55b1da88208758cd56e41a5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 22 Feb 2023 04:30:10 +0000 Subject: [PATCH 22/41] chore(deps): update dependency autofixture.automoq to v4.18.0 --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 5060b3de56..6e997d7064 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -6,7 +6,7 @@ - + From 0af699621aa4794866c24ed9315318ebee66356a Mon Sep 17 00:00:00 2001 From: lyaschuchenko Date: Tue, 21 Feb 2023 05:46:11 +0000 Subject: [PATCH 23/41] Translated using Weblate (Ukrainian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/uk/ --- Emby.Server.Implementations/Localization/Core/uk.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/uk.json b/Emby.Server.Implementations/Localization/Core/uk.json index 92ce616f2e..ff77fb8c56 100644 --- a/Emby.Server.Implementations/Localization/Core/uk.json +++ b/Emby.Server.Implementations/Localization/Core/uk.json @@ -86,7 +86,7 @@ "Shows": "Шоу", "ServerNameNeedsToBeRestarted": "{0} потрібно перезапустити", "ScheduledTaskStartedWithName": "{0} розпочато", - "ScheduledTaskFailedWithName": "Помилка {0}", + "ScheduledTaskFailedWithName": "{0} незавершено, збій", "ProviderValue": "Постачальник: {0}", "PluginUpdatedWithName": "{0} оновлено", "PluginUninstalledWithName": "{0} видалено", From eb3d187f27c34112689d37c50a1f734835a33ef3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 22 Feb 2023 05:48:43 -0700 Subject: [PATCH 24/41] chore(deps): update dependency autofixture.xunit2 to v4.18.0 (#9372) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 6e997d7064..c2a4d5ff3d 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -7,7 +7,7 @@ - + From ab24c0e2cf0e678146474db10ecb8e5f86764a10 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 23 Feb 2023 19:09:16 +0100 Subject: [PATCH 25/41] Enable nullable for more files --- Jellyfin.Api/Controllers/GenresController.cs | 9 +++------ MediaBrowser.Controller/Subtitles/ISubtitleManager.cs | 2 -- .../Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs | 2 -- .../Plugins/Tmdb/Movies/TmdbMovieProvider.cs | 2 -- .../Plugins/Tmdb/People/TmdbPersonProvider.cs | 2 -- .../Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs | 4 +--- .../Plugins/Tmdb/TV/TmdbEpisodeProvider.cs | 4 +--- .../Plugins/Tmdb/TV/TmdbSeriesProvider.cs | 2 -- MediaBrowser.Providers/Subtitles/SubtitleManager.cs | 8 +++----- MediaBrowser.Providers/TV/SeriesMetadataService.cs | 2 -- 10 files changed, 8 insertions(+), 29 deletions(-) diff --git a/Jellyfin.Api/Controllers/GenresController.cs b/Jellyfin.Api/Controllers/GenresController.cs index eb03b514c7..da60f2c60b 100644 --- a/Jellyfin.Api/Controllers/GenresController.cs +++ b/Jellyfin.Api/Controllers/GenresController.cs @@ -172,12 +172,9 @@ public class GenresController : BaseJellyfinApiController item ??= new Genre(); - if (userId.Value.Equals(default)) - { - return _dtoService.GetBaseItemDto(item, dtoOptions); - } - - var user = _userManager.GetUserById(userId.Value); + var user = userId.Value.Equals(default) + ? null + : _userManager.GetUserById(userId.Value); return _dtoService.GetBaseItemDto(item, dtoOptions, user); } diff --git a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs index 841b320376..b86e482435 100644 --- a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs +++ b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs @@ -1,5 +1,3 @@ -#nullable disable - #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs index 02601d3f56..655fa5a16d 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.Globalization; diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs index 9eced93fa5..753a15c6ee 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.Globalization; diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs index b3709baf58..44d5bab76d 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.Globalization; diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs index 5259faf76f..28da62111e 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.Globalization; @@ -63,7 +61,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV var seriesTmdbId = Convert.ToInt32(series?.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); - if (seriesTmdbId <= 0) + if (series == null || seriesTmdbId <= 0) { return Enumerable.Empty(); } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs index 35e304a2ac..66decde842 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.Globalization; @@ -87,7 +85,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV return metadataResult; } - info.SeriesProviderIds.TryGetValue(MetadataProvider.Tmdb.ToString(), out string tmdbId); + info.SeriesProviderIds.TryGetValue(MetadataProvider.Tmdb.ToString(), out string? tmdbId); var seriesTmdbId = Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture); if (seriesTmdbId <= 0) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs index 9590882105..046b63faf8 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.Globalization; diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs index d89fb814d8..0c01c50317 100644 --- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs +++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs @@ -1,5 +1,3 @@ -#nullable disable - #pragma warning disable CS1591 using System; @@ -56,7 +54,7 @@ namespace MediaBrowser.Providers.Subtitles } /// - public event EventHandler SubtitleDownloadFailure; + public event EventHandler? SubtitleDownloadFailure; /// public async Task SearchSubtitles(SubtitleSearchRequest request, CancellationToken cancellationToken) @@ -235,7 +233,7 @@ namespace MediaBrowser.Providers.Subtitles private async Task TrySaveToFiles(Stream stream, List savePaths) { - List exs = null; + List? exs = null; foreach (var savePath in savePaths) { @@ -245,7 +243,7 @@ namespace MediaBrowser.Providers.Subtitles try { - Directory.CreateDirectory(Path.GetDirectoryName(savePath)); + Directory.CreateDirectory(Path.GetDirectoryName(savePath) ?? throw new InvalidOperationException("Path can't be a root directory.")); var fileOptions = AsyncFile.WriteOptions; fileOptions.Mode = FileMode.CreateNew; diff --git a/MediaBrowser.Providers/TV/SeriesMetadataService.cs b/MediaBrowser.Providers/TV/SeriesMetadataService.cs index a261d7cdb5..97f9383971 100644 --- a/MediaBrowser.Providers/TV/SeriesMetadataService.cs +++ b/MediaBrowser.Providers/TV/SeriesMetadataService.cs @@ -1,5 +1,3 @@ -#nullable disable - #pragma warning disable CS1591 using System.Collections.Generic; From a328dba3b16adb1707a8b7fb504cc47eabed18d8 Mon Sep 17 00:00:00 2001 From: Pranav Avva Date: Wed, 22 Feb 2023 20:38:26 +0000 Subject: [PATCH 26/41] Translated using Weblate (Hindi) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hi/ --- Emby.Server.Implementations/Localization/Core/hi.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/hi.json b/Emby.Server.Implementations/Localization/Core/hi.json index 182b43ffca..a0e2f04a16 100644 --- a/Emby.Server.Implementations/Localization/Core/hi.json +++ b/Emby.Server.Implementations/Localization/Core/hi.json @@ -67,5 +67,11 @@ "Plugin": "प्लग-इन", "Playlists": "प्लेलिस्ट", "Photos": "तस्वीरें", - "External": "बाहरी" + "External": "बाहरी", + "PluginUpdatedWithName": "{0} अपडेट हुए", + "ScheduledTaskStartedWithName": "{0} शुरू हुए", + "Songs": "गाने", + "UserStartedPlayingItemWithValues": "{0} {2} पर {1} खेल रहे हैं", + "UserStoppedPlayingItemWithValues": "{0} ने {2} पर {1} खेलना खत्म किया", + "StartupEmbyServerIsLoading": "जेलीफ़िन सर्वर लोड हो रहा है। कृपया शीघ्र ही पुन: प्रयास करें।" } From f94abc1eb7352404e0250060ed0c57ad369d2457 Mon Sep 17 00:00:00 2001 From: Nick <20588554+nicknsy@users.noreply.github.com> Date: Fri, 24 Feb 2023 06:06:01 -0800 Subject: [PATCH 27/41] Copy IsAutomated option when making MetadataRefreshOptions copy. (#9385) --- MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs index 8a37094620..9e91a8bcd7 100644 --- a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs +++ b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs @@ -26,6 +26,7 @@ namespace MediaBrowser.Controller.Providers ReplaceAllMetadata = copy.ReplaceAllMetadata; EnableRemoteContentProbe = copy.EnableRemoteContentProbe; + IsAutomated = copy.IsAutomated; ImageRefreshMode = copy.ImageRefreshMode; ReplaceAllImages = copy.ReplaceAllImages; ReplaceImages = copy.ReplaceImages; From b3273f0f9ac73c466dee47240f00a2b5890b5595 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Fri, 24 Feb 2023 08:06:19 -0700 Subject: [PATCH 28/41] Simplify audio transcode channel lookup --- .../MediaEncoding/EncodingHelper.cs | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index f4684a2218..a4714e7bf8 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -71,6 +71,21 @@ namespace MediaBrowser.Controller.MediaEncoding "m4v", }; + // Set max transcoding channels for encoders that can't handle more than a set amount of channels + // AAC, FLAC, ALAC, libopus, libvorbis encoders all support at least 8 channels + private static readonly Dictionary _audioTranscodeChannelLookup = new(StringComparer.OrdinalIgnoreCase) + { + { "wmav2", 2 }, + { "libmp3lame", 2 }, + { "libfdk_aac", 6 }, + { "aac_at", 6 }, + { "ac3", 6 }, + { "eac3", 6 }, + { "dts", 6 }, + { "mlp", 6 }, + { "truehd", 6 }, + }; + public EncodingHelper( IApplicationPaths appPaths, IMediaEncoder mediaEncoder, @@ -2231,25 +2246,14 @@ namespace MediaBrowser.Controller.MediaEncoding if (isTranscodingAudio) { - // Set max transcoding channels for encoders that can't handle more than a set amount of channels - // AAC, FLAC, ALAC, libopus, libvorbis encoders all support at least 8 channels - int transcoderChannelLimit = GetAudioEncoder(state) switch - { - string audioEncoder when audioEncoder.Equals("wmav2", StringComparison.OrdinalIgnoreCase) - || audioEncoder.Equals("libmp3lame", StringComparison.OrdinalIgnoreCase) => 2, - string audioEncoder when audioEncoder.Equals("libfdk_aac", StringComparison.OrdinalIgnoreCase) - || audioEncoder.Equals("aac_at", StringComparison.OrdinalIgnoreCase) - || audioEncoder.Equals("ac3", StringComparison.OrdinalIgnoreCase) - || audioEncoder.Equals("eac3", StringComparison.OrdinalIgnoreCase) - || audioEncoder.Equals("dts", StringComparison.OrdinalIgnoreCase) - || audioEncoder.Equals("mlp", StringComparison.OrdinalIgnoreCase) - || audioEncoder.Equals("truehd", StringComparison.OrdinalIgnoreCase) => 6, - // Set default max transcoding channels to 8 to prevent encoding errors due to asking for too many channels - _ => 8, - }; + var audioEncoder = GetAudioEncoder(state); + if (!_audioTranscodeChannelLookup.TryGetValue(audioEncoder, out var transcoderChannelLimit)) + { + // Set default max transcoding channels to 8 to prevent encoding errors due to asking for too many channels. + transcoderChannelLimit = 8; + } // Set resultChannels to minimum between resultChannels, TranscodingMaxAudioChannels, transcoderChannelLimit - resultChannels = transcoderChannelLimit < resultChannels ? transcoderChannelLimit : resultChannels ?? transcoderChannelLimit; if (request.TranscodingMaxAudioChannels < resultChannels) From eaeb65f94d94bab40126c12e8fd89c3c5c6b35d8 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Fri, 24 Feb 2023 16:22:30 +0100 Subject: [PATCH 29/41] Update MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs Co-authored-by: Cody Robibero --- .../Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs index 28da62111e..abef732bbb 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs @@ -61,7 +61,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV var seriesTmdbId = Convert.ToInt32(series?.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); - if (series == null || seriesTmdbId <= 0) + if (series is null || seriesTmdbId <= 0) { return Enumerable.Empty(); } From 49eb04899c1e4f0864674b475a87a26e6dddbd58 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Fri, 24 Feb 2023 08:53:08 -0700 Subject: [PATCH 30/41] Update MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs Co-authored-by: Shadowghost --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index a4714e7bf8..e8d011a153 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -81,7 +81,7 @@ namespace MediaBrowser.Controller.MediaEncoding { "aac_at", 6 }, { "ac3", 6 }, { "eac3", 6 }, - { "dts", 6 }, + { "dca", 6 }, { "mlp", 6 }, { "truehd", 6 }, }; From c29e8ffe1d1ffe99c895ad228b6cfc5c18fb3b98 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Fri, 24 Feb 2023 11:45:56 -0500 Subject: [PATCH 31/41] Update MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs Co-authored-by: Cody Robibero --- MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs index 48ee78a7cc..0524999c79 100644 --- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs +++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs @@ -235,7 +235,7 @@ namespace MediaBrowser.Controller.Net catch (Exception ex) { // TODO Investigate and properly fix. - Logger.LogError(ex, "Object Disposed Exception"); + Logger.LogError(ex, "Error disposing websocket"); } lock (_activeConnections) From f8f8505286b7b6865b46eb4a500904a79077ff86 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 24 Feb 2023 14:48:33 -0700 Subject: [PATCH 32/41] chore(deps): update github/codeql-action digest to 32dc499 (#9392) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 41d59e2435..6d87af538c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -27,11 +27,11 @@ jobs: dotnet-version: '7.0.x' - name: Initialize CodeQL - uses: github/codeql-action/init@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2 + uses: github/codeql-action/init@32dc499307d133bb5085bae78498c0ac2cf762d5 # v2 with: languages: ${{ matrix.language }} queries: +security-extended - name: Autobuild - uses: github/codeql-action/autobuild@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2 + uses: github/codeql-action/autobuild@32dc499307d133bb5085bae78498c0ac2cf762d5 # v2 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2 + uses: github/codeql-action/analyze@32dc499307d133bb5085bae78498c0ac2cf762d5 # v2 From e35119987a80f5048572889f4a0153eba148a1d1 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 25 Feb 2023 17:20:53 +0100 Subject: [PATCH 33/41] Enable nullable for more files --- .../Tmdb/BoxSets/TmdbBoxSetImageProvider.cs | 2 - .../Tmdb/BoxSets/TmdbBoxSetProvider.cs | 2 - .../Plugins/Tmdb/Movies/TmdbMovieProvider.cs | 51 ++++++++++-------- .../Plugins/Tmdb/People/TmdbPersonProvider.cs | 4 ++ .../Plugins/Tmdb/TV/TmdbSeriesProvider.cs | 9 +++- .../Plugins/Tmdb/TmdbClientManager.cs | 52 +++++++++---------- .../Plugins/Tmdb/TmdbUtils.cs | 4 +- 7 files changed, 67 insertions(+), 57 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs index eee3658de5..ef32b0a074 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.Globalization; diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs index 1cce7fc35a..e6c7dba250 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.Globalization; diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs index 753a15c6ee..fc72023666 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs @@ -62,32 +62,35 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies cancellationToken) .ConfigureAwait(false); - var remoteResult = new RemoteSearchResult + if (movie is not null) { - Name = movie.Title ?? movie.OriginalTitle, - SearchProviderName = Name, - ImageUrl = _tmdbClientManager.GetPosterUrl(movie.PosterPath), - Overview = movie.Overview - }; + var remoteResult = new RemoteSearchResult + { + Name = movie.Title ?? movie.OriginalTitle, + SearchProviderName = Name, + ImageUrl = _tmdbClientManager.GetPosterUrl(movie.PosterPath), + Overview = movie.Overview + }; - if (movie.ReleaseDate is not null) - { - var releaseDate = movie.ReleaseDate.Value.ToUniversalTime(); - remoteResult.PremiereDate = releaseDate; - remoteResult.ProductionYear = releaseDate.Year; - } + if (movie.ReleaseDate is not null) + { + var releaseDate = movie.ReleaseDate.Value.ToUniversalTime(); + remoteResult.PremiereDate = releaseDate; + remoteResult.ProductionYear = releaseDate.Year; + } - remoteResult.SetProviderId(MetadataProvider.Tmdb, movie.Id.ToString(CultureInfo.InvariantCulture)); + remoteResult.SetProviderId(MetadataProvider.Tmdb, movie.Id.ToString(CultureInfo.InvariantCulture)); - if (!string.IsNullOrWhiteSpace(movie.ImdbId)) - { - remoteResult.SetProviderId(MetadataProvider.Imdb, movie.ImdbId); - } + if (!string.IsNullOrWhiteSpace(movie.ImdbId)) + { + remoteResult.SetProviderId(MetadataProvider.Imdb, movie.ImdbId); + } - return new[] { remoteResult }; + return new[] { remoteResult }; + } } - IReadOnlyList movieResults; + IReadOnlyList? movieResults = null; if (searchInfo.TryGetProviderId(MetadataProvider.Imdb, out id)) { var result = await _tmdbClientManager.FindByExternalIdAsync( @@ -95,18 +98,20 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies FindExternalSource.Imdb, TmdbUtils.GetImageLanguagesParam(searchInfo.MetadataLanguage), cancellationToken).ConfigureAwait(false); - movieResults = result.MovieResults; + movieResults = result?.MovieResults; } - else if (searchInfo.TryGetProviderId(MetadataProvider.Tvdb, out id)) + + if (movieResults is null && searchInfo.TryGetProviderId(MetadataProvider.Tvdb, out id)) { var result = await _tmdbClientManager.FindByExternalIdAsync( id, FindExternalSource.TvDb, TmdbUtils.GetImageLanguagesParam(searchInfo.MetadataLanguage), cancellationToken).ConfigureAwait(false); - movieResults = result.MovieResults; + movieResults = result?.MovieResults; } - else + + if (movieResults is null) { movieResults = await _tmdbClientManager .SearchMovieAsync(searchInfo.Name, searchInfo.Year ?? 0, searchInfo.MetadataLanguage, cancellationToken) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs index 44d5bab76d..c03a1ca3b8 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs @@ -105,6 +105,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People if (personTmdbId > 0) { var person = await _tmdbClientManager.GetPersonAsync(personTmdbId, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); + if (person is null) + { + return result; + } result.HasMetadata = true; diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs index 046b63faf8..09d1a739d2 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs @@ -209,7 +209,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV } } - if (string.IsNullOrEmpty(tmdbId)) + if (!int.TryParse(tmdbId, CultureInfo.InvariantCulture, out int tmdbIdInt)) { return result; } @@ -217,9 +217,14 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV cancellationToken.ThrowIfCancellationRequested(); var tvShow = await _tmdbClientManager - .GetSeriesAsync(Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture), info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage), cancellationToken) + .GetSeriesAsync(tmdbIdInt, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage), cancellationToken) .ConfigureAwait(false); + if (tvShow is null) + { + return result; + } + result = new MetadataResult { Item = MapTvShowToSeries(tvShow, info.MetadataCountryCode), diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs index c7441bf357..500ebaf71c 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs @@ -1,6 +1,4 @@ -#nullable disable - -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Threading; @@ -50,10 +48,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// A comma-separated list of image languages. /// The cancellation token. /// The TMDb movie or null if not found. - public async Task GetMovieAsync(int tmdbId, string language, string imageLanguages, CancellationToken cancellationToken) + public async Task GetMovieAsync(int tmdbId, string? language, string? imageLanguages, CancellationToken cancellationToken) { var key = $"movie-{tmdbId.ToString(CultureInfo.InvariantCulture)}-{language}"; - if (_memoryCache.TryGetValue(key, out Movie movie)) + if (_memoryCache.TryGetValue(key, out Movie? movie)) { return movie; } @@ -89,10 +87,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// A comma-separated list of image languages. /// The cancellation token. /// The TMDb collection or null if not found. - public async Task GetCollectionAsync(int tmdbId, string language, string imageLanguages, CancellationToken cancellationToken) + public async Task GetCollectionAsync(int tmdbId, string? language, string? imageLanguages, CancellationToken cancellationToken) { var key = $"collection-{tmdbId.ToString(CultureInfo.InvariantCulture)}-{language}"; - if (_memoryCache.TryGetValue(key, out Collection collection)) + if (_memoryCache.TryGetValue(key, out Collection? collection)) { return collection; } @@ -122,10 +120,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// A comma-separated list of image languages. /// The cancellation token. /// The TMDb tv show information or null if not found. - public async Task GetSeriesAsync(int tmdbId, string language, string imageLanguages, CancellationToken cancellationToken) + public async Task GetSeriesAsync(int tmdbId, string? language, string? imageLanguages, CancellationToken cancellationToken) { var key = $"series-{tmdbId.ToString(CultureInfo.InvariantCulture)}-{language}"; - if (_memoryCache.TryGetValue(key, out TvShow series)) + if (_memoryCache.TryGetValue(key, out TvShow? series)) { return series; } @@ -162,7 +160,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// A comma-separated list of image languages. /// The cancellation token. /// The TMDb tv show episode group information or null if not found. - private async Task GetSeriesGroupAsync(int tvShowId, string displayOrder, string language, string imageLanguages, CancellationToken cancellationToken) + private async Task GetSeriesGroupAsync(int tvShowId, string displayOrder, string? language, string? imageLanguages, CancellationToken cancellationToken) { TvGroupType? groupType = string.Equals(displayOrder, "originalAirDate", StringComparison.Ordinal) ? TvGroupType.OriginalAirDate : @@ -180,7 +178,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb } var key = $"group-{tvShowId.ToString(CultureInfo.InvariantCulture)}-{displayOrder}-{language}"; - if (_memoryCache.TryGetValue(key, out TvGroupCollection group)) + if (_memoryCache.TryGetValue(key, out TvGroupCollection? group)) { return group; } @@ -217,10 +215,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// A comma-separated list of image languages. /// The cancellation token. /// The TMDb tv season information or null if not found. - public async Task GetSeasonAsync(int tvShowId, int seasonNumber, string language, string imageLanguages, CancellationToken cancellationToken) + public async Task GetSeasonAsync(int tvShowId, int seasonNumber, string? language, string? imageLanguages, CancellationToken cancellationToken) { var key = $"season-{tvShowId.ToString(CultureInfo.InvariantCulture)}-s{seasonNumber.ToString(CultureInfo.InvariantCulture)}-{language}"; - if (_memoryCache.TryGetValue(key, out TvSeason season)) + if (_memoryCache.TryGetValue(key, out TvSeason? season)) { return season; } @@ -254,10 +252,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// A comma-separated list of image languages. /// The cancellation token. /// The TMDb tv episode information or null if not found. - public async Task GetEpisodeAsync(int tvShowId, int seasonNumber, int episodeNumber, string displayOrder, string language, string imageLanguages, CancellationToken cancellationToken) + public async Task GetEpisodeAsync(int tvShowId, int seasonNumber, int episodeNumber, string displayOrder, string? language, string? imageLanguages, CancellationToken cancellationToken) { var key = $"episode-{tvShowId.ToString(CultureInfo.InvariantCulture)}-s{seasonNumber.ToString(CultureInfo.InvariantCulture)}e{episodeNumber.ToString(CultureInfo.InvariantCulture)}-{displayOrder}-{language}"; - if (_memoryCache.TryGetValue(key, out TvEpisode episode)) + if (_memoryCache.TryGetValue(key, out TvEpisode? episode)) { return episode; } @@ -301,10 +299,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// The episode's language. /// The cancellation token. /// The TMDb person information or null if not found. - public async Task GetPersonAsync(int personTmdbId, string language, CancellationToken cancellationToken) + public async Task GetPersonAsync(int personTmdbId, string language, CancellationToken cancellationToken) { var key = $"person-{personTmdbId.ToString(CultureInfo.InvariantCulture)}-{language}"; - if (_memoryCache.TryGetValue(key, out Person person)) + if (_memoryCache.TryGetValue(key, out Person? person)) { return person; } @@ -333,14 +331,14 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// The item's language. /// The cancellation token. /// The TMDb item or null if not found. - public async Task FindByExternalIdAsync( + public async Task FindByExternalIdAsync( string externalId, FindExternalSource source, string language, CancellationToken cancellationToken) { var key = $"find-{source.ToString()}-{externalId.ToString(CultureInfo.InvariantCulture)}-{language}"; - if (_memoryCache.TryGetValue(key, out FindContainer result)) + if (_memoryCache.TryGetValue(key, out FindContainer? result)) { return result; } @@ -372,7 +370,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb public async Task> SearchSeriesAsync(string name, string language, int year = 0, CancellationToken cancellationToken = default) { var key = $"searchseries-{name}-{language}"; - if (_memoryCache.TryGetValue(key, out SearchContainer series)) + if (_memoryCache.TryGetValue(key, out SearchContainer? series) && series is not null) { return series.Results; } @@ -400,7 +398,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb public async Task> SearchPersonAsync(string name, CancellationToken cancellationToken) { var key = $"searchperson-{name}"; - if (_memoryCache.TryGetValue(key, out SearchContainer person)) + if (_memoryCache.TryGetValue(key, out SearchContainer? person) && person is not null) { return person.Results; } @@ -442,7 +440,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb public async Task> SearchMovieAsync(string name, int year, string language, CancellationToken cancellationToken) { var key = $"moviesearch-{name}-{year.ToString(CultureInfo.InvariantCulture)}-{language}"; - if (_memoryCache.TryGetValue(key, out SearchContainer movies)) + if (_memoryCache.TryGetValue(key, out SearchContainer? movies) && movies is not null) { return movies.Results; } @@ -471,7 +469,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb public async Task> SearchCollectionAsync(string name, string language, CancellationToken cancellationToken) { var key = $"collectionsearch-{name}-{language}"; - if (_memoryCache.TryGetValue(key, out SearchContainer collections)) + if (_memoryCache.TryGetValue(key, out SearchContainer? collections) && collections is not null) { return collections.Results; } @@ -496,7 +494,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// The image size to fetch. /// The relative URL of the image. /// The absolute URL. - private string GetUrl(string size, string path) + private string? GetUrl(string? size, string path) { if (string.IsNullOrEmpty(path)) { @@ -511,7 +509,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb ///
/// The relative URL of the poster. /// The absolute URL. - public string GetPosterUrl(string posterPath) + public string? GetPosterUrl(string posterPath) { return GetUrl(Plugin.Instance.Configuration.PosterSize, posterPath); } @@ -521,7 +519,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb ///
/// The relative URL of the profile image. /// The absolute URL. - public string GetProfileUrl(string actorProfilePath) + public string? GetProfileUrl(string actorProfilePath) { return GetUrl(Plugin.Instance.Configuration.ProfileSize, actorProfilePath); } @@ -579,7 +577,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// The type of the image. /// The requested language. /// The remote images. - private IEnumerable ConvertToRemoteImageInfo(IReadOnlyList images, string size, ImageType type, string requestLanguage) + private IEnumerable ConvertToRemoteImageInfo(IReadOnlyList images, string? size, ImageType type, string requestLanguage) { // sizes provided are for original resolution, don't store them when downloading scaled images var scaleImage = !string.Equals(size, "original", StringComparison.OrdinalIgnoreCase); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs index 44c2c81f44..b326d22c87 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Text.RegularExpressions; using MediaBrowser.Model.Entities; using TMDbLib.Objects.General; @@ -128,7 +129,8 @@ namespace MediaBrowser.Providers.Plugins.Tmdb ///
/// The language code. /// The normalized language code. - public static string NormalizeLanguage(string language) + [return: NotNullIfNotNull(nameof(language))] + public static string? NormalizeLanguage(string? language) { if (string.IsNullOrEmpty(language)) { From da25c3ad7b5007c6237777e0d5f2b116789d2e23 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 25 Feb 2023 09:29:23 -0700 Subject: [PATCH 34/41] chore(deps): update dependency efcoresecondlevelcacheinterceptor to v3.8.5 (#9393) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index c2a4d5ff3d..93aad5d918 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -17,7 +17,7 @@ - + From cb2d72d7ec840fb56ce7249bc21ffad565ea5335 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 26 Feb 2023 07:32:12 -0700 Subject: [PATCH 35/41] chore(deps): update peter-evans/find-comment digest to 034abe9 (#9401) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/openapi.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/openapi.yml b/.github/workflows/openapi.yml index 5ba53af865..aa2e0417fe 100644 --- a/.github/workflows/openapi.yml +++ b/.github/workflows/openapi.yml @@ -103,7 +103,7 @@ jobs: body="${body//$'\r'/'%0D'}" echo ::set-output name=body::$body - name: Find difference comment - uses: peter-evans/find-comment@85a676a52594b4481e0532825a2d8906ef96dac2 # v2 + uses: peter-evans/find-comment@034abe94d3191f9c89d870519735beae326f2bdb # v2 id: find-comment with: issue-number: ${{ github.event.pull_request.number }} From edc627fd5b5a5bd19c843dd9e2970b1ebce3fbfd Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Sun, 26 Feb 2023 22:33:27 +0800 Subject: [PATCH 36/41] Improve the Vulkan based subtitle burn-in performance (#9402) https://gitlab.freedesktop.org/mesa/mesa/-/issues/850 Currently Mesa RADV does not support a dedicated transfer queue. Signed-off-by: nyanmisaka --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index e8d011a153..e02a932b12 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -4232,12 +4232,12 @@ namespace MediaBrowser.Controller.MediaEncoding subFilters.Add(subTextSubtitlesFilter); } - subFilters.Add("hwupload=derive_device=vulkan:extra_hw_frames=16"); + // prefer vaapi hwupload to vulkan hwupload, + // Mesa RADV does not support a dedicated transfer queue. + subFilters.Add("hwupload=derive_device=vaapi,format=vaapi,hwmap=derive_device=vulkan"); overlayFilters.Add("overlay_vulkan=eof_action=endall:shortest=1:repeatlast=0"); - - // explicitly sync using libplacebo. - overlayFilters.Add("libplacebo=format=nv12:upscaler=none:downscaler=none"); + overlayFilters.Add("scale_vulkan=format=nv12"); // OUTPUT vaapi(nv12/bgra) surface(vram) // reverse-mapping via vaapi-vulkan interop. From 4873d2a54db7327b3cc021cc587d31d932b57f2e Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Mon, 27 Feb 2023 05:48:37 -0700 Subject: [PATCH 37/41] Fix auth endpoints using api key (#9408) --- .../DefaultAuthorizationHandler.cs | 9 +++++- .../DefaultAuthorizationHandlerTests.cs | 32 ++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs b/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs index b1d97e4a1d..de271ab640 100644 --- a/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs +++ b/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs @@ -46,6 +46,13 @@ namespace Jellyfin.Api.Auth.DefaultAuthorizationPolicy return Task.CompletedTask; } + if (isApiKey) + { + // Api keys are unrestricted. + context.Succeed(requirement); + return Task.CompletedTask; + } + var isInLocalNetwork = _httpContextAccessor.HttpContext is not null && _networkManager.IsInLocalNetwork(_httpContextAccessor.HttpContext.GetNormalizedRemoteIp()); var user = _userManager.GetUserById(userId); @@ -62,7 +69,7 @@ namespace Jellyfin.Api.Auth.DefaultAuthorizationPolicy } // Admins can do everything - if (isApiKey || context.User.IsInRole(UserRoles.Administrator)) + if (context.User.IsInRole(UserRoles.Administrator)) { context.Succeed(requirement); return Task.CompletedTask; diff --git a/tests/Jellyfin.Api.Tests/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandlerTests.cs index 7c85ddd620..ad8a051fdc 100644 --- a/tests/Jellyfin.Api.Tests/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandlerTests.cs +++ b/tests/Jellyfin.Api.Tests/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandlerTests.cs @@ -1,9 +1,13 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Net; +using System.Security.Claims; using System.Threading.Tasks; using AutoFixture; using AutoFixture.AutoMoq; using Jellyfin.Api.Auth.DefaultAuthorizationPolicy; using Jellyfin.Api.Constants; +using Jellyfin.Data.Entities; using Jellyfin.Server.Implementations.Security; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Library; @@ -51,6 +55,32 @@ namespace Jellyfin.Api.Tests.Auth.DefaultAuthorizationPolicy Assert.True(context.HasSucceeded); } + [Fact] + public async Task ShouldSucceedOnApiKey() + { + TestHelpers.SetupConfigurationManager(_configurationManagerMock, true); + + _httpContextAccessor + .Setup(h => h.HttpContext!.Connection.RemoteIpAddress) + .Returns(new IPAddress(0)); + + _userManagerMock + .Setup(u => u.GetUserById(It.IsAny())) + .Returns(null); + + var claims = new[] + { + new Claim(InternalClaimTypes.IsApiKey, bool.TrueString) + }; + + var identity = new ClaimsIdentity(claims, string.Empty); + var principal = new ClaimsPrincipal(identity); + var context = new AuthorizationHandlerContext(_requirements, principal, null); + + await _sut.HandleAsync(context); + Assert.True(context.HasSucceeded); + } + [Theory] [MemberData(nameof(GetParts_ValidAuthHeader_Success_Data))] public void GetParts_ValidAuthHeader_Success(string input, Dictionary parts) From 54cd3e6d551d797bace33d65334cf1e98669676d Mon Sep 17 00:00:00 2001 From: Bas Date: Mon, 27 Feb 2023 20:42:46 +0000 Subject: [PATCH 38/41] Translated using Weblate (Dutch) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nl/ --- Emby.Server.Implementations/Localization/Core/nl.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json index 081ba0cc7e..383096f7e2 100644 --- a/Emby.Server.Implementations/Localization/Core/nl.json +++ b/Emby.Server.Implementations/Localization/Core/nl.json @@ -95,13 +95,13 @@ "TaskDownloadMissingSubtitlesDescription": "Zoekt op het internet naar ontbrekende ondertiteling gebaseerd op metadataconfiguratie.", "TaskDownloadMissingSubtitles": "Ontbrekende ondertiteling downloaden", "TaskRefreshChannelsDescription": "Vernieuwt informatie van internet kanalen.", - "TaskRefreshChannels": "Vernieuw kanalen", + "TaskRefreshChannels": "Kanalen vernieuwen", "TaskCleanTranscodeDescription": "Verwijdert transcode bestanden ouder dan 1 dag.", "TaskCleanLogs": "Logboekmap opschonen", "TaskCleanTranscode": "Transcoderingsmap opschonen", "TaskUpdatePluginsDescription": "Downloadt en installeert updates van plug-ins waarvoor automatisch bijwerken is ingeschakeld.", "TaskUpdatePlugins": "Plug-ins bijwerken", - "TaskRefreshPeopleDescription": "Update metadata voor acteurs en regisseurs in de media bibliotheek.", + "TaskRefreshPeopleDescription": "Updatet metadata voor acteurs en regisseurs in je mediabibliotheek.", "TaskRefreshPeople": "Personen vernieuwen", "TaskCleanLogsDescription": "Verwijdert log bestanden ouder dan {0} dagen.", "TaskRefreshLibraryDescription": "Scant de mediabibliotheek op nieuwe bestanden en vernieuwt de metadata.", From 4b01aaa0f7c52557d1500daaae2bc457a56dbffe Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 1 Mar 2023 00:44:57 +0100 Subject: [PATCH 39/41] Allocate less Lists --- .../ConnectionManagerXmlBuilder.cs | 4 +-- .../ContentDirectoryXmlBuilder.cs | 4 +-- .../AudioBook/AudioBookListResolver.cs | 20 +++++------ .../Channels/ChannelManager.cs | 2 +- .../Collections/CollectionManager.cs | 18 ++++++---- .../Data/SqliteItemRepository.cs | 10 +++--- Emby.Server.Implementations/Dto/DtoService.cs | 12 +++---- .../Library/LibraryManager.cs | 33 +++++++++---------- .../Library/Resolvers/Audio/AudioResolver.cs | 3 +- .../Library/UserViewManager.cs | 2 +- .../Persistence/IItemRepository.cs | 2 +- .../Manager/ProviderManager.cs | 4 +-- .../Playlists/PlaylistItemsProvider.cs | 2 +- .../AudioDb/AudioDbArtistImageProvider.cs | 5 +-- .../MusicBrainz/MusicBrainzAlbumProvider.cs | 10 +++--- .../Plugins/Omdb/OmdbImageProvider.cs | 5 +-- .../Tmdb/BoxSets/TmdbBoxSetImageProvider.cs | 2 +- .../Tmdb/BoxSets/TmdbBoxSetProvider.cs | 4 +-- .../Tmdb/Movies/TmdbMovieImageProvider.cs | 2 +- .../Tmdb/People/TmdbPersonImageProvider.cs | 5 +-- .../Plugins/Tmdb/People/TmdbPersonProvider.cs | 4 +-- .../Tmdb/TV/TmdbEpisodeImageProvider.cs | 5 +-- .../Tmdb/TV/TmdbSeasonImageProvider.cs | 5 +-- .../Tmdb/TV/TmdbSeriesImageProvider.cs | 2 +- 24 files changed, 76 insertions(+), 89 deletions(-) diff --git a/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs b/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs index c484dac542..db1190ae7c 100644 --- a/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs +++ b/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs @@ -27,7 +27,7 @@ namespace Emby.Dlna.ConnectionManager /// The . private static IEnumerable GetStateVariables() { - var list = new List + return new StateVariable[] { new StateVariable { @@ -114,8 +114,6 @@ namespace Emby.Dlna.ConnectionManager SendsEvents = false } }; - - return list; } } } diff --git a/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs b/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs index 3edaabb70e..9af28aa7cb 100644 --- a/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs +++ b/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs @@ -27,7 +27,7 @@ namespace Emby.Dlna.ContentDirectory /// The . private static IEnumerable GetStateVariables() { - var list = new List + return new StateVariable[] { new StateVariable { @@ -154,8 +154,6 @@ namespace Emby.Dlna.ContentDirectory SendsEvents = false } }; - - return list; } } } diff --git a/Emby.Naming/AudioBook/AudioBookListResolver.cs b/Emby.Naming/AudioBook/AudioBookListResolver.cs index bdae20b6b2..ca304102fd 100644 --- a/Emby.Naming/AudioBook/AudioBookListResolver.cs +++ b/Emby.Naming/AudioBook/AudioBookListResolver.cs @@ -79,25 +79,25 @@ namespace Emby.Naming.AudioBook { if (group.Count() > 1 || haveChaptersOrPages) { - var ex = new List(); - var alt = new List(); + List? ex = null; + List? alt = null; foreach (var audioFile in group) { - var name = Path.GetFileNameWithoutExtension(audioFile.Path); - if (name.Equals("audiobook", StringComparison.OrdinalIgnoreCase) || - name.Contains(nameParserResult.Name, StringComparison.OrdinalIgnoreCase) || - name.Contains(nameWithReplacedDots, StringComparison.OrdinalIgnoreCase)) + var name = Path.GetFileNameWithoutExtension(audioFile.Path.AsSpan()); + if (name.Equals("audiobook", StringComparison.OrdinalIgnoreCase) + || name.Contains(nameParserResult.Name, StringComparison.OrdinalIgnoreCase) + || name.Contains(nameWithReplacedDots, StringComparison.OrdinalIgnoreCase)) { - alt.Add(audioFile); + (alt ??= new()).Add(audioFile); } else { - ex.Add(audioFile); + (ex ??= new()).Add(audioFile); } } - if (ex.Count > 0) + if (ex is not null) { var extra = ex .OrderBy(x => x.Container) @@ -108,7 +108,7 @@ namespace Emby.Naming.AudioBook extras.AddRange(extra); } - if (alt.Count > 0) + if (alt is not null) { var alternatives = alt .OrderBy(x => x.Container) diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 84ba194648..1e3c4dea14 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -401,7 +401,7 @@ namespace Emby.Server.Implementations.Channels } else { - results = new List(); + results = Enumerable.Empty(); } return results diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index b53c8ca512..1796830552 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -206,8 +206,7 @@ namespace Emby.Server.Implementations.Collections throw new ArgumentException("No collection exists with the supplied Id"); } - var list = new List(); - var itemList = new List(); + List? itemList = null; var linkedChildrenList = collection.GetLinkedChildren(); var currentLinkedChildrenIds = linkedChildrenList.Select(i => i.Id).ToList(); @@ -223,18 +222,23 @@ namespace Emby.Server.Implementations.Collections if (!currentLinkedChildrenIds.Contains(id)) { - itemList.Add(item); + (itemList ??= new()).Add(item); - list.Add(LinkedChild.Create(item)); linkedChildrenList.Add(item); } } - if (list.Count > 0) + if (itemList is not null) { - LinkedChild[] newChildren = new LinkedChild[collection.LinkedChildren.Length + list.Count]; + var originalLen = collection.LinkedChildren.Length; + var newItemCount = itemList.Count; + LinkedChild[] newChildren = new LinkedChild[originalLen + newItemCount]; collection.LinkedChildren.CopyTo(newChildren, 0); - list.CopyTo(newChildren, collection.LinkedChildren.Length); + for (int i = 0; i < newItemCount; i++) + { + newChildren[originalLen + i] = LinkedChild.Create(itemList[i]); + } + collection.LinkedChildren = newChildren; collection.UpdateRatingToItems(linkedChildrenList); diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 0aa943270e..3bf4d07c59 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -586,7 +586,7 @@ namespace Emby.Server.Implementations.Data /// /// or is null. /// - public void SaveItems(IEnumerable items, CancellationToken cancellationToken) + public void SaveItems(IReadOnlyList items, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(items); @@ -594,9 +594,11 @@ namespace Emby.Server.Implementations.Data CheckDisposed(); - var tuples = new List<(BaseItem, List, BaseItem, string, List)>(); - foreach (var item in items) + var itemsLen = items.Count; + var tuples = new ValueTuple, BaseItem, string, List>[itemsLen]; + for (int i = 0; i < itemsLen; i++) { + var item = items[i]; var ancestorIds = item.SupportsAncestors ? item.GetAncestorIds().Distinct().ToList() : null; @@ -606,7 +608,7 @@ namespace Emby.Server.Implementations.Data var userdataKey = item.GetUserDataKeys().FirstOrDefault(); var inheritedTags = item.GetInheritedTags(); - tuples.Add((item, ancestorIds, topParent, userdataKey, inheritedTags)); + tuples[i] = (item, ancestorIds, topParent, userdataKey, inheritedTags); } using (var connection = GetConnection()) diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index e928f1ff3a..45270de893 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -85,8 +85,8 @@ namespace Emby.Server.Implementations.Dto { var accessibleItems = user is null ? items : items.Where(x => x.IsVisible(user)).ToList(); var returnItems = new BaseItemDto[accessibleItems.Count]; - var programTuples = new List<(BaseItem, BaseItemDto)>(); - var channelTuples = new List<(BaseItemDto, LiveTvChannel)>(); + List<(BaseItem, BaseItemDto)> programTuples = null; + List<(BaseItemDto, LiveTvChannel)> channelTuples = null; for (int index = 0; index < accessibleItems.Count; index++) { @@ -95,11 +95,11 @@ namespace Emby.Server.Implementations.Dto if (item is LiveTvChannel tvChannel) { - channelTuples.Add((dto, tvChannel)); + (channelTuples ??= new()).Add((dto, tvChannel)); } else if (item is LiveTvProgram) { - programTuples.Add((item, dto)); + (programTuples ??= new()).Add((item, dto)); } if (item is IItemByName byName) @@ -122,12 +122,12 @@ namespace Emby.Server.Implementations.Dto returnItems[index] = dto; } - if (programTuples.Count > 0) + if (programTuples is not null) { LivetvManager.AddInfoToProgramDto(programTuples, options.Fields, user).GetAwaiter().GetResult(); } - if (channelTuples.Count > 0) + if (channelTuples is not null) { LivetvManager.AddChannelInfo(channelTuples, options, user); } diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index a3c66dc798..66bd68ddd0 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -356,8 +356,8 @@ namespace Emby.Server.Implementations.Library } var children = item.IsFolder - ? ((Folder)item).GetRecursiveChildren(false).ToList() - : new List(); + ? ((Folder)item).GetRecursiveChildren(false) + : Enumerable.Empty(); foreach (var metadataPath in GetMetadataPaths(item, children)) { @@ -1253,7 +1253,7 @@ namespace Emby.Server.Implementations.Library var parent = GetItemById(query.ParentId); if (parent is not null) { - SetTopParentIdsOrAncestors(query, new List { parent }); + SetTopParentIdsOrAncestors(query, new[] { parent }); } } @@ -1277,7 +1277,7 @@ namespace Emby.Server.Implementations.Library var parent = GetItemById(query.ParentId); if (parent is not null) { - SetTopParentIdsOrAncestors(query, new List { parent }); + SetTopParentIdsOrAncestors(query, new[] { parent }); } } @@ -1435,7 +1435,7 @@ namespace Emby.Server.Implementations.Library var parent = GetItemById(query.ParentId); if (parent is not null) { - SetTopParentIdsOrAncestors(query, new List { parent }); + SetTopParentIdsOrAncestors(query, new[] { parent }); } } @@ -1455,7 +1455,7 @@ namespace Emby.Server.Implementations.Library _itemRepository.GetItemList(query)); } - private void SetTopParentIdsOrAncestors(InternalItemsQuery query, List parents) + private void SetTopParentIdsOrAncestors(InternalItemsQuery query, IReadOnlyCollection parents) { if (parents.All(i => i is ICollectionFolder || i is UserView)) { @@ -1602,7 +1602,7 @@ namespace Emby.Server.Implementations.Library { _logger.LogError(ex, "Error getting intros"); - return new List(); + return Enumerable.Empty(); } } @@ -2876,7 +2876,7 @@ namespace Emby.Server.Implementations.Library private async Task SavePeopleMetadataAsync(IEnumerable people, CancellationToken cancellationToken) { - var personsToSave = new List(); + List personsToSave = null; foreach (var person in people) { @@ -2918,12 +2918,12 @@ namespace Emby.Server.Implementations.Library if (saveEntity) { - personsToSave.Add(personEntity); + (personsToSave ??= new()).Add(personEntity); await RunMetadataSavers(personEntity, itemUpdateType).ConfigureAwait(false); } } - if (personsToSave.Count > 0) + if (personsToSave is not null) { CreateItems(personsToSave, null, CancellationToken.None); } @@ -3085,22 +3085,19 @@ namespace Emby.Server.Implementations.Library throw new ArgumentNullException(nameof(path)); } - var removeList = new List(); + List removeList = null; foreach (var contentType in _configurationManager.Configuration.ContentTypes) { - if (string.IsNullOrWhiteSpace(contentType.Name)) - { - removeList.Add(contentType); - } - else if (_fileSystem.AreEqual(path, contentType.Name) + if (string.IsNullOrWhiteSpace(contentType.Name) + || _fileSystem.AreEqual(path, contentType.Name) || _fileSystem.ContainsSubPath(path, contentType.Name)) { - removeList.Add(contentType); + (removeList ??= new()).Add(contentType); } } - if (removeList.Count > 0) + if (removeList is not null) { _configurationManager.Configuration.ContentTypes = _configurationManager.Configuration.ContentTypes .Except(removeList) diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs index 06621700aa..69e9057984 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs @@ -158,7 +158,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio private MultiItemResolverResult ResolveMultipleAudio(Folder parent, IEnumerable fileSystemEntries, bool parseName) { var files = new List(); - var items = new List(); var leftOver = new List(); // Loop through each child file/folder and see if we find a video @@ -180,7 +179,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio var result = new MultiItemResolverResult { ExtraFiles = leftOver, - Items = items + Items = new List() }; var isInMixedFolder = resolverResult.Count > 1 || (parent is not null && parent.IsTopParent); diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs index 1137625f44..0e2d34d39c 100644 --- a/Emby.Server.Implementations/Library/UserViewManager.cs +++ b/Emby.Server.Implementations/Library/UserViewManager.cs @@ -286,7 +286,7 @@ namespace Emby.Server.Implementations.Library if (parents.Count == 0) { - return new List(); + return Array.Empty(); } if (includeItemTypes.Length == 0) diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs index 24f7b5cd36..2c52b2b45e 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -28,7 +28,7 @@ namespace MediaBrowser.Controller.Persistence ///
/// The items. /// The cancellation token. - void SaveItems(IEnumerable items, CancellationToken cancellationToken); + void SaveItems(IReadOnlyList items, CancellationToken cancellationToken); void SaveImages(BaseItem item); diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index c07839ff25..81ccd86534 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -284,12 +284,12 @@ namespace MediaBrowser.Providers.Manager } catch (OperationCanceledException) { - return new List(); + return Enumerable.Empty(); } catch (Exception ex) { _logger.LogError(ex, "{ProviderName} failed in GetImageInfos for type {ItemType} at {ItemPath}", provider.GetType().Name, item.GetType().Name, item.Path); - return new List(); + return Enumerable.Empty(); } } diff --git a/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs b/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs index db4c5f436e..9bd36f25c3 100644 --- a/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs +++ b/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs @@ -87,7 +87,7 @@ namespace MediaBrowser.Providers.Playlists return GetPlsItems(stream); } - return new List(); + return Enumerable.Empty(); } private IEnumerable GetPlsItems(Stream stream) diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs index b1a285a964..2232dfa0d7 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net.Http; using System.Text.Json; using System.Threading; @@ -42,7 +43,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb /// public IEnumerable GetSupportedImages(BaseItem item) { - return new List + return new ImageType[] { ImageType.Primary, ImageType.Logo, @@ -74,7 +75,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb } } - return new List(); + return Enumerable.Empty(); } private IEnumerable GetImages(AudioDbArtistProvider.Artist item) diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs index 4aa4269891..d0bd7d6098 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs @@ -157,10 +157,10 @@ public class MusicBrainzAlbumProvider : IRemoteMetadataProvider 0) { - var artistResults = new List(); - - foreach (var artist in artists) + var artistResults = new RemoteSearchResult[artists.Count]; + for (int i = 0; i < artists.Count; i++) { + var artist = artists[i]; var artistResult = new RemoteSearchResult { Name = artist.Name @@ -171,11 +171,11 @@ public class MusicBrainzAlbumProvider : IRemoteMetadataProvider GetSupportedImages(BaseItem item) { - return new List - { - ImageType.Primary - }; + yield return ImageType.Primary; } public async Task> GetImages(BaseItem item, CancellationToken cancellationToken) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs index ef32b0a074..a4c6cb47de 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs @@ -48,7 +48,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets /// public IEnumerable GetSupportedImages(BaseItem item) { - return new List + return new ImageType[] { ImageType.Primary, ImageType.Backdrop diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs index e6c7dba250..c2018d820e 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs @@ -72,7 +72,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets var collectionSearchResults = await _tmdbClientManager.SearchCollectionAsync(searchInfo.Name, language, cancellationToken).ConfigureAwait(false); - var collections = new List(); + var collections = new RemoteSearchResult[collectionSearchResults.Count]; for (var i = 0; i < collectionSearchResults.Count; i++) { var collection = new RemoteSearchResult @@ -82,7 +82,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets }; collection.SetProviderId(MetadataProvider.Tmdb, collectionSearchResults[i].Id.ToString(CultureInfo.InvariantCulture)); - collections.Add(collection); + collections[i] = collection; } return collections; diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs index 655fa5a16d..bfec48e7c7 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs @@ -49,7 +49,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies /// public IEnumerable GetSupportedImages(BaseItem item) { - return new List + return new ImageType[] { ImageType.Primary, ImageType.Backdrop, diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs index bc959ee2bd..9e5404b325 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs @@ -46,10 +46,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People /// public IEnumerable GetSupportedImages(BaseItem item) { - return new List - { - ImageType.Primary - }; + yield return ImageType.Primary; } /// diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs index c03a1ca3b8..5c6e71fd89 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs @@ -67,7 +67,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People var personSearchResult = await _tmdbClientManager.SearchPersonAsync(searchInfo.Name, cancellationToken).ConfigureAwait(false); - var remoteSearchResults = new List(); + var remoteSearchResults = new RemoteSearchResult[personSearchResult.Count]; for (var i = 0; i < personSearchResult.Count; i++) { var person = personSearchResult[i]; @@ -79,7 +79,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People }; remoteSearchResult.SetProviderId(MetadataProvider.Tmdb, person.Id.ToString(CultureInfo.InvariantCulture)); - remoteSearchResults.Add(remoteSearchResult); + remoteSearchResults[i] = remoteSearchResult; } return remoteSearchResults; diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs index abef732bbb..d1fec7cb13 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs @@ -47,10 +47,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV /// public IEnumerable GetSupportedImages(BaseItem item) { - return new List - { - ImageType.Primary - }; + yield return ImageType.Primary; } /// diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs index b8d1460db9..a743601ed3 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs @@ -48,10 +48,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV /// public IEnumerable GetSupportedImages(BaseItem item) { - return new List - { - ImageType.Primary - }; + yield return ImageType.Primary; } /// diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs index 79cb6e86d4..192fb052d7 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs @@ -48,7 +48,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV /// public IEnumerable GetSupportedImages(BaseItem item) { - return new List + return new ImageType[] { ImageType.Primary, ImageType.Backdrop, From e58bf6b2be1c366f0fb705d60f1d242df7123386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20F=C3=BCrni=C3=9F?= Date: Wed, 1 Mar 2023 00:46:08 +0100 Subject: [PATCH 40/41] Add SeasonProviderIds to EpisodeInfo (#9407) Co-authored-by: Cody Robibero --- MediaBrowser.Controller/Entities/TV/Episode.cs | 5 +++++ MediaBrowser.Controller/Providers/EpisodeInfo.cs | 3 +++ 2 files changed, 8 insertions(+) diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index c83149a6dc..597b4cecbc 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -308,6 +308,11 @@ namespace MediaBrowser.Controller.Entities.TV id.SeriesDisplayOrder = series.DisplayOrder; } + if (Season is not null) + { + id.SeasonProviderIds = Season.ProviderIds; + } + id.IsMissingEpisode = IsMissingEpisode; id.IndexNumberEnd = IndexNumberEnd; diff --git a/MediaBrowser.Controller/Providers/EpisodeInfo.cs b/MediaBrowser.Controller/Providers/EpisodeInfo.cs index b59a037384..c4ad352a3b 100644 --- a/MediaBrowser.Controller/Providers/EpisodeInfo.cs +++ b/MediaBrowser.Controller/Providers/EpisodeInfo.cs @@ -12,10 +12,13 @@ namespace MediaBrowser.Controller.Providers public EpisodeInfo() { SeriesProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); + SeasonProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); } public Dictionary SeriesProviderIds { get; set; } + public Dictionary SeasonProviderIds { get; set; } + public int? IndexNumberEnd { get; set; } public bool IsMissingEpisode { get; set; } From d280dc65549b2507c058bcf1823e3ab040293da7 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 1 Mar 2023 16:43:55 +0100 Subject: [PATCH 41/41] Reduce log spam Fixes #7801 --- .../ScheduledTasks/ScheduledTaskWorker.cs | 9 +++------ .../ScheduledTasks/TaskManager.cs | 7 ++----- Jellyfin.Networking/Manager/NetworkManager.cs | 6 +++--- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index ee9aa85699..1af2c96d2f 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -93,11 +93,8 @@ namespace Emby.Server.Implementations.ScheduledTasks public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, ILogger logger) { ArgumentNullException.ThrowIfNull(scheduledTask); - ArgumentNullException.ThrowIfNull(applicationPaths); - ArgumentNullException.ThrowIfNull(taskManager); - ArgumentNullException.ThrowIfNull(logger); ScheduledTask = scheduledTask; @@ -332,7 +329,7 @@ namespace Emby.Server.Implementations.ScheduledTasks return; } - _logger.LogInformation("{0} fired for task: {1}", trigger.GetType().Name, Name); + _logger.LogDebug("{0} fired for task: {1}", trigger.GetType().Name, Name); trigger.Stop(); @@ -378,7 +375,7 @@ namespace Emby.Server.Implementations.ScheduledTasks CurrentCancellationTokenSource = new CancellationTokenSource(); - _logger.LogInformation("Executing {0}", Name); + _logger.LogDebug("Executing {0}", Name); ((TaskManager)_taskManager).OnTaskExecuting(this); @@ -406,7 +403,7 @@ namespace Emby.Server.Implementations.ScheduledTasks } catch (Exception ex) { - _logger.LogError(ex, "Error"); + _logger.LogError(ex, "Error executing Scheduled Task"); failureException = ex; diff --git a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs index 6dc20e66ba..42c30c959d 100644 --- a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs +++ b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs @@ -132,7 +132,7 @@ namespace Emby.Server.Implementations.ScheduledTasks { var type = scheduledTask.ScheduledTask.GetType(); - _logger.LogInformation("Queuing task {0}", type.Name); + _logger.LogDebug("Queuing task {0}", type.Name); lock (_taskQueue) { @@ -172,7 +172,7 @@ namespace Emby.Server.Implementations.ScheduledTasks { var type = task.ScheduledTask.GetType(); - _logger.LogInformation("Queuing task {0}", type.Name); + _logger.LogDebug("Queuing task {0}", type.Name); lock (_taskQueue) { @@ -254,9 +254,6 @@ namespace Emby.Server.Implementations.ScheduledTasks ///
private void ExecuteQueuedTasks() { - _logger.LogInformation("ExecuteQueuedTasks"); - - // Execute queued tasks lock (_taskQueue) { var list = new List>(); diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index 88332ce393..f406e27a6d 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -1019,8 +1019,8 @@ namespace Jellyfin.Networking.Manager _internalInterfaces = CreateCollection(_interfaceAddresses.Where(IsInLocalNetwork)); } - _logger.LogInformation("Defined LAN addresses : {0}", _lanSubnets.AsString()); - _logger.LogInformation("Defined LAN exclusions : {0}", _excludedSubnets.AsString()); + _logger.LogInformation("Defined LAN addresses: {0}", _lanSubnets.AsString()); + _logger.LogInformation("Defined LAN exclusions: {0}", _excludedSubnets.AsString()); _logger.LogInformation("Using LAN addresses: {0}", _lanSubnets.Exclude(_excludedSubnets, true).AsNetworks().AsString()); } } @@ -1145,7 +1145,7 @@ namespace Jellyfin.Networking.Manager } _logger.LogDebug("Discovered {0} interfaces.", _interfaceAddresses.Count); - _logger.LogDebug("Interfaces addresses : {0}", _interfaceAddresses.AsString()); + _logger.LogDebug("Interfaces addresses: {0}", _interfaceAddresses.AsString()); } }