2020-06-09 15:04:38 +02:00
|
|
|
import uri, strutils, httpclient, os, hashes, base64, re
|
2020-06-06 04:39:22 +02:00
|
|
|
import asynchttpserver, asyncstreams, asyncfile, asyncnet
|
2019-09-06 02:42:35 +02:00
|
|
|
|
2020-06-09 15:04:38 +02:00
|
|
|
import jester
|
2019-09-06 02:42:35 +02:00
|
|
|
|
|
|
|
import router_utils
|
2020-06-07 07:55:57 +02:00
|
|
|
import ".."/[types, formatters, agents, utils]
|
2019-09-06 02:42:35 +02:00
|
|
|
|
2020-06-06 04:39:22 +02:00
|
|
|
export asynchttpserver, asyncstreams, asyncfile, asyncnet
|
2020-06-09 15:04:38 +02:00
|
|
|
export httpclient, os, strutils, asyncstreams, base64, re
|
2020-06-06 04:39:22 +02:00
|
|
|
|
|
|
|
const
|
|
|
|
m3u8Mime* = "application/vnd.apple.mpegurl"
|
|
|
|
maxAge* = "max-age=604800"
|
2019-09-06 02:42:35 +02:00
|
|
|
|
2020-06-02 20:27:43 +02:00
|
|
|
let mediaAgent* = getAgent()
|
2020-01-22 13:04:35 +01:00
|
|
|
|
2020-06-09 16:45:21 +02:00
|
|
|
proc safeFetch*(url, agent: string): Future[string] {.async.} =
|
|
|
|
let client = newAsyncHttpClient(userAgent=agent)
|
|
|
|
try: result = await client.getContent(url)
|
|
|
|
except: discard
|
|
|
|
finally: client.close()
|
|
|
|
|
2020-06-06 04:39:22 +02:00
|
|
|
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 headers = newHttpHeaders({
|
|
|
|
"Content-Type": res.headers["content-type", 0],
|
|
|
|
"Content-Length": res.headers["content-length", 0],
|
|
|
|
"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
|
2020-06-07 08:23:05 +02:00
|
|
|
except HttpRequestError, ProtocolError, OSError:
|
2020-06-06 04:39:22 +02:00
|
|
|
result = Http404
|
|
|
|
finally:
|
2020-06-09 16:45:21 +02:00
|
|
|
client.close()
|
2020-06-06 04:39:22 +02:00
|
|
|
|
2020-06-09 15:04:38 +02:00
|
|
|
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)
|
|
|
|
|
2019-09-06 02:42:35 +02:00
|
|
|
proc createMediaRouter*(cfg: Config) =
|
|
|
|
router media:
|
2020-05-26 14:24:41 +02:00
|
|
|
get "/pic/?":
|
|
|
|
resp Http404
|
|
|
|
|
2020-06-09 15:04:38 +02:00
|
|
|
get re"^\/pic\/(enc)?\/?(.+)":
|
|
|
|
var url = decoded(request, 1)
|
2020-06-07 07:55:57 +02:00
|
|
|
if "twimg.com" notin url:
|
|
|
|
url.insert(twimg)
|
|
|
|
if not url.startsWith(https):
|
|
|
|
url.insert(https)
|
|
|
|
|
|
|
|
let uri = parseUri(url)
|
|
|
|
cond isTwitterUrl(uri) == true
|
2019-09-13 12:27:04 +02:00
|
|
|
|
2020-06-09 15:04:38 +02:00
|
|
|
let code = await proxyMedia(request, url)
|
|
|
|
check code
|
2019-09-13 12:27:04 +02:00
|
|
|
|
2020-06-09 15:04:38 +02:00
|
|
|
get re"^\/video\/(enc)?\/?(.+)\/(.+)$":
|
|
|
|
let url = decoded(request, 2)
|
|
|
|
cond "http" in url
|
2019-09-06 02:42:35 +02:00
|
|
|
|
2020-06-09 15:04:38 +02:00
|
|
|
if getHmac(url) != request.matches[1]:
|
2019-10-21 05:19:00 +02:00
|
|
|
resp showError("Failed to verify signature", cfg)
|
2019-09-06 02:42:35 +02:00
|
|
|
|
2020-06-06 04:39:22 +02:00
|
|
|
if ".mp4" in url or ".ts" in url:
|
|
|
|
let code = await proxyMedia(request, url)
|
2020-06-09 15:04:38 +02:00
|
|
|
check code
|
2019-09-06 02:42:35 +02:00
|
|
|
|
2020-06-06 04:39:22 +02:00
|
|
|
var content: string
|
2019-09-06 02:42:35 +02:00
|
|
|
if ".vmap" in url:
|
2020-06-09 15:04:38 +02:00
|
|
|
let m3u8 = getM3u8Url(await safeFetch(url, mediaAgent))
|
|
|
|
if m3u8.len > 0:
|
2020-06-06 04:39:22 +02:00
|
|
|
content = await safeFetch(url, mediaAgent)
|
|
|
|
else:
|
|
|
|
resp Http404
|
2019-09-06 02:42:35 +02:00
|
|
|
|
|
|
|
if ".m3u8" in url:
|
2020-06-09 16:45:21 +02:00
|
|
|
let vid = await safeFetch(url, mediaAgent)
|
|
|
|
content = proxifyVideo(vid, cookiePref(proxyVideos))
|
2019-09-06 02:42:35 +02:00
|
|
|
|
2020-06-06 04:39:22 +02:00
|
|
|
resp content, m3u8Mime
|