Skip to content

Commit 34bc872

Browse files
committed
Merge from upstream repo
Highlights of this merge: - Temporarily revert title truncation in RSS feeds (see zedeus/nitter#493) - Make tweet timestamps more consistent and indicate UTC time zone - Add description and verified badge to video cards - List API improvements (includes adding RSS feed support)
2 parents 59b1b63 + 9a578b3 commit 34bc872

26 files changed

+156
-132
lines changed

.dockerignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
.*
21
*.png
32
*.md
43
LICENSE

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ performance reasons.
9494

9595
### Docker
9696

97+
#### NOTE: For ARM64/ARM support, please use [unixfox's image](https://quay.io/repository/unixfox/nitter?tab=tags), more info [here](https://github.com/zedeus/nitter/issues/399#issuecomment-997263495)
98+
9799
To run Nitter with Docker, you'll need to install and run Redis separately
98100
before you can run the container. See below for how to also run Redis using
99101
Docker.

src/agents.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ proc windows(): string =
3131
trident = ["", "; Trident/5.0", "; Trident/6.0", "; Trident/7.0"]
3232
"Windows " & sample(nt) & sample(enc) & sample(arch) & sample(trident)
3333

34-
let macs = toSeq(6..15).mapIt($it) & @["14_4", "10_1", "9_3"]
34+
const macs = toSeq(6..15).mapIt($it) & @["14_4", "10_1", "9_3"]
3535

3636
proc mac(): string =
3737
"Macintosh; Intel Mac OS X 10_" & sample(macs) & sample(enc)

src/api.nim

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ proc getGraphProfile*(username: string): Future[Profile] {.async.} =
99
js = await fetch(graphUser ? {"variables": $variables})
1010
result = parseGraphProfile(js, username)
1111

12-
proc getGraphList*(name, list: string): Future[List] {.async.} =
12+
proc getGraphListBySlug*(name, list: string): Future[List] {.async.} =
1313
let
1414
variables = %*{"screenName": name, "listSlug": list, "withHighlightedLabel": false}
1515
js = await fetch(graphList ? {"variables": $variables})
1616
result = parseGraphList(js)
1717

18-
proc getGraphListById*(id: string): Future[List] {.async.} =
18+
proc getGraphList*(id: string): Future[List] {.async.} =
1919
let
2020
variables = %*{"listId": id, "withHighlightedLabel": false}
2121
js = await fetch(graphListId ? {"variables": $variables})

src/apiutils.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import types, tokens, consts, parserutils, http_pool
55

66
const rl = "x-rate-limit-"
77

8-
var pool {.threadvar.}: HttpPool
8+
var pool: HttpPool
99

1010
proc genParams*(pars: openarray[(string, string)] = @[]; cursor="";
1111
count="20"; ext=true): seq[(string, string)] =

src/formatters.nim

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,11 @@ proc getJoinDateFull*(profile: Profile): string =
118118
profile.joinDate.format("h:mm tt - d MMM YYYY")
119119

120120
proc getTime*(tweet: Tweet): string =
121-
tweet.time.format("d/M/yyyy', 'HH:mm:ss")
121+
tweet.time.format("MMM d', 'YYYY' · 'h:mm tt' UTC'")
122122

123123
proc getRfc822Time*(tweet: Tweet): string =
124124
tweet.time.format("ddd', 'dd MMM yyyy HH:mm:ss 'GMT'")
125125

126-
proc getTweetTime*(tweet: Tweet): string =
127-
tweet.time.format("h:mm tt' · 'MMM d', 'YYYY")
128-
129126
proc getShortTime*(tweet: Tweet): string =
130127
let now = now()
131128
let since = now - tweet.time

src/http_pool.nim

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ type
55
HttpPool* = ref object
66
conns*: seq[AsyncHttpClient]
77

8-
var maxConns {.threadvar.}: int
9-
var proxy {.threadvar.}: Proxy
8+
var maxConns: int
9+
var proxy: Proxy
1010

1111
proc setMaxHttpConns*(n: int) =
1212
maxConns = n

src/parser.nim

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ proc parseGraphList*(js: JsonNode): List =
7373

7474
result = List(
7575
id: list{"id_str"}.getStr,
76-
name: list{"name"}.getStr.replace(' ', '-'),
76+
name: list{"name"}.getStr,
7777
username: list{"user", "legacy", "screen_name"}.getStr,
78-
userId: list{"user", "legacy", "id_str"}.getStr,
78+
userId: list{"user", "rest_id"}.getStr,
7979
description: list{"description"}.getStr,
8080
members: list{"member_count"}.getInt,
8181
banner: list{"custom_banner_media", "media_info", "url"}.getImageStr
@@ -128,13 +128,16 @@ proc parseVideo(js: JsonNode): Video =
128128
views: js{"ext", "mediaStats", "r", "ok", "viewCount"}.getStr,
129129
available: js{"ext_media_availability", "status"}.getStr == "available",
130130
title: js{"ext_alt_text"}.getStr,
131-
durationMs: js{"duration_millis"}.getInt
131+
durationMs: js{"video_info", "duration_millis"}.getInt
132132
# playbackType: mp4
133133
)
134134

135135
with title, js{"additional_media_info", "title"}:
136136
result.title = title.getStr
137137

138+
with description, js{"additional_media_info", "description"}:
139+
result.description = description.getStr
140+
138141
for v in js{"video_info", "variants"}:
139142
result.variants.add VideoVariant(
140143
videoType: parseEnum[VideoType](v{"content_type"}.getStr("summary")),

src/parserutils.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ proc getBanner*(js: JsonNode): string =
118118
if color.len > 0:
119119
return '#' & color
120120

121-
# use primary color from profile picture color histrogram
121+
# use primary color from profile picture color histogram
122122
with p, js{"profile_image_extensions", "mediaColor", "r", "ok", "palette"}:
123123
if p.len > 0:
124124
let pal = p[0]{"rgb"}

src/prefs.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ from parsecfg import nil
66

77
export genUpdatePrefs, genResetPrefs
88

9-
var defaultPrefs* {.threadvar.}: Prefs
9+
var defaultPrefs*: Prefs
1010

1111
proc updateDefaultPrefs*(cfg: parsecfg.Config) =
1212
genDefaultPrefs()

src/redis_cache.nim

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ import redis, redpool, flatty, supersnappy
44

55
import types, api
66

7-
const redisNil = "\0\0"
7+
const
8+
redisNil = "\0\0"
9+
baseCacheTime = 60 * 60
810

911
var
10-
pool {.threadvar.}: RedisPool
11-
baseCacheTime = 60 * 60
12+
pool: RedisPool
1213
rssCacheTime: int
1314
listCacheTime*: int
1415

@@ -17,7 +18,9 @@ proc toFlatty*(s: var string, x: DateTime) =
1718
s.toFlatty(x.toTime().toUnix())
1819

1920
proc fromFlatty*(s: string, i: var int, x: var DateTime) =
20-
x = fromUnix(s.fromFlatty(int64)).utc()
21+
var unix: int64
22+
s.fromFlatty(i, unix)
23+
x = fromUnix(unix).utc()
2124

2225
proc setCacheTimes*(cfg: Config) =
2326
rssCacheTime = cfg.rssCacheTime * 60
@@ -56,7 +59,7 @@ proc initRedisPool*(cfg: Config) {.async.} =
5659

5760
template pidKey(name: string): string = "pid:" & $(hash(name) div 1_000_000)
5861
template profileKey(name: string): string = "p:" & name
59-
template listKey(l: List): string = toLower("l:" & l.username & '/' & l.name)
62+
template listKey(l: List): string = "l:" & l.id
6063

6164
proc get(query: string): Future[string] {.async.} =
6265
pool.withAcquire(r):
@@ -129,17 +132,17 @@ proc getCachedPhotoRail*(name: string): Future[PhotoRail] {.async.} =
129132
result = await getPhotoRail(name)
130133
await cache(result, name)
131134

132-
proc getCachedList*(username=""; name=""; id=""): Future[List] {.async.} =
133-
let list = if id.len > 0: redisNil
134-
else: await get(toLower("l:" & username & '/' & name))
135+
proc getCachedList*(username=""; slug=""; id=""): Future[List] {.async.} =
136+
let list = if id.len == 0: redisNil
137+
else: await get("l:" & id)
135138

136139
if list != redisNil:
137140
result = fromFlatty(uncompress(list), List)
138141
else:
139142
if id.len > 0:
140-
result = await getGraphListById(id)
143+
result = await getGraphList(id)
141144
else:
142-
result = await getGraphList(username, name)
145+
result = await getGraphListBySlug(username, slug)
143146
await cache(result)
144147

145148
proc getCachedRss*(key: string): Future[Rss] {.async.} =

src/routes/list.nim

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# SPDX-License-Identifier: AGPL-3.0-only
2-
import strutils
2+
import strutils, uri
33

44
import jester
55

@@ -8,41 +8,44 @@ import ".."/[types, redis_cache, api]
88
import ../views/[general, timeline, list]
99
export getListTimeline, getGraphList
1010

11-
template respList*(list, timeline, vnode: typed) =
12-
if list.id.len == 0:
13-
resp Http404, showError("List \"" & @"list" & "\" not found", cfg)
11+
template respList*(list, timeline, title, vnode: typed) =
12+
if list.id.len == 0 or list.name.len == 0:
13+
resp Http404, showError("List " & @"id" & " not found", cfg)
1414

1515
let
1616
html = renderList(vnode, timeline.query, list)
17-
rss = "/$1/lists/$2/rss" % [@"name", @"list"]
17+
rss = "/i/lists/$1/rss" % [@"id"]
1818

19-
resp renderMain(html, request, cfg, prefs, rss=rss, banner=list.banner)
19+
resp renderMain(html, request, cfg, prefs, titleText=title, rss=rss, banner=list.banner)
2020

2121
proc createListRouter*(cfg: Config) =
2222
router list:
23-
get "/@name/lists/@list/?":
23+
get "/@name/lists/@slug/?":
2424
cond '.' notin @"name"
2525
cond @"name" != "i"
26+
cond @"slug" != "memberships"
27+
let
28+
slug = decodeUrl(@"slug")
29+
list = await getCachedList(@"name", slug)
30+
if list.id.len == 0:
31+
resp Http404, showError("List \"" & @"slug" & "\" not found", cfg)
32+
redirect("/i/lists/" & list.id)
33+
34+
get "/i/lists/@id/?":
35+
cond '.' notin @"id"
2636
let
2737
prefs = cookiePrefs()
28-
list = await getCachedList(@"name", @"list")
38+
list = await getCachedList(id=(@"id"))
39+
title = "@" & list.username & "/" & list.name
2940
timeline = await getListTimeline(list.id, getCursor())
3041
vnode = renderTimelineTweets(timeline, prefs, request.path)
31-
respList(list, timeline, vnode)
42+
respList(list, timeline, title, vnode)
3243

33-
get "/@name/lists/@list/members":
34-
cond '.' notin @"name"
35-
cond @"name" != "i"
44+
get "/i/lists/@id/members":
45+
cond '.' notin @"id"
3646
let
3747
prefs = cookiePrefs()
38-
list = await getCachedList(@"name", @"list")
48+
list = await getCachedList(id=(@"id"))
49+
title = "@" & list.username & "/" & list.name
3950
members = await getListMembers(list, getCursor())
40-
respList(list, members, renderTimelineUsers(members, prefs, request.path))
41-
42-
get "/i/lists/@id/?":
43-
cond '.' notin @"id"
44-
let list = await getCachedList(id=(@"id"))
45-
if list.id.len == 0:
46-
resp Http404
47-
await cache(list)
48-
redirect("/" & list.username & "/lists/" & list.name)
51+
respList(list, members, title, renderTimelineUsers(members, prefs, request.path))

src/routes/preferences.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ proc createPrefRouter*(cfg: Config) =
3232

3333
post "/resetprefs":
3434
genResetPrefs()
35-
redirect($(parseUri("/settings") ? filterParams(request.params)))
35+
redirect("/settings?referer=" & encodeUrl(refPath()))
3636

3737
post "/enablehls":
3838
savePref("hlsPlayback", "on", request)

0 commit comments

Comments
 (0)