diff --git a/CHANGELOG.md b/CHANGELOG.md
index f12c281..0fae182 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,10 +23,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
map, and left alone.
- Audience tagging. Applies a configurable tag (default `grandma`) to titles
- that pass a genre and rating rule, so a user account can be narrowed with
- Allowed Tags. Because Allowed Tags fails closed, new content is invisible to
- that account until tagged, which is why this runs on a schedule rather than
- once.
+ that clear a genre rule, a rating ceiling and a community-rating floor, so a
+ user account can be narrowed with Allowed Tags. The floor applies to rated and
+ unrated titles alike, through separate settings, because a ceiling on its own
+ admits poorly reviewed titles that happen to carry a mild rating, and the
+ point of the tag is to make an overwhelming library smaller rather than to
+ reproduce it. Unrated titles are judged on reputation alone, which is what
+ keeps the older classics TMDB holds no US certification for. Because Allowed
+ Tags fails closed, new content is invisible to that account until tagged,
+ which is why this runs on a schedule rather than once.
- A curation ledger at `tagged-ids.txt` in the plugin data folder. An item whose
id is in the ledger but which no longer carries the tag was untagged by a
diff --git a/Jellyfin.Plugin.Audience/AudienceRules.cs b/Jellyfin.Plugin.Audience/AudienceRules.cs
index 4027860..3a5df85 100644
--- a/Jellyfin.Plugin.Audience/AudienceRules.cs
+++ b/Jellyfin.Plugin.Audience/AudienceRules.cs
@@ -130,15 +130,22 @@ public static class AudienceRules
return false;
}
- // A usable rating is judged on the rating. Anything at or below the ceiling is in.
+ // A usable rating must clear the ceiling AND the quality floor. The floor matters
+ // as much as the ceiling: the point of this tag is to make an overwhelming library
+ // smaller, and a poorly reviewed film that happens to be rated PG adds noise rather
+ // than removing it. An item with no community rating at all fails here, which is
+ // deliberate - without a rating and without a score there is nothing to judge on.
if (ratingScore.HasValue)
{
- return ratingScore.Value <= config.MaxRatingScore;
+ return ratingScore.Value <= config.MaxRatingScore
+ && item.CommunityRating.HasValue
+ && item.CommunityRating.Value >= config.MinCommunityRating;
}
- // No usable rating: fall back to reputation. This is what keeps the older
- // classics, which are exactly what this audience wants and which TMDB
- // frequently has no US certification for.
+ // No usable rating: fall back to reputation alone, against its own floor. This is
+ // what keeps the older classics, which are exactly what this audience wants and
+ // which TMDB frequently has no US certification for. The floor is separate because
+ // an unrated item is carrying more risk, so it may warrant a higher bar.
return item.CommunityRating.HasValue
&& item.CommunityRating.Value >= config.MinCommunityRatingWhenUnrated;
}
diff --git a/Jellyfin.Plugin.Audience/Configuration/PluginConfiguration.cs b/Jellyfin.Plugin.Audience/Configuration/PluginConfiguration.cs
index 3e0011f..75143a6 100644
--- a/Jellyfin.Plugin.Audience/Configuration/PluginConfiguration.cs
+++ b/Jellyfin.Plugin.Audience/Configuration/PluginConfiguration.cs
@@ -51,6 +51,15 @@ public class PluginConfiguration : BasePluginConfiguration
///
public int MaxRatingScore { get; set; } = 14;
+ ///