From 5a79ec025aebf51fda8348723a4f79f200b3ed75 Mon Sep 17 00:00:00 2001 From: kuu7o Date: Wed, 19 Nov 2025 12:28:26 -0300 Subject: [PATCH] feat(pref): add ability to hide NSFW content --- nitter.example.conf | 1 + src/parser.nim | 2 ++ src/prefs_impl.nim | 3 +++ src/types.nim | 2 ++ src/views/general.nim | 4 ++++ src/views/profile.nim | 3 ++- src/views/timeline.nim | 3 ++- src/views/tweet.nim | 3 +++ 8 files changed, 19 insertions(+), 2 deletions(-) diff --git a/nitter.example.conf b/nitter.example.conf index fda8897..578f943 100644 --- a/nitter.example.conf +++ b/nitter.example.conf @@ -40,3 +40,4 @@ replaceSoundCloud = "sc.kuuro.net" proxyVideos = true hlsPlayback = false infiniteScroll = false +hideNsfw = true diff --git a/src/parser.nim b/src/parser.nim index 614ab57..02833b6 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -23,6 +23,7 @@ proc parseUser(js: JsonNode; id=""): User = media: js{"media_count"}.getInt, verifiedType: parseEnum[VerifiedType](js{"verified_type"}.getStr("None")), protected: js{"protected"}.getBool, + sensitive: "sensitive" in js{"profile_interstitial_type"}.getStr("") or js{"possibly_sensitive"}.getBool, joinDate: js{"created_at"}.getTime ) @@ -225,6 +226,7 @@ proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet = text: js{"full_text"}.getStr, time: js{"created_at"}.getTime, hasThread: js{"self_thread"}.notNull, + sensitive: js{"possibly_sensitive"}.getBool, available: true, user: User(id: js{"user_id_str"}.getStr), stats: TweetStats( diff --git a/src/prefs_impl.nim b/src/prefs_impl.nim index 8149d73..a3ed888 100644 --- a/src/prefs_impl.nim +++ b/src/prefs_impl.nim @@ -82,6 +82,9 @@ genPrefs: squareAvatars(checkbox, false): "Square profile pictures" + hideNsfw(checkbox, true): + "Hide NSFW content" + Media: mp4Playback(checkbox, true): "Enable mp4 video playback (only for gifs)" diff --git a/src/types.nim b/src/types.nim index 0b3b5a7..fbd5f89 100644 --- a/src/types.nim +++ b/src/types.nim @@ -103,6 +103,7 @@ type media*: int verifiedType*: VerifiedType protected*: bool + sensitive*: bool suspended*: bool joinDate*: DateTime @@ -214,6 +215,7 @@ type time*: DateTime reply*: seq[string] pinned*: bool + sensitive*: bool hasThread*: bool available*: bool tombstone*: string diff --git a/src/views/general.nim b/src/views/general.nim index 1c159ee..8bc0719 100644 --- a/src/views/general.nim +++ b/src/views/general.nim @@ -120,6 +120,10 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc=""; link(rel="preload", type="font/woff2", `as`="font", href="/fonts/fontello.woff2?61663884", crossorigin="anonymous") + if prefs.hideNsfw: + style: + verbatim ".nsfw { display: none !important; }" + proc renderMain*(body: VNode; req: Request; cfg: Config; prefs=defaultPrefs; titleText=""; desc=""; ogTitle=""; rss=""; video=""; images: seq[string] = @[]; banner=""): string = diff --git a/src/views/profile.nim b/src/views/profile.nim index 2b2e410..72f56f3 100644 --- a/src/views/profile.nim +++ b/src/views/profile.nim @@ -13,7 +13,8 @@ proc renderStat(num: int; class: string; text=""): VNode = text insertSep($num, ',') proc renderUserCard*(user: User; prefs: Prefs): VNode = - buildHtml(tdiv(class="profile-card")): + let class = if user.sensitive: "profile-card nsfw" else: "profile-card" + buildHtml(tdiv(class=class)): tdiv(class="profile-card-info"): let url = getPicUrl(user.getUserPic()) diff --git a/src/views/timeline.nim b/src/views/timeline.nim index abeb6d3..d3a5b87 100644 --- a/src/views/timeline.nim +++ b/src/views/timeline.nim @@ -56,7 +56,8 @@ proc renderThread(thread: Tweets; prefs: Prefs; path: string): VNode = index=i, last=(i == thread.high), showThread=show) proc renderUser(user: User; prefs: Prefs): VNode = - buildHtml(tdiv(class="timeline-item")): + let class = if user.sensitive: "timeline-item nsfw" else: "timeline-item" + buildHtml(tdiv(class=class)): a(class="tweet-link", href=("/" & user.username)) tdiv(class="tweet-body profile-result"): tdiv(class="tweet-header"): diff --git a/src/views/tweet.nim b/src/views/tweet.nim index 71749ca..6fa4d08 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -277,6 +277,9 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0; if index == -1 or last: divClass = "thread-last " & class + if tweet.sensitive: + divClass.add " nsfw" + if not tweet.available: return buildHtml(tdiv(class=divClass & "unavailable timeline-item")): tdiv(class="unavailable-box"):