mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-05-31 04:48:27 +01:00
Rework parental ratings (#12615)
Some checks are pending
CodeQL / Analyze (csharp) (push) Waiting to run
OpenAPI / OpenAPI - HEAD (push) Waiting to run
OpenAPI / OpenAPI - BASE (push) Waiting to run
OpenAPI / OpenAPI - Difference (push) Blocked by required conditions
OpenAPI / OpenAPI - Publish Unstable Spec (push) Blocked by required conditions
OpenAPI / OpenAPI - Publish Stable Spec (push) Blocked by required conditions
Tests / run-tests (macos-latest) (push) Waiting to run
Tests / run-tests (ubuntu-latest) (push) Waiting to run
Tests / run-tests (windows-latest) (push) Waiting to run
Project Automation / Project board (push) Waiting to run
Merge Conflict Labeler / Labeling (push) Waiting to run
Some checks are pending
CodeQL / Analyze (csharp) (push) Waiting to run
OpenAPI / OpenAPI - HEAD (push) Waiting to run
OpenAPI / OpenAPI - BASE (push) Waiting to run
OpenAPI / OpenAPI - Difference (push) Blocked by required conditions
OpenAPI / OpenAPI - Publish Unstable Spec (push) Blocked by required conditions
OpenAPI / OpenAPI - Publish Stable Spec (push) Blocked by required conditions
Tests / run-tests (macos-latest) (push) Waiting to run
Tests / run-tests (ubuntu-latest) (push) Waiting to run
Tests / run-tests (windows-latest) (push) Waiting to run
Project Automation / Project board (push) Waiting to run
Merge Conflict Labeler / Labeling (push) Waiting to run
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides <see cref="Expression"/> extension methods.
|
||||
/// </summary>
|
||||
public static class ExpressionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Combines two predicates into a single predicate using a logical OR operation.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The predicate parameter type.</typeparam>
|
||||
/// <param name="firstPredicate">The first predicate expression to combine.</param>
|
||||
/// <param name="secondPredicate">The second predicate expression to combine.</param>
|
||||
/// <returns>A new expression representing the OR combination of the input predicates.</returns>
|
||||
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> firstPredicate, Expression<Func<T, bool>> secondPredicate)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(firstPredicate);
|
||||
ArgumentNullException.ThrowIfNull(secondPredicate);
|
||||
|
||||
var invokedExpression = Expression.Invoke(secondPredicate, firstPredicate.Parameters);
|
||||
return Expression.Lambda<Func<T, bool>>(Expression.OrElse(firstPredicate.Body, invokedExpression), firstPredicate.Parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combines multiple predicates into a single predicate using a logical OR operation.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The predicate parameter type.</typeparam>
|
||||
/// <param name="predicates">A collection of predicate expressions to combine.</param>
|
||||
/// <returns>A new expression representing the OR combination of all input predicates.</returns>
|
||||
public static Expression<Func<T, bool>> Or<T>(this IEnumerable<Expression<Func<T, bool>>> predicates)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(predicates);
|
||||
|
||||
return predicates.Aggregate((aggregatePredicate, nextPredicate) => aggregatePredicate.Or(nextPredicate));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combines two predicates into a single predicate using a logical AND operation.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The predicate parameter type.</typeparam>
|
||||
/// <param name="firstPredicate">The first predicate expression to combine.</param>
|
||||
/// <param name="secondPredicate">The second predicate expression to combine.</param>
|
||||
/// <returns>A new expression representing the AND combination of the input predicates.</returns>
|
||||
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> firstPredicate, Expression<Func<T, bool>> secondPredicate)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(firstPredicate);
|
||||
ArgumentNullException.ThrowIfNull(secondPredicate);
|
||||
|
||||
var invokedExpression = Expression.Invoke(secondPredicate, firstPredicate.Parameters);
|
||||
return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(firstPredicate.Body, invokedExpression), firstPredicate.Parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combines multiple predicates into a single predicate using a logical AND operation.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The predicate parameter type.</typeparam>
|
||||
/// <param name="predicates">A collection of predicate expressions to combine.</param>
|
||||
/// <returns>A new expression representing the AND combination of all input predicates.</returns>
|
||||
public static Expression<Func<T, bool>> And<T>(this IEnumerable<Expression<Func<T, bool>>> predicates)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(predicates);
|
||||
|
||||
return predicates.Aggregate((aggregatePredicate, nextPredicate) => aggregatePredicate.And(nextPredicate));
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
@@ -19,6 +20,7 @@ using Jellyfin.Database.Implementations.Entities;
|
||||
using Jellyfin.Database.Implementations.Enums;
|
||||
using Jellyfin.Extensions;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using Jellyfin.Server.Implementations.Extensions;
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Channels;
|
||||
@@ -781,6 +783,7 @@ public sealed class BaseItemRepository
|
||||
entity.PreferredMetadataCountryCode = dto.PreferredMetadataCountryCode;
|
||||
entity.IsInMixedFolder = dto.IsInMixedFolder;
|
||||
entity.InheritedParentalRatingValue = dto.InheritedParentalRatingValue;
|
||||
entity.InheritedParentalRatingSubValue = dto.InheritedParentalRatingSubValue;
|
||||
entity.CriticRating = dto.CriticRating;
|
||||
entity.PresentationUniqueKey = dto.PresentationUniqueKey;
|
||||
entity.OriginalTitle = dto.OriginalTitle;
|
||||
@@ -1796,62 +1799,74 @@ public sealed class BaseItemRepository
|
||||
.Where(e => filter.OfficialRatings.Contains(e.OfficialRating));
|
||||
}
|
||||
|
||||
Expression<Func<BaseItemEntity, bool>>? minParentalRatingFilter = null;
|
||||
if (filter.MinParentalRating != null)
|
||||
{
|
||||
var min = filter.MinParentalRating;
|
||||
minParentalRatingFilter = e => e.InheritedParentalRatingValue >= min.Score || e.InheritedParentalRatingValue == null;
|
||||
if (min.SubScore != null)
|
||||
{
|
||||
minParentalRatingFilter = minParentalRatingFilter.And(e => e.InheritedParentalRatingValue >= min.SubScore || e.InheritedParentalRatingValue == null);
|
||||
}
|
||||
}
|
||||
|
||||
Expression<Func<BaseItemEntity, bool>>? maxParentalRatingFilter = null;
|
||||
if (filter.MaxParentalRating != null)
|
||||
{
|
||||
var max = filter.MaxParentalRating;
|
||||
maxParentalRatingFilter = e => e.InheritedParentalRatingValue <= max.Score || e.InheritedParentalRatingValue == null;
|
||||
if (max.SubScore != null)
|
||||
{
|
||||
maxParentalRatingFilter = maxParentalRatingFilter.And(e => e.InheritedParentalRatingValue <= max.SubScore || e.InheritedParentalRatingValue == null);
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.HasParentalRating ?? false)
|
||||
{
|
||||
if (filter.MinParentalRating.HasValue)
|
||||
if (minParentalRatingFilter != null)
|
||||
{
|
||||
baseQuery = baseQuery
|
||||
.Where(e => e.InheritedParentalRatingValue >= filter.MinParentalRating.Value);
|
||||
baseQuery = baseQuery.Where(minParentalRatingFilter);
|
||||
}
|
||||
|
||||
if (filter.MaxParentalRating.HasValue)
|
||||
if (maxParentalRatingFilter != null)
|
||||
{
|
||||
baseQuery = baseQuery
|
||||
.Where(e => e.InheritedParentalRatingValue < filter.MaxParentalRating.Value);
|
||||
baseQuery = baseQuery.Where(maxParentalRatingFilter);
|
||||
}
|
||||
}
|
||||
else if (filter.BlockUnratedItems.Length > 0)
|
||||
{
|
||||
var unratedItems = filter.BlockUnratedItems.Select(f => f.ToString()).ToArray();
|
||||
if (filter.MinParentalRating.HasValue)
|
||||
var unratedItemTypes = filter.BlockUnratedItems.Select(f => f.ToString()).ToArray();
|
||||
Expression<Func<BaseItemEntity, bool>> unratedItemFilter = e => e.InheritedParentalRatingValue != null || !unratedItemTypes.Contains(e.UnratedType);
|
||||
|
||||
if (minParentalRatingFilter != null && maxParentalRatingFilter != null)
|
||||
{
|
||||
if (filter.MaxParentalRating.HasValue)
|
||||
{
|
||||
baseQuery = baseQuery
|
||||
.Where(e => (e.InheritedParentalRatingValue == null && !unratedItems.Contains(e.UnratedType))
|
||||
|| (e.InheritedParentalRatingValue >= filter.MinParentalRating && e.InheritedParentalRatingValue <= filter.MaxParentalRating));
|
||||
}
|
||||
else
|
||||
{
|
||||
baseQuery = baseQuery
|
||||
.Where(e => (e.InheritedParentalRatingValue == null && !unratedItems.Contains(e.UnratedType))
|
||||
|| e.InheritedParentalRatingValue >= filter.MinParentalRating);
|
||||
}
|
||||
baseQuery = baseQuery.Where(unratedItemFilter.And(minParentalRatingFilter.And(maxParentalRatingFilter)));
|
||||
}
|
||||
else if (minParentalRatingFilter != null)
|
||||
{
|
||||
baseQuery = baseQuery.Where(unratedItemFilter.And(minParentalRatingFilter));
|
||||
}
|
||||
else if (maxParentalRatingFilter != null)
|
||||
{
|
||||
baseQuery = baseQuery.Where(unratedItemFilter.And(maxParentalRatingFilter));
|
||||
}
|
||||
else
|
||||
{
|
||||
baseQuery = baseQuery
|
||||
.Where(e => e.InheritedParentalRatingValue != null && !unratedItems.Contains(e.UnratedType));
|
||||
baseQuery = baseQuery.Where(unratedItemFilter);
|
||||
}
|
||||
}
|
||||
else if (filter.MinParentalRating.HasValue)
|
||||
else if (minParentalRatingFilter != null || maxParentalRatingFilter != null)
|
||||
{
|
||||
if (filter.MaxParentalRating.HasValue)
|
||||
if (minParentalRatingFilter != null)
|
||||
{
|
||||
baseQuery = baseQuery
|
||||
.Where(e => e.InheritedParentalRatingValue != null && e.InheritedParentalRatingValue >= filter.MinParentalRating.Value && e.InheritedParentalRatingValue <= filter.MaxParentalRating.Value);
|
||||
baseQuery = baseQuery.Where(minParentalRatingFilter);
|
||||
}
|
||||
else
|
||||
|
||||
if (maxParentalRatingFilter != null)
|
||||
{
|
||||
baseQuery = baseQuery
|
||||
.Where(e => e.InheritedParentalRatingValue != null && e.InheritedParentalRatingValue >= filter.MinParentalRating.Value);
|
||||
baseQuery = baseQuery.Where(maxParentalRatingFilter);
|
||||
}
|
||||
}
|
||||
else if (filter.MaxParentalRating.HasValue)
|
||||
{
|
||||
baseQuery = baseQuery
|
||||
.Where(e => e.InheritedParentalRatingValue != null && e.InheritedParentalRatingValue >= filter.MaxParentalRating.Value);
|
||||
}
|
||||
else if (!filter.HasParentalRating ?? false)
|
||||
{
|
||||
baseQuery = baseQuery
|
||||
|
||||
@@ -342,7 +342,8 @@ namespace Jellyfin.Server.Implementations.Users
|
||||
},
|
||||
Policy = new UserPolicy
|
||||
{
|
||||
MaxParentalRating = user.MaxParentalAgeRating,
|
||||
MaxParentalRating = user.MaxParentalRatingScore,
|
||||
MaxParentalSubRating = user.MaxParentalRatingSubScore,
|
||||
EnableUserPreferenceAccess = user.EnableUserPreferenceAccess,
|
||||
RemoteClientBitrateLimit = user.RemoteClientBitrateLimit ?? 0,
|
||||
AuthenticationProviderId = user.AuthenticationProviderId,
|
||||
@@ -668,7 +669,8 @@ namespace Jellyfin.Server.Implementations.Users
|
||||
_ => policy.LoginAttemptsBeforeLockout
|
||||
};
|
||||
|
||||
user.MaxParentalAgeRating = policy.MaxParentalRating;
|
||||
user.MaxParentalRatingScore = policy.MaxParentalRating;
|
||||
user.MaxParentalRatingSubScore = policy.MaxParentalSubRating;
|
||||
user.EnableUserPreferenceAccess = policy.EnableUserPreferenceAccess;
|
||||
user.RemoteClientBitrateLimit = policy.RemoteClientBitrateLimit;
|
||||
user.AuthenticationProviderId = policy.AuthenticationProviderId;
|
||||
|
||||
Reference in New Issue
Block a user