nitter/src/routes/media.nim
decoy-walrus 15e1ba1322
Hack in support for the new twitter video format (#381)
This change adds ".m4s" and ".mp4" to the regex that modifies m3u8 playlist files proxied from twitter, and adds ".m4s" to the list of extensions proxied through nitter. The net effect is the new video format that twitter is using now should be supported.
2021-05-08 18:05:31 +02:00

128 lines
3.3 KiB
Nim

import uri, strutils, httpclient, os, hashes, base64, re
import asynchttpserver, asyncstreams, asyncfile, asyncnet
import jester
import router_utils
import ".."/[types, formatters, agents, utils]
export asynchttpserver, asyncstreams, asyncfile, asyncnet
export httpclient, os, strutils, asyncstreams, base64, re
const
m3u8Mime* = "application/vnd.apple.mpegurl"
maxAge* = "max-age=604800"
let mediaAgent* = getAgent()
proc safeFetch*(url, agent: string): Future[string] {.async.} =
let client = newAsyncHttpClient(userAgent=agent)
try: result = await client.getContent(url)
except: discard
finally: client.close()
template respond*(req: asynchttpserver.Request; headers) =
var msg = "HTTP/1.1 200 OK\c\L"
for k, v in headers:
msg.add(k & ": " & v & "\c\L")
msg.add "\c\L"
yield req.client.send(msg)
proc proxyMedia*(req: jester.Request; url: string): Future[HttpCode] {.async.} =
result = Http200
let
request = req.getNativeReq()
client = newAsyncHttpClient(userAgent=mediaAgent)
try:
let res = await client.get(url)
if res.status != "200 OK":
return Http404
let hashed = $hash(url)
if request.headers.getOrDefault("If-None-Match") == hashed:
return Http304
let contentLength =
if res.headers.hasKey("content-length"):
res.headers["content-length", 0]
else:
""
let headers = newHttpHeaders({
"Content-Type": res.headers["content-type", 0],
"Content-Length": contentLength,
"Cache-Control": maxAge,
"ETag": hashed
})
respond(request, headers)
var (hasValue, data) = (true, "")
while hasValue:
(hasValue, data) = await res.bodyStream.read()
if hasValue:
await request.client.send(data)
data.setLen 0
except HttpRequestError, ProtocolError, OSError:
result = Http404
finally:
client.close()
template check*(code): untyped =
if code != Http200:
resp code
else:
enableRawMode()
break route
proc decoded*(req: jester.Request; index: int): string =
let
based = req.matches[0].len > 1
encoded = req.matches[index]
if based: decode(encoded)
else: decodeUrl(encoded)
proc createMediaRouter*(cfg: Config) =
router media:
get "/pic/?":
resp Http404
get re"^\/pic\/(enc)?\/?(.+)":
var url = decoded(request, 1)
if "twimg.com" notin url:
url.insert(twimg)
if not url.startsWith(https):
url.insert(https)
let uri = parseUri(url)
cond isTwitterUrl(uri) == true
let code = await proxyMedia(request, url)
check code
get re"^\/video\/(enc)?\/?(.+)\/(.+)$":
let url = decoded(request, 2)
cond "http" in url
if getHmac(url) != request.matches[1]:
resp showError("Failed to verify signature", cfg)
if ".mp4" in url or ".ts" in url or ".m4s" in url:
let code = await proxyMedia(request, url)
check code
var content: string
if ".vmap" in url:
let m3u8 = getM3u8Url(await safeFetch(url, mediaAgent))
if m3u8.len > 0:
content = await safeFetch(url, mediaAgent)
else:
resp Http404
if ".m3u8" in url:
let vid = await safeFetch(url, mediaAgent)
content = proxifyVideo(vid, cookiePref(proxyVideos))
resp content, m3u8Mime