diff --git a/src/formatters.nim b/src/formatters.nim
index 9e27fe5..4a268c0 100644
--- a/src/formatters.nim
+++ b/src/formatters.nim
@@ -11,6 +11,8 @@ const
usernameRegex = re"(^|[^A-z0-9_?])@([A-z0-9_]+)"
picRegex = re"pic.twitter.com/[^ ]+"
ellipsisRegex = re" ?…"
+ ytRegex = re"youtu(be.com|.be)"
+ twRegex = re"twitter.com"
nbsp = $Rune(0x000A0)
proc stripText*(text: string): string =
@@ -46,7 +48,7 @@ proc reUsernameToLink*(m: RegexMatch; s: string): string =
pretext & toLink("/" & username, "@" & username)
-proc linkifyText*(text: string): string =
+proc linkifyText*(text: string; prefs: Prefs): string =
result = xmltree.escape(stripText(text))
result = result.replace(ellipsisRegex, "")
result = result.replace(emailRegex, reEmailToLink)
@@ -55,6 +57,16 @@ proc linkifyText*(text: string): string =
result = result.replace(re"([^\s\(\n%])\s+([;.,!\)'%]|')", "$1")
result = result.replace(re"^\. 0:
+ result = result.replace(ytRegex, prefs.replaceYouTube)
+ if prefs.replaceTwitter.len > 0:
+ result = result.replace(twRegex, prefs.replaceTwitter)
+
+proc replaceUrl*(url: string; prefs: Prefs): string =
+ if prefs.replaceYouTube.len > 0:
+ return url.replace(ytRegex, prefs.replaceYouTube)
+ if prefs.replaceTwitter.len > 0:
+ return url.replace(twRegex, prefs.replaceTwitter)
proc stripTwitterUrls*(text: string): string =
result = text
diff --git a/src/nitter.nim b/src/nitter.nim
index 8b29c7d..45198bf 100644
--- a/src/nitter.nim
+++ b/src/nitter.nim
@@ -1,4 +1,5 @@
-import asyncdispatch, asyncfile, httpclient, sequtils, strutils, strformat, uri, os
+import asyncdispatch, asyncfile, httpclient, uri, os
+import sequtils, strformat, strutils
from net import Port
import jester, regex
diff --git a/src/prefs.nim b/src/prefs.nim
index 93549b8..4fb9ef7 100644
--- a/src/prefs.nim
+++ b/src/prefs.nim
@@ -1,4 +1,4 @@
-import asyncdispatch, times, macros, tables
+import asyncdispatch, times, macros, tables, xmltree
import types
withCustomDb("prefs.db", "", "", ""):
@@ -25,6 +25,16 @@ type
placeholder*: string
const prefList*: Table[string, seq[Pref]] = {
+ "Privacy": @[
+ Pref(kind: input, name: "replaceTwitter",
+ label: "Replace Twitter links with Nitter (blank to disable)",
+ defaultInput: "nitter.net", placeholder: "Nitter hostname"),
+
+ Pref(kind: input, name: "replaceYouTube",
+ label: "Replace YouTube links with Invidious (blank to disable)",
+ defaultInput: "invidio.us", placeholder: "Invidious hostname")
+ ],
+
"Media": @[
Pref(kind: checkbox, name: "videoPlayback",
label: "Enable hls.js video playback (requires JavaScript)",
@@ -94,7 +104,7 @@ macro genUpdatePrefs*(): untyped =
of checkbox:
result.add quote do: prefs.`ident` = `value` == "on"
of input:
- result.add quote do: prefs.`ident` = `value`
+ result.add quote do: prefs.`ident` = xmltree.escape(strip(`value`))
of select:
let options = pref.options
let default = pref.defaultOption
diff --git a/src/types.nim b/src/types.nim
index 8787fc0..38ef3b5 100644
--- a/src/types.nim
+++ b/src/types.nim
@@ -43,9 +43,9 @@ db("cache.db", "", "", ""):
thumb*: string
views*: string
playbackType* {.
- dbType: "STRING",
- parseIt: parseEnum[VideoType](it.s),
- formatIt: $it,
+ dbType: "STRING"
+ parseIt: parseEnum[VideoType](it.s)
+ formatIt: $it
.}: VideoType
available* {.dbType: "STRING", parseIt: parseBool(it.s) formatIt: $it.}: bool
@@ -55,6 +55,8 @@ db("cache.db", "", "", ""):
hideTweetStats* {.dbType: "STRING", parseIt: parseBool(it.s), formatIt: $it.}: bool
hideBanner* {.dbType: "STRING", parseIt: parseBool(it.s), formatIt: $it.}: bool
stickyProfile* {.dbType: "STRING", parseIt: parseBool(it.s), formatIt: $it.}: bool
+ replaceYouTube*: string
+ replaceTwitter*: string
type
QueryKind* = enum
diff --git a/src/views/profile.nim b/src/views/profile.nim
index 513b0d9..e6b8318 100644
--- a/src/views/profile.nim
+++ b/src/views/profile.nim
@@ -11,7 +11,7 @@ proc renderStat(num, class: string; text=""): VNode =
span(class="profile-stat-num"):
text if num.len == 0: "?" else: num
-proc renderProfileCard*(profile: Profile): VNode =
+proc renderProfileCard*(profile: Profile; prefs: Prefs): VNode =
buildHtml(tdiv(class="profile-card")):
a(class="profile-card-avatar", href=profile.getUserPic().getSigUrl("pic")):
genImg(profile.getUserpic("_200x200"))
@@ -23,7 +23,7 @@ proc renderProfileCard*(profile: Profile): VNode =
tdiv(class="profile-card-extra"):
if profile.bio.len > 0:
tdiv(class="profile-bio"):
- p: verbatim linkifyText(profile.bio)
+ p: verbatim linkifyText(profile.bio, prefs)
if profile.location.len > 0:
tdiv(class="profile-location"):
@@ -76,7 +76,7 @@ proc renderProfile*(profile: Profile; timeline: Timeline;
let sticky = if prefs.stickyProfile: "sticky" else: "unset"
tdiv(class="profile-tab", style={position: sticky}):
- renderProfileCard(profile)
+ renderProfileCard(profile, prefs)
if photoRail.len > 0:
renderPhotoRail(profile, photoRail)
diff --git a/src/views/tweet.nim b/src/views/tweet.nim
index 407dd4e..d36ac84 100644
--- a/src/views/tweet.nim
+++ b/src/views/tweet.nim
@@ -92,7 +92,7 @@ proc renderPoll(poll: Poll): VNode =
proc renderCardImage(card: Card): VNode =
buildHtml(tdiv(class="card-image-container")):
tdiv(class="card-image"):
- img(src=get(card.image).getSigUrl("pic"))
+ img(src=getSigUrl(get(card.image), "pic"))
if card.kind == player:
tdiv(class="card-overlay"):
tdiv(class="card-overlay-circle"):
@@ -103,7 +103,7 @@ proc renderCard(card: Card; prefs: Prefs): VNode =
let large = if card.kind in largeCards: " large" else: ""
buildHtml(tdiv(class=("card" & large))):
- a(class="card-container", href=card.url):
+ a(class="card-container", href=replaceUrl(card.url, prefs)):
if card.image.isSome:
renderCardImage(card)
elif card.video.isSome:
@@ -147,7 +147,7 @@ proc renderQuoteMedia(quote: Quote): VNode =
tdiv(class="quote-sensitive"):
icon "attention", class="quote-sensitive-icon"
-proc renderQuote(quote: Quote): VNode =
+proc renderQuote(quote: Quote; prefs: Prefs): VNode =
if not quote.available:
return buildHtml(tdiv(class="quote unavailable")):
tdiv(class="unavailable-quote"):
@@ -167,7 +167,7 @@ proc renderQuote(quote: Quote): VNode =
renderReply(quote)
tdiv(class="quote-text"):
- verbatim linkifyText(quote.text)
+ verbatim linkifyText(quote.text, prefs)
if quote.hasThread:
a(class="show-thread", href=getLink(quote)):
@@ -194,10 +194,10 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; class="";
renderReply(tweet)
tdiv(class="status-content media-body"):
- verbatim linkifyText(tweet.text)
+ verbatim linkifyText(tweet.text, prefs)
if tweet.quote.isSome:
- renderQuote(tweet.quote.get())
+ renderQuote(tweet.quote.get(), prefs)
if tweet.card.isSome:
renderCard(tweet.card.get(), prefs)