Compare commits

..

No commits in common. "1cf37e4e84d1fedc234a4da580ca424d0161b02b" and "9129820cb02a57213a1f7a32456436f1922edbbb" have entirely different histories.

6 changed files with 31 additions and 87 deletions

View file

@ -4,9 +4,6 @@ Nitter is a free and open source alternative Twitter front-end focused on
privacy and performance. The source is available on GitHub at privacy and performance. The source is available on GitHub at
<https://github.com/zedeus/nitter> <https://github.com/zedeus/nitter>
**This instance is running a fork, whose source can be found at**
<https://gitdab.com/Cynosphere/nitter>
* No JavaScript or ads * No JavaScript or ads
* All requests go through the backend, client never talks to Twitter * All requests go through the backend, client never talks to Twitter
* Prevents Twitter from tracking your IP or JavaScript fingerprint * Prevents Twitter from tracking your IP or JavaScript fingerprint
@ -22,13 +19,6 @@ Nitter's GitHub wiki contains
[browser extensions](https://github.com/zedeus/nitter/wiki/Extensions) [browser extensions](https://github.com/zedeus/nitter/wiki/Extensions)
maintained by the community. maintained by the community.
### Fork features
* Localized following via cookies (list exportable and editable in preferences)
* Image zooming/carousel (requires JavaScript)
* Up to date Twitter features, e.g. Community Notes
* Embeds for chat services on-par with services like [FxTwitter](https://github.com/FixTweet/FxTwitter) and [vxTwitter](https://github.com/dylanpdx/BetterTwitFix)
## Why use Nitter? ## Why use Nitter?
It's impossible to use Twitter without JavaScript enabled. For privacy-minded It's impossible to use Twitter without JavaScript enabled. For privacy-minded
@ -46,12 +36,12 @@ Twitter without JavaScript while retaining your privacy. In addition to
respecting your privacy, Nitter is on average around 15 times lighter than respecting your privacy, Nitter is on average around 15 times lighter than
Twitter, and in most cases serves pages faster (eg. timelines load 2-4x faster). Twitter, and in most cases serves pages faster (eg. timelines load 2-4x faster).
In the future a simple account system will be added that lets you follow Twitter
users, allowing you to have a clean chronological timeline without needing a
Twitter account.
## Donating ## Donating
Even though I could be selfish and point people to donate to me instead of
Zedeus, it would be disrespectful.
GitHub Sponsors: <https://github.com/sponsors/zedeus> \
Liberapay: <https://liberapay.com/zedeus> \ Liberapay: <https://liberapay.com/zedeus> \
Patreon: <https://patreon.com/nitter> \ Patreon: <https://patreon.com/nitter> \
BTC: bc1qp7q4qz0fgfvftm5hwz3vy284nue6jedt44kxya \ BTC: bc1qp7q4qz0fgfvftm5hwz3vy284nue6jedt44kxya \
@ -59,23 +49,6 @@ ETH: 0x66d84bc3fd031b62857ad18c62f1ba072b011925 \
LTC: ltc1qhsz5nxw6jw9rdtw9qssjeq2h8hqk2f85rdgpkr \ LTC: ltc1qhsz5nxw6jw9rdtw9qssjeq2h8hqk2f85rdgpkr \
XMR: 42hKayRoEAw4D6G6t8mQHPJHQcXqofjFuVfavqKeNMNUZfeJLJAcNU19i1bGdDvcdN6romiSscWGWJCczFLe9RFhM3d1zpL XMR: 42hKayRoEAw4D6G6t8mQHPJHQcXqofjFuVfavqKeNMNUZfeJLJAcNU19i1bGdDvcdN6romiSscWGWJCczFLe9RFhM3d1zpL
## Credits ## Contact
* Zedeus for this project Feel free to join our [Matrix channel](https://matrix.to/#/#nitter:matrix.org).
* PrivacyDevel, cmj, and taskylizard for keeping this project alive with forks after the main repo went inactive
* Every other contributors who've committed to the main repo in the past
## To any law enforcement agencies and copyright holders
**All illegal content should be reported to Twitter directly.** This service is
merely a proxy of Twitter and no content is hosted on this server. Do not waste
your time contacting internet service providers, hosting providers and/or domain
registrars.
If you would like more context, you can read about this exact issue happening to
[PussTheCat.org's instance](https://pussthecat.org/nitter/).
I emplore all Nitter instance hosts to not enable media proxying, even if it
"phones home" to Twitter's CDN (which doesn't really pose a tracking risk and
breaks videos anyways), as it [has been used as an attack vector to take down
nitter.net](https://github.com/zedeus/nitter/issues/1150#issuecomment-1890855255).

View file

@ -208,7 +208,6 @@ proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet =
id: js{"id_str"}.getId, id: js{"id_str"}.getId,
threadId: js{"conversation_id_str"}.getId, threadId: js{"conversation_id_str"}.getId,
replyId: js{"in_reply_to_status_id_str"}.getId, replyId: js{"in_reply_to_status_id_str"}.getId,
replyHandle: js{"in_reply_to_screen_name"}.getStr,
text: js{"full_text"}.getStr, text: js{"full_text"}.getStr,
time: js{"created_at"}.getTime, time: js{"created_at"}.getTime,
hasThread: js{"self_thread"}.notNull, hasThread: js{"self_thread"}.notNull,

View file

@ -1,5 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
import asyncdispatch, strutils, sequtils, uri, options, sugar, strformat import asyncdispatch, strutils, sequtils, uri, options, sugar
import jester, karax/vdom import jester, karax/vdom
@ -64,43 +64,30 @@ proc createStatusRouter*(cfg: Config) =
resp Http404, showError(error, cfg) resp Http404, showError(error, cfg)
let let
tweet = conv.tweet title = pageTitle(conv.tweet)
title = pageTitle(tweet) ogTitle = pageTitle(conv.tweet.user)
ogTitle = pageTitle(tweet.user) desc = conv.tweet.text
desc = tweet.text avatar = conv.tweet.user.userPic
avatar = tweet.user.userPic time = some(conv.tweet.time)
time = some(tweet.time)
var var
images = tweet.photos images = conv.tweet.photos
video = "" video = ""
context = ""
contextUrl = ""
if tweet.quote.isSome(): if conv.tweet.video.isSome():
let let videoObj = get(conv.tweet.video)
quote = tweet.quote.get()
quoteUser = quote.user
context = &"↘ Quoting: {quoteUser.fullname} (@{quoteUser.username})"
contextUrl = &"{getUrlPrefix(cfg)}/i/status/{quote.id}"
elif tweet.replyId != 0:
context = &"↩ Replying to: @{tweet.replyHandle}"
contextUrl = &"{getUrlPrefix(cfg)}/i/status/{tweet.replyId}"
if tweet.video.isSome():
let videoObj = get(tweet.video)
images = @[videoObj.thumb] images = @[videoObj.thumb]
let vars = videoObj.variants.filterIt(it.contentType == mp4) let vars = videoObj.variants.filterIt(it.contentType == mp4)
# idk why this wont sort when it sorts everywhere else # idk why this wont sort when it sorts everywhere else
#video = vars.sortedByIt(it.bitrate)[^1].url #video = vars.sortedByIt(it.bitrate)[^1].url
video = vars[^1].url video = vars[^1].url
elif tweet.gif.isSome(): elif conv.tweet.gif.isSome():
let gif = get(tweet.gif) let gif = get(conv.tweet.gif)
images = @[gif.thumb] images = @[gif.thumb]
video = getPicUrl(gif.url) video = getPicUrl(gif.url)
#elif tweet.card.isSome(): #elif conv.tweet.card.isSome():
# let card = tweet.card.get() # let card = conv.tweet.card.get()
# if card.image.len > 0: # if card.image.len > 0:
# images = @[card.image] # images = @[card.image]
# elif card.video.isSome(): # elif card.video.isSome():
@ -108,8 +95,7 @@ proc createStatusRouter*(cfg: Config) =
let html = renderConversation(conv, prefs, getPath() & "#m") let html = renderConversation(conv, prefs, getPath() & "#m")
resp renderMain(html, request, cfg, prefs, title, desc, ogTitle, resp renderMain(html, request, cfg, prefs, title, desc, ogTitle,
images=images, video=video, avatar=avatar, time=time, images=images, video=video, avatar=avatar, time=time)
context=context, contextUrl=contextUrl)
get "/@name/@s/@id/@m/?@i?": get "/@name/@s/@id/@m/?@i?":
cond @"s" in ["status", "statuses"] cond @"s" in ["status", "statuses"]

View file

@ -211,7 +211,6 @@ type
text*: string text*: string
time*: DateTime time*: DateTime
reply*: seq[string] reply*: seq[string]
replyHandle*: string
pinned*: bool pinned*: bool
hasThread*: bool hasThread*: bool
available*: bool available*: bool

View file

@ -5,7 +5,7 @@ import karax/[karaxdsl, vdom]
const const
date = staticExec("git show -s --format=\"%cd\" --date=format:\"%Y.%m.%d\"") date = staticExec("git show -s --format=\"%cd\" --date=format:\"%Y.%m.%d\"")
hash = staticExec("git show -s --format=\"%h\"") hash = staticExec("git show -s --format=\"%h\"")
link = "https://gitdab.com/Cynosphere/nitter/commit/" & hash link = "https://github.com/zedeus/nitter/commit/" & hash
version = &"{date}-{hash}" version = &"{date}-{hash}"
var aboutHtml: string var aboutHtml: string

View file

@ -38,7 +38,7 @@ proc renderNavbar(cfg: Config; req: Request; rss, canonical: string): VNode =
proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc=""; proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc="";
video=""; images: seq[string] = @[]; banner=""; ogTitle=""; video=""; images: seq[string] = @[]; banner=""; ogTitle="";
rss=""; canonical=""; avatar=""; context=""; contextUrl=""; rss=""; canonical=""; avatar="";
time: Option[DateTime] = none(DateTime)): VNode = time: Option[DateTime] = none(DateTime)): VNode =
var theme = prefs.theme.toTheme var theme = prefs.theme.toTheme
if "theme" in req.params: if "theme" in req.params:
@ -140,28 +140,16 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc="";
meta(property="og:video:url", content=video) meta(property="og:video:url", content=video)
meta(property="og:video:secure_url", content=video) meta(property="og:video:secure_url", content=video)
meta(property="og:video:type", content="video/mp4") meta(property="og:video:type", content="video/mp4")
var title = encodeUrl(finalizedDesc)
var var author = encodeUrl(finalizedTitleText)
title = encodeUrl(finalizedDesc)
author = encodeUrl(finalizedTitleText)
url = req.path
if len(finalizedDesc) > 67: if len(finalizedDesc) > 67:
title = author title = author
author = encodeUrl(finalizedDesc) author = encodeUrl(finalizedDesc)
if context != "": verbatim &"<link rel=\"alternate\" href=\"{getUrlPrefix(cfg)}/oembed.json?type=video&provider={encodeUrl(siteName)}&title={title}&user={author}&url={encodeUrl(req.path)}\" type=\"application/json+oembed\" />"
author = encodeUrl(context & "\n") & author #link(rel="alternate",
# href=&"{getUrlPrefix(cfg)}/oembed.json?type=video&title={encodeUrl(stripHtml(desc))}&user={encodeUrl(finalizedTitleText)}&url={encodeUrl(req.path)}",
if contextUrl != "": # `type`="application/json+oembed")
url = contextUrl
verbatim &"<link rel=\"alternate\" href=\"{getUrlPrefix(cfg)}/oembed.json?type=video&provider={encodeUrl(siteName)}&title={title}&user={author}&url={encodeUrl(url)}\" type=\"application/json+oembed\" />"
elif context != "" and contextUrl != "":
var
title = encodeUrl(finalizedTitleText)
author = encodeUrl(context)
verbatim &"<link rel=\"alternate\" href=\"{getUrlPrefix(cfg)}/oembed.json?type=video&provider={encodeUrl(siteName)}&title={title}&user={author}&url={encodeUrl(contextUrl)}\" type=\"application/json+oembed\" />"
# this is last so images are also preloaded # this is last so images are also preloaded
# if this is done earlier, Chrome only preloads one image for some reason # if this is done earlier, Chrome only preloads one image for some reason
@ -170,15 +158,14 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc="";
proc renderMain*(body: VNode; req: Request; cfg: Config; prefs=defaultPrefs; proc renderMain*(body: VNode; req: Request; cfg: Config; prefs=defaultPrefs;
titleText=""; desc=""; ogTitle=""; rss=""; video=""; titleText=""; desc=""; ogTitle=""; rss=""; video="";
images: seq[string] = @[]; banner=""; avatar=""; context=""; images: seq[string] = @[]; banner=""; avatar="";
contextUrl = ""; time: Option[DateTime] = none(DateTime) time: Option[DateTime] = none(DateTime)): string =
): string =
let canonical = getTwitterLink(req.path, req.params) let canonical = getTwitterLink(req.path, req.params)
let node = buildHtml(html(lang="en")): let node = buildHtml(html(lang="en")):
renderHead(prefs, cfg, req, titleText, desc, video, images, banner, ogTitle, renderHead(prefs, cfg, req, titleText, desc, video, images, banner, ogTitle,
rss, canonical, avatar, context, contextUrl, time) rss, canonical, avatar, time)
body: body:
renderNavbar(cfg, req, rss, canonical) renderNavbar(cfg, req, rss, canonical)