Commit ddb8aa2979

ddb8aa297962379fa7731032140221ba35f84b50

parent: ad54fbcda1

Unsigned

cmc <hello@cleberg.net> · 2026-08-07 23:09 UTC

feat: add hide-ai option to omit AI-generated deviations

Adds a hide-ai config flag that drops deviations flagged data.AI from
every listing (search, daily, galleries, favourites) in the shared
DeviationList loop, analogous to how nsfw gates mature content. The
flag is already parsed and shown as a marker, so this is a filter over
existing data. Off by default.

Closes #15

Layout: unified · split

SETUP.txt +2
@@ -35,6 +35,8 @@ Time units:
3535 DeviantArt's CDN. Required by `cache`; when off, clients fetch images from
3636 wixmp directly.
3737* `nsfw` — Show mature content.
38* `hide-ai` — Omit AI-generated deviations (those flagged `[🤖]`) from all
39 listings: search, daily deviations, galleries and favourites.
3840
3941# Setting up reverse proxy
4042Pretty much business as usual, except for the [`X-Forwarded-Proto`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto) header setting.
app/config.go +1
@@ -34,6 +34,7 @@ type config struct {
3434 Cache cacheConfig `json:"cache"`
3535 Proxy bool `json:"proxy"`
3636 Nsfw bool `json:"nsfw"`
37 HideAI bool `json:"hide-ai"`
3738 UserAgent string `json:"user-agent"`
3839 DownloadProxy string `json:"download-proxy"`
3940 StaticPath string `json:"static-path"`
app/parsers.go +3
@@ -92,6 +92,9 @@ func (s skunkyart) DeviationList(devs []devianter.Deviation, allowAtom bool, con
9292
9393 for i, l := 0, len(devs); i < l; i++ {
9494 data := &devs[i]
95 if data.AI && CFG.HideAI {
96 continue
97 }
9598 if preview, fullview := ParseMedia(s.Host, data.Media, 320), ParseMedia(s.Host, data.Media); !data.NSFW || CFG.Nsfw {
9699 if allowAtom && s.Atom {
97100 s.Writer.Header().Add("Content-Type", "application/atom+xml")
app/parsers_test.go added +58
@@ -0,0 +1,58 @@
1package app
2
3import (
4 "strings"
5 "testing"
6
7 "github.com/krazywarez/devianter"
8)
9
10// aiListDevs returns one human-made and one AI-flagged deviation, each with a
11// distinct title so the rendered output can be searched for either one.
12func aiListDevs() []devianter.Deviation {
13 human := devianter.Deviation{Title: "HumanArt"}
14 human.Author.Username = "alice"
15
16 robot := devianter.Deviation{Title: "RobotArt", AI: true}
17 robot.Author.Username = "bob"
18
19 return []devianter.Deviation{human, robot}
20}
21
22// TestDeviationListHidesAIWhenConfigured is the regression test for the hide-ai
23// option: with it on, deviations flagged data.AI must not appear in a listing at
24// all, while human-made ones are untouched.
25func TestDeviationListHidesAIWhenConfigured(t *testing.T) {
26 nsfw, hide := CFG.Nsfw, CFG.HideAI
27 CFG.Nsfw, CFG.HideAI = true, true
28 defer func() { CFG.Nsfw, CFG.HideAI = nsfw, hide }()
29
30 out := skunkyart{Host: "http://localhost"}.DeviationList(aiListDevs(), false)
31
32 if !strings.Contains(out, "HumanArt") {
33 t.Error("human deviation was dropped, want it kept")
34 }
35 if strings.Contains(out, "RobotArt") {
36 t.Error("AI deviation rendered while hide-ai is on, want it omitted")
37 }
38 if strings.Contains(out, "🤖") {
39 t.Error("AI marker present while hide-ai is on")
40 }
41}
42
43// TestDeviationListShowsAIByDefault pins down that the filter is opt-in: with
44// hide-ai off the AI deviation is still listed and still carries its marker.
45func TestDeviationListShowsAIByDefault(t *testing.T) {
46 nsfw, hide := CFG.Nsfw, CFG.HideAI
47 CFG.Nsfw, CFG.HideAI = true, false
48 defer func() { CFG.Nsfw, CFG.HideAI = nsfw, hide }()
49
50 out := skunkyart{Host: "http://localhost"}.DeviationList(aiListDevs(), false)
51
52 if !strings.Contains(out, "RobotArt") {
53 t.Error("AI deviation dropped while hide-ai is off, want it kept")
54 }
55 if !strings.Contains(out, "🤖") {
56 t.Error("AI marker missing while hide-ai is off")
57 }
58}
config.example.json +2 −1
@@ -13,5 +13,6 @@
1313 "download-proxy": "",
1414 "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
1515 "proxy": true,
16 "nsfw": false
16 "nsfw": false,
17 "hide-ai": false
1718}