media.nim 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import httpclient, asyncdispatch, times, sequtils, strutils, json, uri
  2. import ".."/[types, parser, formatters]
  3. import utils, consts
  4. var
  5. guestToken = ""
  6. tokenUses = 0
  7. tokenMaxUses = 230
  8. tokenUpdated: Time
  9. tokenLifetime = initDuration(minutes=20)
  10. macro genMediaGet(media: untyped; token=false) =
  11. let
  12. mediaName = capitalizeAscii($media)
  13. multi = ident("get" & mediaName & "s")
  14. convo = ident("getConversation" & mediaName & "s")
  15. single = ident("get" & mediaName)
  16. quote do:
  17. proc `multi`*(thread: Thread | Timeline; agent: string; token="") {.async.} =
  18. if thread == nil: return
  19. var `media` = thread.content.filterIt(it.`media`.isSome)
  20. when `token`:
  21. var gToken = token
  22. if gToken.len == 0: gToken = await getGuestToken(agent)
  23. await all(`media`.mapIt(`single`(it, token, agent)))
  24. else:
  25. await all(`media`.mapIt(`single`(it, agent)))
  26. proc `convo`*(convo: Conversation; agent: string) {.async.} =
  27. var futs: seq[Future[void]]
  28. when `token`:
  29. var token = await getGuestToken(agent)
  30. futs.add `single`(convo.tweet, agent, token)
  31. futs.add `multi`(convo.before, agent, token=token)
  32. futs.add `multi`(convo.after, agent, token=token)
  33. futs.add convo.replies.content.mapIt(`multi`(it, agent, token=token))
  34. else:
  35. futs.add `single`(convo.tweet, agent)
  36. futs.add `multi`(convo.before, agent)
  37. futs.add `multi`(convo.after, agent)
  38. futs.add convo.replies.content.mapIt(`multi`(it, agent))
  39. await all(futs)
  40. proc getGuestToken(agent: string; force=false): Future[string] {.async.} =
  41. if getTime() - tokenUpdated < tokenLifetime and
  42. not force and tokenUses < tokenMaxUses:
  43. return guestToken
  44. tokenUpdated = getTime()
  45. tokenUses = 0
  46. let
  47. headers = genHeaders({"authorization": auth}, agent, base, lang=false)
  48. json = await fetchJson(apiBase / tokenUrl, headers)
  49. if json != nil:
  50. result = json["guest_token"].to(string)
  51. guestToken = result
  52. proc getVideoFetch(tweet: Tweet; agent, token: string) {.async.} =
  53. if tweet.video.isNone(): return
  54. let
  55. headers = genHeaders({"authorization": auth, "x-guest-token": token},
  56. agent, base / getLink(tweet), lang=false)
  57. url = apiBase / (videoUrl % tweet.id)
  58. json = await fetchJson(url, headers)
  59. if json == nil:
  60. if getTime() - tokenUpdated > initDuration(seconds=1):
  61. tokenUpdated = getTime()
  62. discard await getGuestToken(agent, force=true)
  63. await getVideoFetch(tweet, agent, guestToken)
  64. return
  65. if tweet.card.isNone:
  66. tweet.video = some parseVideo(json, tweet.id)
  67. else:
  68. get(tweet.card).video = some parseVideo(json, tweet.id)
  69. tweet.video = none Video
  70. tokenUses.inc
  71. proc getVideoVar(tweet: Tweet): var Option[Video] =
  72. if tweet.card.isSome():
  73. return get(tweet.card).video
  74. else:
  75. return tweet.video
  76. proc getVideo*(tweet: Tweet; agent, token: string; force=false) {.async.} =
  77. withCustomDb("cache.db", "", "", ""):
  78. try:
  79. getVideoVar(tweet) = some Video.getOne("videoId = ?", tweet.id)
  80. except KeyError:
  81. await getVideoFetch(tweet, agent, token)
  82. var video = getVideoVar(tweet)
  83. if video.isSome():
  84. get(video).insert()
  85. proc getPoll*(tweet: Tweet; agent: string) {.async.} =
  86. if tweet.poll.isNone(): return
  87. let
  88. headers = genHeaders(agent, base / getLink(tweet), auth=true)
  89. url = base / (pollUrl % tweet.id)
  90. html = await fetchHtml(url, headers)
  91. if html == nil: return
  92. tweet.poll = some parsePoll(html)
  93. proc getCard*(tweet: Tweet; agent: string) {.async.} =
  94. if tweet.card.isNone(): return
  95. let
  96. headers = genHeaders(agent, base / getLink(tweet), auth=true)
  97. query = get(tweet.card).query.replace("sensitive=true", "sensitive=false")
  98. html = await fetchHtml(base / query, headers)
  99. if html == nil: return
  100. parseCard(get(tweet.card), html)
  101. proc getPhotoRail*(username, agent: string): Future[seq[GalleryPhoto]] {.async.} =
  102. let
  103. headers = genHeaders({"x-requested-with": "XMLHttpRequest"}, agent, base / username)
  104. params = {"for_photo_rail": "true", "oldest_unread_id": "0"}
  105. url = base / (timelineMediaUrl % username) ? params
  106. html = await fetchHtml(url, headers, jsonKey="items_html")
  107. result = parsePhotoRail(html)
  108. genMediaGet(video, token=true)
  109. genMediaGet(poll)
  110. genMediaGet(card)