diff --git a/public/style.css b/public/style.css index e97cfc8..f8fc63d 100644 --- a/public/style.css +++ b/public/style.css @@ -498,6 +498,7 @@ nav { .thread-last .status-el::before { background: unset; + min-width: unset; width: 0; margin: 0; } diff --git a/src/api.nim b/src/api.nim index 208484b..2982ddf 100644 --- a/src/api.nim +++ b/src/api.nim @@ -96,7 +96,7 @@ proc getVideo*(tweet: Tweet; token: string) {.async.} = url = apiBase / (videoUrl % tweet.id) json = await fetchJson(url, headers) - if json.isNil: + if json == nil: if getTime() - tokenUpdated > initDuration(seconds=1): tokenUpdated = getTime() guestToken = await getGuestToken(force=true) @@ -134,8 +134,7 @@ proc getProfileFallback(username: string; headers: HttpHeaders): Future[Profile] url = base / profileIntentUrl ? {"screen_name": username} html = await fetchHtml(url, headers) - if html.isNil: - return Profile() + if html == nil: return Profile() result = parseIntentProfile(html) @@ -158,10 +157,9 @@ proc getProfile*(username: string): Future[Profile] {.async.} = url = base / profilePopupUrl ? params html = await fetchHtml(url, headers, jsonKey="html") - if html.isNil: - return Profile() + if html == nil: return Profile() - if not html.select(".ProfileCard-sensitiveWarningContainer").isNil: + if html.select(".ProfileCard-sensitiveWarningContainer") != nil: return await getProfileFallback(username, headers) result = parsePopupProfile(html) @@ -182,7 +180,7 @@ proc getTimeline*(username: string; after=""): Future[Timeline] {.async.} = url &= "&max_position=" & cleanAfter let json = await fetchJson(base / url, headers) - if json.isNil: return Timeline() + if json == nil: return Timeline() result = Timeline( hasMore: json["has_more_items"].to(bool), @@ -213,8 +211,7 @@ proc getTweet*(username: string; id: string): Future[Conversation] {.async.} = url = base / username / tweetUrl / id html = await fetchHtml(url, headers) - if html.isNil: - return + if html == nil: return result = parseConversation(html) await getConversationVideos(result) diff --git a/src/nitter.nim b/src/nitter.nim index ced2114..9f10a00 100644 --- a/src/nitter.nim +++ b/src/nitter.nim @@ -44,7 +44,7 @@ routes: cond '.' notin @"name" let conversation = await getTweet(@"name", @"id") - if conversation.isNil or conversation.tweet.id.len == 0: + if conversation == nil or conversation.tweet.id.len == 0: resp Http404, showError("Tweet not found") let title = pageTitle(conversation.tweet.profile) diff --git a/src/parser.nim b/src/parser.nim index 1c12bec..8a2b308 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -4,7 +4,7 @@ import ./types, ./parserutils, ./formatters proc parsePopupProfile*(node: XmlNode): Profile = let profile = node.select(".profile-card") - if profile.isNil: return + if profile == nil: return result = Profile( fullname: profile.getName(".fullname"), @@ -24,8 +24,8 @@ proc parseIntentProfile*(profile: XmlNode): Profile = username: profile.getUsername(".nickname"), bio: profile.getBio("p.note"), userpic: profile.select(".profile.summary").getAvatar("img.photo"), - verified: not profile.select("li.verified").isNil, - protected: not profile.select("li.protected").isNil, + verified: profile.select("li.verified") != nil, + protected: profile.select("li.protected") != nil, banner: getBanner(profile) ) @@ -33,22 +33,22 @@ proc parseIntentProfile*(profile: XmlNode): Profile = proc parseTweetProfile*(profile: XmlNode): Profile = result = Profile( - fullname: profile.getAttr("data-name").stripText(), - username: profile.getAttr("data-screen-name"), + fullname: profile.attr("data-name").stripText(), + username: profile.attr("data-screen-name"), userpic: profile.getAvatar(".avatar"), verified: isVerified(profile) ) proc parseQuote*(quote: XmlNode): Quote = result = Quote( - id: quote.getAttr("data-item-id"), - link: quote.getAttr("href"), + id: quote.attr("data-item-id"), + link: quote.attr("href"), text: getQuoteText(quote) ) result.profile = Profile( fullname: quote.selectText(".QuoteTweet-fullname").stripText(), - username: quote.getAttr("data-screen-name"), + username: quote.attr("data-screen-name"), verified: isVerified(quote) ) @@ -56,17 +56,17 @@ proc parseQuote*(quote: XmlNode): Quote = proc parseTweet*(node: XmlNode): Tweet = let tweet = node.select(".tweet") - if tweet.isNil(): - return Tweet() + if tweet == nil: return Tweet() result = Tweet( - id: tweet.getAttr("data-item-id"), - link: tweet.getAttr("data-permalink-path"), - profile: parseTweetProfile(tweet), + id: tweet.attr("data-item-id"), + link: tweet.attr("data-permalink-path"), text: getTweetText(tweet), time: getTimestamp(tweet), shortTime: getShortTime(tweet), - pinned: "pinned" in tweet.getAttr("class") + profile: parseTweetProfile(tweet), + pinned: "pinned" in tweet.attr("class"), + available: true ) result.getTweetStats(tweet) @@ -75,14 +75,14 @@ proc parseTweet*(node: XmlNode): Tweet = let by = tweet.selectText(".js-retweet-text > a > b") if by.len > 0: result.retweetBy = some(by.stripText()) - result.retweetId = some(tweet.getAttr("data-retweet-id")) + result.retweetId = some(tweet.attr("data-retweet-id")) let quote = tweet.select(".QuoteTweet-innerContainer") - if not quote.isNil: + if quote != nil: result.quote = some(parseQuote(quote)) proc parseTweets*(node: XmlNode): Tweets = - if node.isNil or node.kind == xnText: return + if node == nil or node.kind == xnText: return node.selectAll(".stream-item").map(parseTweet) proc parseConversation*(node: XmlNode): Conversation = @@ -92,7 +92,7 @@ proc parseConversation*(node: XmlNode): Conversation = ) let replies = node.select(".replies-to", ".stream-items") - if replies.isNil: return + if replies == nil: return for reply in replies.filterIt(it.kind != xnText): if "selfThread" in reply.attr("class"): diff --git a/src/parserutils.nim b/src/parserutils.nim index f7a946c..a32ad49 100644 --- a/src/parserutils.nim +++ b/src/parserutils.nim @@ -10,24 +10,22 @@ const gifRegex = re".+thumb/([^\.']+)\.jpg.*" proc selectAll*(node: XmlNode; selector: string): seq[XmlNode] = + if node == nil: return q.select(node, selector) proc select*(node: XmlNode; selector: string): XmlNode = + if node == nil: return let nodes = node.selectAll(selector) if nodes.len > 0: nodes[0] else: nil proc select*(node: XmlNode; parent, child: string): XmlNode = let parentNode = node.select(parent) - if parentNode.isNil(): return + if parentNode == nil: return result = parentNode.select(child) -proc getAttr*(node: XmlNode; attr: string; default=""): string = - if node.isNil or node.attrs.isNil: return default - return node.attrs.getOrDefault(attr) - -proc selectAttr*(node: XmlNode; selector: string; attr: string; default=""): string = +proc selectAttr*(node: XmlNode; selector: string; attr: string): string = let res = node.select(selector) - if res == nil: "" else: res.getAttr(attr, default) + if res == nil: "" else: res.attr(attr) proc selectText*(node: XmlNode; selector: string): string = let res = node.select(selector) @@ -35,9 +33,9 @@ proc selectText*(node: XmlNode; selector: string): string = proc getHeader(profile: XmlNode): XmlNode = result = profile.select(".permalink-header") - if result.isNil: + if result == nil: result = profile.select(".stream-item-header") - if result.isNil: + if result == nil: result = profile.select(".ProfileCard-userFields") proc isVerified*(profile: XmlNode): bool = @@ -54,7 +52,7 @@ proc getUsername*(profile: XmlNode; selector: string): string = proc emojify*(node: XmlNode) = for i in node.selectAll(".Emoji"): - i.add newText(i.getAttr("alt")) + i.add newText(i.attr("alt")) proc getQuoteText*(tweet: XmlNode): string = let text = tweet.select(".QuoteTweet-text") @@ -71,7 +69,7 @@ proc getTweetText*(tweet: XmlNode): string = emojify(text) result = stripText(text.innerText()) - if not quote.isNil and link.len > 0: + if quote != nil and link.len > 0: result = result.replace(link, "") result = stripTwitterUrls(result) @@ -80,8 +78,8 @@ proc getTime(tweet: XmlNode): XmlNode = tweet.select(".js-short-timestamp") proc getTimestamp*(tweet: XmlNode): Time = - let time = getTime(tweet).getAttr("data-time", "0") - fromUnix(parseInt(time)) + let time = getTime(tweet).attr("data-time") + fromUnix(if time.len > 0: parseInt(time) else: 0) proc getShortTime*(tweet: XmlNode): string = getTime(tweet).innerText() @@ -105,8 +103,8 @@ proc getBanner*(tweet: XmlNode): string = proc getPopupStats*(profile: var Profile; node: XmlNode) = for s in node.selectAll( ".ProfileCardStats-statLink"): - let text = s.getAttr("title").split(" ")[0] - case s.getAttr("href").split("/")[^1] + let text = s.attr("title").split(" ")[0] + case s.attr("href").split("/")[^1] of "followers": profile.followers = text of "following": profile.following = text else: profile.tweets = text @@ -115,7 +113,7 @@ proc getIntentStats*(profile: var Profile; node: XmlNode) = profile.tweets = "?" for s in node.selectAll( "dd.count > a"): let text = s.innerText() - case s.getAttr("href").split("/")[^1] + case s.attr("href").split("/")[^1] of "followers": profile.followers = text of "following": profile.following = text @@ -132,7 +130,7 @@ proc getTweetStats*(tweet: Tweet; node: XmlNode) = proc getGif(player: XmlNode): Gif = let - thumb = player.getAttr("style").replace(thumbRegex, "$1") + thumb = player.attr("style").replace(thumbRegex, "$1") id = thumb.replace(gifRegex, "$1") url = fmt"https://video.twimg.com/tweet_video/{id}.mp4" Gif(url: url, thumb: thumb) @@ -142,28 +140,28 @@ proc getTweetMedia*(tweet: Tweet; node: XmlNode) = tweet.photos.add photo.attrs["data-image-url"] let player = node.select(".PlayableMedia") - if player.isNil: + if player == nil: return - if "gif" in player.getAttr("class"): + if "gif" in player.attr("class"): tweet.gif = some(getGif(player.select(".PlayableMedia-player"))) - elif "video" in player.getAttr("class"): + elif "video" in player.attr("class"): tweet.video = some(Video()) proc getQuoteMedia*(quote: var Quote; node: XmlNode) = let sensitive = node.select(".QuoteTweet--sensitive") - if not sensitive.isNil: + if sensitive != nil: quote.sensitive = true return let media = node.select(".QuoteMedia") - if not media.isNil: + if media != nil: quote.thumb = some(media.selectAttr("img", "src")) let badge = node.select(".AdaptiveMedia-badgeText") let gifBadge = node.select(".Icon--gifBadge") - if not badge.isNil: + if badge != nil: quote.badge = some(badge.innerText()) - elif not gifBadge.isNil: + elif gifBadge != nil: quote.badge = some("GIF")