diff --git a/src/parser.nim b/src/parser.nim index 1a7c073..b731bdb 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -87,10 +87,15 @@ proc parseVideo(js: JsonNode): Video = result.description = description.getStr for v in js{"video_info", "variants"}: + let + contentType = parseEnum[VideoType](v{"content_type"}.getStr("summary")) + url = v{"url"}.getStr + result.variants.add VideoVariant( - contentType: parseEnum[VideoType](v{"content_type"}.getStr("summary")), + contentType: contentType, bitrate: v{"bitrate"}.getInt, - url: v{"url"}.getStr + url: url, + resolution: if contentType == mp4: getMp4Resolution(url) else: 0 ) proc parsePromoVideo(js: JsonNode): Video = diff --git a/src/parserutils.nim b/src/parserutils.nim index 4929c2a..51ccea5 100644 --- a/src/parserutils.nim +++ b/src/parserutils.nim @@ -137,6 +137,21 @@ proc getSource*(js: JsonNode): string = let src = js{"source"}.getStr result = src.substr(src.find('>') + 1, src.rfind('<') - 1) +proc getMp4Resolution*(url: string): int = + # parses the height out of a URL like this one: + # https://video.twimg.com/ext_tw_video//pu/vid/720x1280/.mp4 + const vidSep = "/vid/" + let + vidIdx = url.find(vidSep) + vidSep.len + resIdx = url.find('x', vidIdx) + 1 + res = url[resIdx ..< url.find("/", resIdx)] + + try: + return parseInt(res) + except ValueError: + # cannot determine resolution (e.g. m3u8/non-mp4 video) + return 0 + proc extractSlice(js: JsonNode): Slice[int] = result = js["indices"][0].getInt ..< js["indices"][1].getInt diff --git a/src/types.nim b/src/types.nim index 98433aa..061ec8a 100644 --- a/src/types.nim +++ b/src/types.nim @@ -75,6 +75,7 @@ type contentType*: VideoType url*: string bitrate*: int + resolution*: int Video* = object durationMs*: int diff --git a/src/views/tweet.nim b/src/views/tweet.nim index 042e52a..fe96c8e 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -import strutils, sequtils, strformat, options +import strutils, sequtils, strformat, options, algorithm import karax/[karaxdsl, vdom, vstyles] from jester import Request @@ -62,11 +62,14 @@ proc renderAlbum(tweet: Tweet): VNode = a(href=getOrigPicUrl(orig), class="still-image", target="_blank"): genImg(small) -proc isPlaybackEnabled(prefs: Prefs; video: Video): bool = - case video.playbackType +proc isPlaybackEnabled(prefs: Prefs; playbackType: VideoType): bool = + case playbackType of mp4: prefs.mp4Playback of m3u8, vmap: prefs.hlsPlayback +proc hasMp4Url(video: Video): bool = + video.variants.anyIt(it.contentType == mp4) + proc renderVideoDisabled(video: Video; path: string): VNode = buildHtml(tdiv(class="video-overlay")): case video.playbackType @@ -84,9 +87,11 @@ proc renderVideoUnavailable(video: Video): VNode = p: text "This media is unavailable" proc renderVideo*(video: Video; prefs: Prefs; path: string): VNode = - let container = - if video.description.len > 0 or video.title.len > 0: " card-container" - else: "" + let + container = if video.description.len == 0 and video.title.len == 0: "" + else: " card-container" + playbackType = if not prefs.proxyVideos and video.hasMp4Url: mp4 + else: video.playbackType buildHtml(tdiv(class="attachments card")): tdiv(class="gallery-video" & container): @@ -95,13 +100,16 @@ proc renderVideo*(video: Video; prefs: Prefs; path: string): VNode = if not video.available: img(src=thumb) renderVideoUnavailable(video) - elif not prefs.isPlaybackEnabled(video): + elif not prefs.isPlaybackEnabled(playbackType): img(src=thumb) renderVideoDisabled(video, path) else: - let vid = video.variants.filterIt(it.contentType == video.playbackType) - let source = getVidUrl(vid[0].url) - case video.playbackType + let + vars = video.variants.filterIt(it.contentType == playbackType) + vidUrl = vars.sortedByIt(it.resolution)[^1].url + source = if prefs.proxyVideos: getVidUrl(vidUrl) + else: vidUrl + case playbackType of mp4: if prefs.muteVideos: video(poster=thumb, controls="", muted=""):