Cache profiles
This commit is contained in:
parent
63d7528b8f
commit
6103db6893
8 changed files with 78 additions and 100 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
|||
nitter
|
||||
*.html
|
||||
*.html
|
||||
*.db
|
|
@ -1,74 +1,30 @@
|
|||
import sharedtables, times, hashes
|
||||
import asyncdispatch, times
|
||||
import types, api
|
||||
|
||||
# var
|
||||
# profileCache: SharedTable[int, Profile]
|
||||
# profileCacheTime = initDuration(seconds=10)
|
||||
withDb:
|
||||
try:
|
||||
createTables()
|
||||
except DbError:
|
||||
discard
|
||||
|
||||
# profileCache.init()
|
||||
var profileCacheTime = initDuration(seconds=60)
|
||||
|
||||
proc getCachedProfile*(username: string; force=false): Profile =
|
||||
return getProfile(username)
|
||||
# let index = username.hash
|
||||
|
||||
# try:
|
||||
# result = profileCache.mget(index)
|
||||
# # if force or getTime() - result.lastUpdated > profileCacheTime:
|
||||
# # result = getProfile(username)
|
||||
# # profileCache[username.hash] = deepCopy(result)
|
||||
# # return
|
||||
# except KeyError:
|
||||
# # result = getProfile(username)
|
||||
# # profileCache.add(username.hash, deepCopy(result))
|
||||
|
||||
|
||||
|
||||
# var profile: Profile
|
||||
# profileCache.withKey(index) do (k: int, v: var Profile, pairExists: var bool):
|
||||
# v = getProfile(username)
|
||||
# profile = v
|
||||
# echo v
|
||||
# pairExists = true
|
||||
# echo profile.username
|
||||
# return profile
|
||||
|
||||
# profileCache.withValue(hash(username), value) do:
|
||||
# if getTime() - value.lastUpdated > profileCacheTime or force:
|
||||
# result = getProfile(username)
|
||||
# value = result
|
||||
# else:
|
||||
# result = value
|
||||
# do:
|
||||
# result = getProfile(username)
|
||||
# value = result
|
||||
|
||||
# var profile: Profile
|
||||
|
||||
# profileCache.withKey(username.hash) do (k: int, v: var Profile, pairExists: var bool):
|
||||
# if pairExists and getTime() - v.lastUpdated < profileCacheTime and not force:
|
||||
# profile = deepCopy(v)
|
||||
# echo "cached"
|
||||
# else:
|
||||
# profile = getProfile(username)
|
||||
# v = deepCopy(profile)
|
||||
# pairExists = true
|
||||
# echo "fetched"
|
||||
|
||||
# return profile
|
||||
|
||||
# try:
|
||||
# result = profileCache.mget(username.hash)
|
||||
# if force or getTime() - result.lastUpdated > profileCacheTime:
|
||||
# result = getProfile(username)
|
||||
# profileCache[username.hash] = deepCopy(result)
|
||||
# return
|
||||
# except KeyError:
|
||||
# result = getProfile(username)
|
||||
# profileCache.add(username.hash, deepCopy(result))
|
||||
|
||||
# if not result.isNil or force or
|
||||
# getTime() - result.lastUpdated > profileCacheTime:
|
||||
# result = getProfile(username)
|
||||
# profileCache[username] = result
|
||||
# return
|
||||
proc outdated(profile: Profile): bool =
|
||||
getTime() - profile.updated > profileCacheTime
|
||||
|
||||
proc getCachedProfile*(username: string; force=false): Future[Profile] {.async.} =
|
||||
withDb:
|
||||
try:
|
||||
result.getOne("username = ?", username)
|
||||
doAssert(not result.outdated())
|
||||
except:
|
||||
if result.id == 0:
|
||||
result = await getProfile(username)
|
||||
result.insert()
|
||||
elif result.outdated():
|
||||
let
|
||||
profile = await getProfile(username)
|
||||
oldId = result.id
|
||||
result = profile
|
||||
result.id = oldId
|
||||
result.update()
|
||||
|
|
|
@ -64,10 +64,10 @@ proc getUserpic*(profile: Profile; style=""): string =
|
|||
getUserPic(profile.userpic, style)
|
||||
|
||||
proc getGifSrc*(tweet: Tweet): string =
|
||||
fmt"https://video.twimg.com/tweet_video/{tweet.gif}.mp4"
|
||||
fmt"https://video.twimg.com/tweet_video/{tweet.gif.get()}.mp4"
|
||||
|
||||
proc getGifThumb*(tweet: Tweet): string =
|
||||
fmt"https://pbs.twimg.com/tweet_video_thumb/{tweet.gif}.jpg"
|
||||
fmt"https://pbs.twimg.com/tweet_video_thumb/{tweet.gif.get()}.jpg"
|
||||
|
||||
proc formatName*(profile: Profile): string =
|
||||
result = xmltree.escape(profile.fullname)
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import asyncdispatch, httpclient, times, strutils, hashes, random, uri
|
||||
import jester, regex
|
||||
|
||||
import api, utils, types
|
||||
import api, utils, types, cache
|
||||
import views/[user, general, conversation]
|
||||
|
||||
proc showTimeline(name: string; num=""): Future[string] {.async.} =
|
||||
let
|
||||
username = name.strip(chars={'/'})
|
||||
profileFut = getProfile(username)
|
||||
profileFut = getCachedProfile(username)
|
||||
tweetsFut = getTimeline(username, after=num)
|
||||
|
||||
let profile = await profileFut
|
||||
|
|
|
@ -47,7 +47,6 @@ proc parseTweet*(tweet: XmlNode): Tweet =
|
|||
result.id = tweet.getAttr("data-item-id")
|
||||
result.link = tweet.getAttr("data-permalink-path")
|
||||
result.text = tweet.selectText(".tweet-text").stripTwitterUrls()
|
||||
result.retweetBy = tweet.selectText(".js-retweet-text > a > b")
|
||||
result.pinned = "pinned" in tweet.getAttr("class")
|
||||
result.profile = parseTweetProfile(tweet)
|
||||
|
||||
|
@ -70,6 +69,10 @@ proc parseTweet*(tweet: XmlNode): Tweet =
|
|||
of "retweets": result.retweets = num
|
||||
else: discard
|
||||
|
||||
let by = tweet.selectText(".js-retweet-text > a > b")
|
||||
if by.len > 0:
|
||||
result.retweetBy = some(by)
|
||||
|
||||
for photo in tweet.querySelectorAll(".AdaptiveMedia-photoContainer"):
|
||||
result.photos.add photo.attrs["data-image-url"]
|
||||
|
||||
|
@ -77,9 +80,9 @@ proc parseTweet*(tweet: XmlNode): Tweet =
|
|||
if player.len > 0:
|
||||
let thumb = player.replace(re".+:url\('([^']+)'\)", "$1")
|
||||
if "tweet_video" in thumb:
|
||||
result.gif = thumb.replace(re".+thumb/([^\.']+)\.jpg.+", "$1")
|
||||
result.gif = some(thumb.replace(re".+thumb/([^\.']+)\.jpg.+", "$1"))
|
||||
else:
|
||||
result.videoThumb = thumb
|
||||
result.videoThumb = some(thumb)
|
||||
|
||||
proc parseTweets*(node: XmlNode): Tweets =
|
||||
if node.isNil: return
|
||||
|
|
|
@ -1,18 +1,36 @@
|
|||
import times, sequtils
|
||||
import times, sequtils, strutils, options
|
||||
import norm/sqlite
|
||||
|
||||
export sqlite, options
|
||||
|
||||
db("cache.db", "", "", ""):
|
||||
type
|
||||
Profile* = object
|
||||
username*: string
|
||||
fullname*: string
|
||||
description*: string
|
||||
userpic*: string
|
||||
banner*: string
|
||||
following*: string
|
||||
followers*: string
|
||||
tweets*: string
|
||||
verified* {.
|
||||
dbType: "STRING",
|
||||
parseIt: parseBool(it.s)
|
||||
formatIt: $it
|
||||
.}: bool
|
||||
protected* {.
|
||||
dbType: "STRING",
|
||||
parseIt: parseBool(it.s)
|
||||
formatIt: $it
|
||||
.}: bool
|
||||
updated* {.
|
||||
dbType: "INTEGER",
|
||||
parseIt: it.i.fromUnix(),
|
||||
formatIt: getTime().toUnix()
|
||||
.}: Time
|
||||
|
||||
type
|
||||
Profile* = object
|
||||
username*: string
|
||||
fullname*: string
|
||||
description*: string
|
||||
userpic*: string
|
||||
banner*: string
|
||||
following*: string
|
||||
followers*: string
|
||||
tweets*: string
|
||||
verified*: bool
|
||||
protected*: bool
|
||||
|
||||
Tweet* = object
|
||||
id*: string
|
||||
profile*: Profile
|
||||
|
@ -23,12 +41,12 @@ type
|
|||
replies*: string
|
||||
retweets*: string
|
||||
likes*: string
|
||||
retweetBy*: string
|
||||
pinned*: bool
|
||||
photos*: seq[string]
|
||||
gif*: string
|
||||
video*: string
|
||||
videoThumb*: string
|
||||
retweetBy*: Option[string]
|
||||
gif*: Option[string]
|
||||
video*: Option[string]
|
||||
videoThumb*: Option[string]
|
||||
|
||||
Tweets* = seq[Tweet]
|
||||
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
#import ../types, ../formatters, ../utils
|
||||
#
|
||||
#proc renderHeading(tweet: Tweet): string =
|
||||
#if tweet.retweetBy != "":
|
||||
#if tweet.retweetBy.isSome:
|
||||
<div class="retweet">
|
||||
<span>🔄 ${tweet.retweetBy} retweeted</span>
|
||||
<span>🔄 ${tweet.retweetBy.get()} retweeted</span>
|
||||
</div>
|
||||
#end if
|
||||
#if tweet.pinned:
|
||||
|
@ -59,7 +59,7 @@
|
|||
<div class="attachments media-body">
|
||||
<div class="gallery-row" style="max-height: unset;">
|
||||
<div class="attachment image">
|
||||
<video poster=${tweet.videoThumb} style="width: 100%; height: 100%;" autoplay muted loop></video>
|
||||
<video poster=${tweet.videoThumb.get()} style="width: 100%; height: 100%;" autoplay muted loop></video>
|
||||
<div class="video-overlay">
|
||||
<p>Video playback not supported</p>
|
||||
</div>
|
||||
|
@ -102,9 +102,9 @@
|
|||
</div>
|
||||
#if tweet.photos.len > 0:
|
||||
${renderMediaGroup(tweet)}
|
||||
#elif tweet.videoThumb.len > 0:
|
||||
#elif tweet.videoThumb.isSome:
|
||||
${renderVideo(tweet)}
|
||||
#elif tweet.gif.len > 0:
|
||||
#elif tweet.gif.isSome:
|
||||
${renderGif(tweet)}
|
||||
#end if
|
||||
${renderStats(tweet)}
|
||||
|
|
|
@ -73,7 +73,7 @@
|
|||
#for tweet in tweets:
|
||||
#if tweet in retweets: continue
|
||||
#end if
|
||||
#if tweet.retweetBy.len > 0: retweets.add tweet
|
||||
#if tweet.retweetBy.isSome: retweets.add tweet
|
||||
#end if
|
||||
${renderTweet(tweet, "timeline-tweet")}
|
||||
#end for
|
||||
|
|
Loading…
Reference in a new issue