|
|
@@ -1,4 +1,5 @@
|
|
|
-import asyncfile, uri, strutils, httpclient, os, mimetypes
|
|
|
+import uri, strutils, httpclient, os, hashes
|
|
|
+import asynchttpserver, asyncstreams, asyncfile, asyncnet
|
|
|
|
|
|
import jester, regex
|
|
|
|
|
|
@@ -6,12 +7,59 @@ import router_utils
|
|
|
import ".."/[types, formatters, agents]
|
|
|
import ../views/general
|
|
|
|
|
|
-export asyncfile, httpclient, os, strutils
|
|
|
-export regex
|
|
|
+export asynchttpserver, asyncstreams, asyncfile, asyncnet
|
|
|
+export httpclient, os, strutils, asyncstreams, regex
|
|
|
+
|
|
|
+const
|
|
|
+ m3u8Regex* = re"""url="(.+.m3u8)""""
|
|
|
+ m3u8Mime* = "application/vnd.apple.mpegurl"
|
|
|
+ maxAge* = "max-age=604800"
|
|
|
|
|
|
-const m3u8Regex* = re"""url="(.+.m3u8)""""
|
|
|
let mediaAgent* = getAgent()
|
|
|
|
|
|
+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
|
|
|
+ except HttpRequestError, OSError:
|
|
|
+ result = Http404
|
|
|
+ finally:
|
|
|
+ client.safeClose()
|
|
|
+
|
|
|
proc createMediaRouter*(cfg: Config) =
|
|
|
router media:
|
|
|
get "/pic/?":
|
|
|
@@ -24,48 +72,13 @@ proc createMediaRouter*(cfg: Config) =
|
|
|
let uri = parseUri(decodeUrl(@"url"))
|
|
|
cond isTwitterUrl($uri) == true
|
|
|
|
|
|
- let path = uri.path.split("/")[2 .. ^1].join("/")
|
|
|
- let filename = cfg.cacheDir / cleanFilename(path & uri.query)
|
|
|
-
|
|
|
- if path.len == 0:
|
|
|
- resp Http404
|
|
|
-
|
|
|
- if not existsDir(cfg.cacheDir):
|
|
|
- createDir(cfg.cacheDir)
|
|
|
-
|
|
|
- if not existsFile(filename):
|
|
|
- let client = newAsyncHttpClient(userAgent=mediaAgent)
|
|
|
- var failed = false
|
|
|
-
|
|
|
- try:
|
|
|
- await client.downloadFile($uri, filename)
|
|
|
- except HttpRequestError:
|
|
|
- failed = true
|
|
|
- removeFile(filename)
|
|
|
- except OSError as e:
|
|
|
- echo "Disk full, or network error: ", e.msg
|
|
|
- failed = true
|
|
|
- finally:
|
|
|
- client.safeClose()
|
|
|
-
|
|
|
- if failed:
|
|
|
- resp Http404
|
|
|
-
|
|
|
- sendFile(filename)
|
|
|
-
|
|
|
- get "/gif/@url":
|
|
|
- cond "http" in @"url"
|
|
|
- cond "twimg" in @"url"
|
|
|
- cond "mp4" in @"url" or "gif" in @"url"
|
|
|
-
|
|
|
- let url = decodeUrl(@"url")
|
|
|
- cond isTwitterUrl(url) == true
|
|
|
-
|
|
|
- let content = await safeFetch(url, mediaAgent)
|
|
|
- if content.len == 0: resp Http404
|
|
|
-
|
|
|
- let filename = parseUri(url).path.split(".")[^1]
|
|
|
- resp content, settings.mimes.getMimetype(filename)
|
|
|
+ enableRawMode()
|
|
|
+ let code = await proxyMedia(request, $uri)
|
|
|
+ if code == Http200:
|
|
|
+ enableRawMode()
|
|
|
+ break route
|
|
|
+ else:
|
|
|
+ resp code
|
|
|
|
|
|
get "/video/@sig/@url":
|
|
|
cond "http" in @"url"
|
|
|
@@ -75,17 +88,26 @@ proc createMediaRouter*(cfg: Config) =
|
|
|
if getHmac(url) != @"sig":
|
|
|
resp showError("Failed to verify signature", cfg)
|
|
|
|
|
|
- var content = await safeFetch(url, mediaAgent)
|
|
|
- if content.len == 0: resp Http404
|
|
|
+ if ".mp4" in url or ".ts" in url:
|
|
|
+ let code = await proxyMedia(request, url)
|
|
|
+ if code == Http200:
|
|
|
+ enableRawMode()
|
|
|
+ break route
|
|
|
+ else:
|
|
|
+ resp code
|
|
|
|
|
|
+ var content: string
|
|
|
if ".vmap" in url:
|
|
|
var m: RegexMatch
|
|
|
- discard content.find(m3u8Regex, m)
|
|
|
- url = decodeUrl(content[m.group(0)[0]])
|
|
|
content = await safeFetch(url, mediaAgent)
|
|
|
+ if content.find(m3u8Regex, m):
|
|
|
+ url = decodeUrl(content[m.group(0)[0]])
|
|
|
+ content = await safeFetch(url, mediaAgent)
|
|
|
+ else:
|
|
|
+ resp Http404
|
|
|
|
|
|
if ".m3u8" in url:
|
|
|
- content = proxifyVideo(content, prefs.proxyVideos)
|
|
|
+ let vid = await safeFetch(url, mediaAgent)
|
|
|
+ content = proxifyVideo(vid, prefs.proxyVideos)
|
|
|
|
|
|
- let ext = parseUri(url).path.split(".")[^1]
|
|
|
- resp content, settings.mimes.getMimetype(ext)
|
|
|
+ resp content, m3u8Mime
|