From 0633ec2c3935aae068bfa6867a0547bbe28e685d Mon Sep 17 00:00:00 2001 From: girst Date: Fri, 25 Feb 2022 19:26:24 +0100 Subject: [PATCH 1/2] Prefer mp4 to m3u8 for Video Playback if proxyVideos is off m3u8 videos only work when the proxy is enabled. Further, this allows video playback without Javascript. This is only done when proxying is disabled to avoid excessive memory usage on the nitter instance that would result from loading longer videos in a single chunk. --- src/views/tweet.nim | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/views/tweet.nim b/src/views/tweet.nim index 6e6f3af..73548a1 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -62,11 +62,14 @@ proc renderAlbum(tweet: Tweet): VNode = a(href=getPicUrl(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 @@ -87,6 +90,9 @@ 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 playbackType = + if not prefs.proxyVideos and video.hasMp4Url: mp4 + else: video.playbackType buildHtml(tdiv(class="attachments card")): tdiv(class="gallery-video" & container): @@ -95,13 +101,13 @@ 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 vid = video.variants.filterIt(it.contentType == playbackType) + let source = if prefs.proxyVideos: getVidUrl(vid[0].url) else: vid[0].url + case playbackType of mp4: if prefs.muteVideos: video(poster=thumb, controls="", muted=""): From e2b8e17f857f3a1bcfdc7942e58f3e4070ea8533 Mon Sep 17 00:00:00 2001 From: girst Date: Wed, 18 May 2022 19:47:03 +0200 Subject: [PATCH 2/2] use largest resolution mp4 video available --- src/parser.nim | 9 +++++++-- src/parserutils.nim | 15 +++++++++++++++ src/types.nim | 1 + src/views/tweet.nim | 9 ++++++--- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/parser.nim b/src/parser.nim index 1a7c073..fc8dc3b 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 + resolution = getMp4Resolution(url) # only available if contentType == mp4 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: resolution ) 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 73548a1..d07ca06 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 @@ -105,8 +105,11 @@ proc renderVideo*(video: Video; prefs: Prefs; path: string): VNode = img(src=thumb) renderVideoDisabled(video, path) else: - let vid = video.variants.filterIt(it.contentType == playbackType) - let source = if prefs.proxyVideos: getVidUrl(vid[0].url) else: vid[0].url + 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: