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: |
| 35 | 35 | DeviantArt's CDN. Required by `cache`; when off, clients fetch images from |
| 36 | 36 | wixmp directly. |
| 37 | 37 | * `nsfw` — Show mature content. |
| 38 | * `hide-ai` — Omit AI-generated deviations (those flagged `[🤖]`) from all |
| 39 | listings: search, daily deviations, galleries and favourites. |
| 38 | 40 | |
| 39 | 41 | # Setting up reverse proxy |
| 40 | 42 | Pretty 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 { |
| 34 | 34 | Cache cacheConfig `json:"cache"` |
| 35 | 35 | Proxy bool `json:"proxy"` |
| 36 | 36 | Nsfw bool `json:"nsfw"` |
| 37 | HideAI bool `json:"hide-ai"` |
| 37 | 38 | UserAgent string `json:"user-agent"` |
| 38 | 39 | DownloadProxy string `json:"download-proxy"` |
| 39 | 40 | StaticPath string `json:"static-path"` |
app/parsers.go
+3
| @@ -92,6 +92,9 @@ func (s skunkyart) DeviationList(devs []devianter.Deviation, allowAtom bool, con |
| 92 | 92 | |
| 93 | 93 | for i, l := 0, len(devs); i < l; i++ { |
| 94 | 94 | data := &devs[i] |
| 95 | if data.AI && CFG.HideAI { |
| 96 | continue |
| 97 | } |
| 95 | 98 | if preview, fullview := ParseMedia(s.Host, data.Media, 320), ParseMedia(s.Host, data.Media); !data.NSFW || CFG.Nsfw { |
| 96 | 99 | if allowAtom && s.Atom { |
| 97 | 100 | s.Writer.Header().Add("Content-Type", "application/atom+xml") |
app/parsers_test.go
added
+58
| @@ -0,0 +1,58 @@ |
| 1 | package app |
| 2 | |
| 3 | import ( |
| 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. |
| 12 | func 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. |
| 25 | func 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. |
| 45 | func 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 @@ |
| 13 | 13 | "download-proxy": "", |
| 14 | 14 | "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", |
| 15 | 15 | "proxy": true, |
| 16 | | "nsfw": false |
| 16 | "nsfw": false, |
| 17 | "hide-ai": false |
| 17 | 18 | } |