Add SFW content mode option (#6262)

* Use more neutral language for content
* Add sfw mode setting
* Make configuration context mandatory
* Add sfw class when sfw mode active
* Hide nsfw performer fields in sfw mode
* Hide nsfw sort options
* Hide nsfw filter/sort options in sfw mode
* Replace o-count with like counter in sfw mode
* Use sfw label for o-counter filter in sfw mode
* Use likes instead of o-count in sfw mode in other places
* Rename sfw mode to sfw content mode
* Use sfw image for default performers in sfw mode
* Document SFW content mode
* Add SFW mode setting to setup
* Clarify README
* Change wording of sfw mode description
* Handle configuration loading error correctly
* Hide age in performer cards
This commit is contained in:
WithoutPants
2025-11-18 11:13:35 +11:00
committed by GitHub
parent bb56b619f5
commit 51999135be
105 changed files with 843 additions and 370 deletions

View File

@@ -78,7 +78,7 @@ export abstract class Criterion {
protected cloneValues() {}
public abstract getLabel(intl: IntlShape): string;
public abstract getLabel(intl: IntlShape, sfwContentMode?: boolean): string;
public getId(): string {
return `${this.criterionOption.type}`;
@@ -148,7 +148,7 @@ export abstract class ModifierCriterion<
: "";
}
public getLabel(intl: IntlShape): string {
public getLabel(intl: IntlShape, sfwContentMode: boolean = false): string {
const modifierString = ModifierCriterion.getModifierLabel(
intl,
this.modifier
@@ -162,10 +162,14 @@ export abstract class ModifierCriterion<
valueString = this.getLabelValue(intl);
}
const messageID = !sfwContentMode
? this.criterionOption.messageID
: this.criterionOption.sfwMessageID ?? this.criterionOption.messageID;
return intl.formatMessage(
{ id: "criterion_modifier.format_string" },
{
criterion: intl.formatMessage({ id: this.criterionOption.messageID }),
criterion: intl.formatMessage({ id: messageID }),
modifierString,
valueString,
}
@@ -257,12 +261,14 @@ interface ICriterionOptionParams {
type: CriterionType;
makeCriterion: MakeCriterionFn;
hidden?: boolean;
sfwMessageID?: string;
}
export class CriterionOption {
public readonly type: CriterionType;
public readonly messageID: string;
public readonly makeCriterionFn: MakeCriterionFn;
public readonly sfwMessageID?: string;
// used for legacy criteria that are not shown in the UI
public readonly hidden: boolean = false;
@@ -272,6 +278,7 @@ export class CriterionOption {
this.messageID = options.messageID;
this.makeCriterionFn = options.makeCriterion;
this.hidden = options.hidden ?? false;
this.sfwMessageID = options.sfwMessageID;
}
public makeCriterion(config?: ConfigDataFragment) {
@@ -478,7 +485,7 @@ export class IHierarchicalLabeledIdCriterion extends ModifierCriterion<IHierarch
);
}
public getLabel(intl: IntlShape): string {
public getLabel(intl: IntlShape, sfwContentMode?: boolean): string {
let id = "criterion_modifier.format_string";
let modifierString = ModifierCriterion.getModifierLabel(
intl,
@@ -511,10 +518,14 @@ export class IHierarchicalLabeledIdCriterion extends ModifierCriterion<IHierarch
}
}
const messageID = !sfwContentMode
? this.criterionOption.messageID
: this.criterionOption.sfwMessageID ?? this.criterionOption.messageID;
return intl.formatMessage(
{ id },
{
criterion: intl.formatMessage({ id: this.criterionOption.messageID }),
criterion: intl.formatMessage({ id: messageID }),
modifierString,
valueString,
excludedString,
@@ -552,9 +563,14 @@ export class StringCriterionOption extends ModifierCriterionOption {
export function createStringCriterionOption(
type: CriterionType,
messageID?: string
messageID?: string,
options?: { nsfw?: boolean }
) {
return new StringCriterionOption({ messageID: messageID ?? type, type });
return new StringCriterionOption({
messageID: messageID ?? type,
type,
...options,
});
}
export class MandatoryStringCriterionOption extends ModifierCriterionOption {
@@ -755,7 +771,8 @@ export class MandatoryNumberCriterionOption extends ModifierCriterionOption {
constructor(
messageID: string,
value: CriterionType,
makeCriterion?: () => ModifierCriterion<CriterionValue>
makeCriterion?: () => ModifierCriterion<CriterionValue>,
options?: { sfwMessageID?: string }
) {
super({
messageID,
@@ -773,15 +790,22 @@ export class MandatoryNumberCriterionOption extends ModifierCriterionOption {
makeCriterion: makeCriterion
? makeCriterion
: () => new NumberCriterion(this),
...options,
});
}
}
export function createMandatoryNumberCriterionOption(
value: CriterionType,
messageID?: string
messageID?: string,
options?: { sfwMessageID?: string }
) {
return new MandatoryNumberCriterionOption(messageID ?? value, value);
return new MandatoryNumberCriterionOption(
messageID ?? value,
value,
undefined,
options
);
}
export function encodeRangeValue<V>(

View File

@@ -4,6 +4,7 @@ import { DisplayMode } from "./types";
export interface ISortByOption {
messageID: string;
value: string;
sfwMessageID?: string;
}
export const MediaSortByOptions = [
@@ -22,7 +23,7 @@ export class ListFilterOptions {
public readonly displayModeOptions: DisplayMode[] = [];
public readonly criterionOptions: CriterionOption[] = [];
public static createSortBy(value: string) {
public static createSortBy(value: string): ISortByOption {
return {
messageID: value,
value,

View File

@@ -38,6 +38,7 @@ const sortByOptions = [
{
messageID: "o_count",
value: "o_counter",
sfwMessageID: "o_count_sfw",
},
]);
const displayModeOptions = [DisplayMode.Grid];
@@ -53,7 +54,9 @@ const criterionOptions = [
RatingCriterionOption,
PerformersCriterionOption,
createDateCriterionOption("date"),
createMandatoryNumberCriterionOption("o_counter", "o_count"),
createMandatoryNumberCriterionOption("o_counter", "o_count", {
sfwMessageID: "o_count_sfw",
}),
ContainingGroupsCriterionOption,
SubGroupsCriterionOption,
createMandatoryNumberCriterionOption("containing_group_count"),

View File

@@ -31,6 +31,7 @@ const sortByOptions = ["filesize", "file_count", "date", ...MediaSortByOptions]
{
messageID: "o_count",
value: "o_counter",
sfwMessageID: "o_count_sfw",
},
]);
const displayModeOptions = [DisplayMode.Grid, DisplayMode.Wall];
@@ -43,7 +44,9 @@ const criterionOptions = [
PathCriterionOption,
GalleriesCriterionOption,
OrganizedCriterionOption,
createMandatoryNumberCriterionOption("o_counter", "o_count"),
createMandatoryNumberCriterionOption("o_counter", "o_count", {
sfwMessageID: "o_count_sfw",
}),
ResolutionCriterionOption,
OrientationCriterionOption,
ImageIsMissingCriterionOption,

View File

@@ -31,7 +31,6 @@ const sortByOptions = [
"penis_length",
"play_count",
"last_played_at",
"last_o_at",
"career_length",
"weight",
"measurements",
@@ -54,6 +53,12 @@ const sortByOptions = [
{
messageID: "o_count",
value: "o_counter",
sfwMessageID: "o_count_sfw",
},
{
messageID: "last_o_at",
value: "last_o_at",
sfwMessageID: "last_o_at_sfw",
},
]);
@@ -102,7 +107,9 @@ const criterionOptions = [
createMandatoryNumberCriterionOption("image_count"),
createMandatoryNumberCriterionOption("gallery_count"),
createMandatoryNumberCriterionOption("play_count"),
createMandatoryNumberCriterionOption("o_counter", "o_count"),
createMandatoryNumberCriterionOption("o_counter", "o_count", {
sfwMessageID: "o_count_sfw",
}),
createBooleanCriterionOption("ignore_auto_tag"),
CountryCriterionOption,
createNumberCriterionOption("height_cm", "height"),

View File

@@ -46,7 +46,6 @@ const sortByOptions = [
"framerate",
"bitrate",
"last_played_at",
"last_o_at",
"resume_time",
"play_duration",
"play_count",
@@ -62,6 +61,12 @@ const sortByOptions = [
{
messageID: "o_count",
value: "o_counter",
sfwMessageID: "o_count_sfw",
},
{
messageID: "last_o_at",
value: "last_o_at",
sfwMessageID: "last_o_at_sfw",
},
{
messageID: "group_scene_number",
@@ -97,7 +102,9 @@ const criterionOptions = [
DuplicatedCriterionOption,
OrganizedCriterionOption,
RatingCriterionOption,
createMandatoryNumberCriterionOption("o_counter", "o_count"),
createMandatoryNumberCriterionOption("o_counter", "o_count", {
sfwMessageID: "o_count_sfw",
}),
ResolutionCriterionOption,
OrientationCriterionOption,
createMandatoryNumberCriterionOption("framerate"),