From 4d650fd33e422fb09a7ba9d9e484a8855131d840 Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 3 Jun 2020 02:33:34 +0200 Subject: [PATCH] Support more cards, even the undocumented ones --- src/parser.nim | 44 ++++++++++++++++++++++++++++++-------------- src/parserutils.nim | 7 +++++++ src/types.nim | 11 ++++------- src/views/tweet.nim | 2 +- 4 files changed, 42 insertions(+), 22 deletions(-) diff --git a/src/parser.nim b/src/parser.nim index 563b515..f431b73 100644 --- a/src/parser.nim +++ b/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"}) diff --git a/src/parserutils.nim b/src/parserutils.nim index a4fb8e6..93fbaf1 100644 --- a/src/parserutils.nim +++ b/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 diff --git a/src/types.nim b/src/types.nim index d5a8f95..c93e613 100644 --- a/src/types.nim +++ b/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" diff --git a/src/views/tweet.nim b/src/views/tweet.nim index 6edacb0..8de0bb5 100644 --- a/src/views/tweet.nim +++ b/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)