Jelajahi Sumber

Support more cards, even the undocumented ones

Zed 6 tahun lalu
induk
melakukan
4d650fd33e
4 mengubah file dengan 42 tambahan dan 22 penghapusan
  1. 30 14
      src/parser.nim
  2. 7 0
      src/parserutils.nim
  3. 4 7
      src/types.nim
  4. 1 1
      src/views/tweet.nim

+ 30 - 14
src/parser.nim

@@ -1,6 +1,6 @@
 import strutils, options, tables, times, math
 import packedjson
-import types, parserutils
+import types, parserutils, utils
 
 proc parseProfile(js: JsonNode; id=""): Profile =
   if js.isNull: return
@@ -124,25 +124,28 @@ proc parseVideo(js: JsonNode): Video =
 
 proc parsePromoVideo(js: JsonNode): Video =
   result = Video(
-    videoId: js{"player_content_id"}.getStrVal(js{"card_id"}.getStrVal),
-    thumb: js{"player_image_large", "image_value", "url"}.getStr,
+    thumb: js{"player_image_large"}.getImageVal,
     available: true,
     durationMs: js{"content_duration_seconds"}.getStrVal("0").parseInt * 1000,
+    playbackType: vmap,
+    videoId: js{"player_content_id"}.getStrVal(js{"card_id"}.getStrVal(
+        js{"amplify_content_id"}.getStrVal())),
   )
 
   var variant = VideoVariant(
-    videoType: m3u8,
-    url: js{"player_hls_url"}.getStrVal(js{"player_stream_url"}.getStrVal)
+    videoType: vmap,
+    url: js{"player_hls_url"}.getStrVal(js{"player_stream_url"}.getStrVal(
+        js{"amplify_url_vmap"}.getStrVal()))
   )
 
-  if "vmap" in variant.url:
-    variant.videoType = vmap
+  if "m3u8" in variant.url:
+    variant.videoType = m3u8
+    result.playbackType = m3u8
 
-  result.playbackType = vmap
   result.variants.add variant
 
 proc parseBroadcast(js: JsonNode): Card =
-  let image = js{"broadcast_thumbnail_large", "image_value", "url"}.getStr
+  let image = js{"broadcast_thumbnail_large"}.getImageVal
   result = Card(
     kind: broadcast,
     url: js{"broadcast_url"}.getStrVal,
@@ -153,8 +156,9 @@ proc parseBroadcast(js: JsonNode): Card =
   )
 
 proc parseCard(js: JsonNode; urls: JsonNode): Card =
-  const imageTypes = ["photo_image_full_size", "summary_photo_image",
-                      "thumbnail_image", "promo_image", "player_image"]
+  const imageTypes = ["summary_photo_image", "player_image", "promo_image",
+                      "photo_image_full_size", "thumbnail_image", "thumbnail",
+                      "event_thumbnail"]
   let
     vals = ? js{"binding_values"}
     name = js{"name"}.getStr
@@ -172,19 +176,25 @@ proc parseCard(js: JsonNode; urls: JsonNode): Card =
     result.url = js{"url"}.getStr
 
   case kind
-  of promoVideo, promoVideoConvo:
+  of promoVideoConvo, appPlayer:
     result.video = some parsePromoVideo(vals)
+    if kind == appPlayer:
+      result.text = vals{"app_category"}.getStrVal(result.text)
   of broadcast:
     result = parseBroadcast(vals)
+  of liveEvent:
+    result.text = vals{"event_title"}.getStrVal
   of player:
     result.url = vals{"player_url"}.getStrVal
     if "youtube.com" in result.url:
       result.url = result.url.replace("/embed/", "/watch?v=")
+  of unified:
+    result.title = "This card type is not supported."
   else: discard
 
   for typ in imageTypes:
     with img, vals{typ & "_large"}:
-      result.image = img{"image_value", "url"}.getStr
+      result.image = img.getImageVal
       break
 
   for u in ? urls:
@@ -192,6 +202,10 @@ proc parseCard(js: JsonNode; urls: JsonNode): Card =
       result.url = u{"expanded_url"}.getStr
       break
 
+  if kind in {promoImageConvo, promoImageApp} and result.url.len == 0 or
+      result.url.startsWith("card://"):
+    result.url = getPicUrl(result.image)
+
 proc parseTweet(js: JsonNode): Tweet =
   if js.isNull: return
   result = Tweet(
@@ -223,9 +237,11 @@ proc parseTweet(js: JsonNode): Tweet =
     let name = jsCard{"name"}.getStr
     if "poll" in name:
       if "image" in name:
-        result.photos.add jsCard{"binding_values", "image_large", "image_value", "url"}.getStr
+        result.photos.add jsCard{"binding_values", "image_large"}.getImageVal
 
       result.poll = some parsePoll(jsCard)
+    elif name == "amplify":
+      result.video = some(parsePromoVideo(jsCard{"binding_values"}))
     else:
       result.card = some parseCard(jsCard, js{"entities", "urls"})
 

+ 7 - 0
src/parserutils.nim

@@ -58,10 +58,15 @@ proc getId*(js: JsonNode): int64 {.inline.} =
 template getStrVal*(js: JsonNode; default=""): string =
   js{"string_value"}.getStr(default)
 
+template getImageVal*(js: JsonNode; default=""): string =
+  js{"image_value", "url"}.getStr(default)
+
 proc getCardUrl*(js: JsonNode; kind: CardKind): string =
   result = js{"website_url"}.getStrVal
   if kind == promoVideoConvo:
     result = js{"thank_you_url"}.getStrVal(result)
+  if result.startsWith("card://"):
+    result = ""
 
 proc getCardDomain*(js: JsonNode; kind: CardKind): string =
   result = js{"vanity_url"}.getStrVal(js{"domain"}.getStr)
@@ -72,6 +77,8 @@ proc getCardTitle*(js: JsonNode; kind: CardKind): string =
   result = js{"title"}.getStrVal
   if kind == promoVideoConvo:
     result = js{"thank_you_text"}.getStrVal(result)
+  if kind == liveEvent:
+    result = js{"event_category"}.getStrVal
 
 proc getBanner*(js: JsonNode): string =
   let url = js{"profile_banner_url"}.getStr

+ 4 - 7
src/types.nim

@@ -101,24 +101,21 @@ type
     status*: string
 
   CardKind* = enum
+    amplify = "amplify"
     app = "app"
-    appplayer = "appplayer"
+    appPlayer = "appplayer"
     player = "player"
-    audio = "audio"
     summary = "summary"
     summaryLarge = "summary_large_image"
     promoWebsite = "promo_website"
-    promoVideo = "promo_video_website"
     promoVideoConvo = "promo_video_convo"
     promoImageConvo = "promo_image_convo"
-    promoVideoApp = "promo_video_app"
     promoImageApp = "promo_image_app"
-    amplify = "amplify"
-    unified = "unified_card"
+    storeLink = "direct_store_link_app"
     liveEvent = "live_event"
     broadcast = "broadcast"
     periscope = "periscope_broadcast"
-    storeLink = "direct_store_link_app"
+    unified = "unified_card"
     moment = "moment"
     messageMe = "message_me"
 

+ 1 - 1
src/views/tweet.nim

@@ -145,7 +145,7 @@ proc renderCardContent(card: Card): VNode =
     span(class="card-destination"): text card.dest
 
 proc renderCard(card: Card; prefs: Prefs; path: string): VNode =
-  const smallCards = {player, summary}
+  const smallCards = {app, player, summary, storeLink}
   let large = if card.kind notin smallCards: " large" else: ""
   let url = replaceUrl(card.url, prefs)