@@ -12,6 +12,7 @@ export httpclient, os, strutils, asyncstreams, base64, re
1212
1313const
1414 m3u8Mime* = " application/vnd.apple.mpegurl"
15+ mp4Mime* = " video/mp4"
1516 maxAge* = " max-age=604800"
1617
1718proc safeFetch * (url: string ): Future [string ] {.async .} =
@@ -20,56 +21,81 @@ proc safeFetch*(url: string): Future[string] {.async.} =
2021 except : discard
2122 finally : client.close ()
2223
23- template respond * (req: asynchttpserver.Request ; headers) =
24- var msg = " HTTP/1.1 200 OK\c\L "
25- for k, v in headers:
24+ template respond * (req: asynchttpserver.Request ; code: HttpCode ;
25+ headers: seq [(string , string )]) =
26+ var msg = " HTTP/1.1 " & $ code & " \c\L "
27+ for (k, v) in headers:
2628 msg.add (k & " : " & v & " \c\L " )
2729
2830 msg.add " \c\L "
29- yield req.client.send (msg)
31+ yield req.client.send (msg, flags= {})
32+
33+ proc getContentLength (res: AsyncResponse ): string =
34+ result = " 0"
35+ if res.headers.hasKey (" content-length" ):
36+ result = $ res.contentLength
37+ elif res.headers.hasKey (" content-range" ):
38+ result = res.headers[" content-range" ]
39+ result = result [result .find ('/' ) + 1 .. ^ 1 ]
40+ if result == " *" :
41+ result .setLen (0 )
3042
3143proc proxyMedia * (req: jester.Request ; url: string ): Future [HttpCode ] {.async .} =
3244 result = Http200
45+
3346 let
3447 request = req.getNativeReq ()
35- client = newAsyncHttpClient ()
48+ hashed = $ hash (url)
49+
50+ if request.headers.getOrDefault (" If-None-Match" ) == hashed:
51+ return Http304
52+
53+ let c = newAsyncHttpClient (headers= newHttpHeaders ({
54+ " accept" : " */*" ,
55+ " range" : $ req.headers.getOrDefault (" range" )
56+ }))
3657
3758 try :
38- let res = await client .get (url)
39- if res.status != " 200 OK " :
59+ var res = await c .get (url)
60+ if not res.status. startsWith ( " 20 " ) :
4061 return Http404
4162
42- let hashed = $ hash (url)
43- if request.headers.getOrDefault (" If-None-Match" ) == hashed:
44- return Http304
63+ var headers = @ {
64+ " Accept-Ranges" : " bytes" ,
65+ " Content-Type" : res.headers[" content-type" , 0 ],
66+ " Cache-Control" : maxAge
67+ }
4568
46- let contentLength =
47- if res.headers.hasKey (" content-length " ):
48- res.headers[ " content-length " , 0 ]
49- else :
50- " "
69+ var tries = 0
70+ while tries <= 10 and res.headers.hasKey (" transfer-encoding " ):
71+ await sleepAsync ( 100 + tries * 200 )
72+ res = await c. get (url)
73+ tries.inc
5174
52- let headers = newHttpHeaders ({
53- " Content-Type" : res.headers[" content-type" , 0 ],
54- " Content-Length" : contentLength,
55- " Cache-Control" : maxAge,
56- " ETag" : hashed
57- })
75+ let contentLength = res.getContentLength
76+ if contentLength.len > 0 :
77+ headers.add (" Content-Length" , contentLength)
5878
59- respond (request, headers)
79+ if res.headers.hasKey (" content-range" ):
80+ headers.add (" Content-Range" , $ res.headers.getOrDefault (" content-range" ))
81+ respond (request, Http206 , headers)
82+ else :
83+ respond (request, Http200 , headers)
6084
6185 var (hasValue, data) = (true , " " )
6286 while hasValue:
6387 (hasValue, data) = await res.bodyStream.read ()
6488 if hasValue:
65- await request.client.send (data)
89+ await request.client.send (data, flags = {} )
6690 data.setLen 0
67- except HttpRequestError , ProtocolError , OSError :
91+ except OSError : discard
92+ except ProtocolError , HttpRequestError :
6893 result = Http404
6994 finally :
70- client .close ()
95+ c .close ()
7196
72- template check * (code): untyped =
97+ template check * (c): untyped =
98+ let code = c
7399 if code != Http200 :
74100 resp code
75101 else :
@@ -83,37 +109,27 @@ proc decoded*(req: jester.Request; index: int): string =
83109 if based: decode (encoded)
84110 else : decodeUrl (encoded)
85111
112+ proc getPicUrl * (req: jester.Request ): string =
113+ result = decoded (req, 1 )
114+ if " twimg.com" notin result :
115+ result .insert (twimg)
116+ if not result .startsWith (https):
117+ result .insert (https)
118+
86119proc createMediaRouter * (cfg: Config ) =
87120 router media:
88121 get " /pic/?" :
89122 resp Http404
90123
91124 get re " ^\ /pic\ /orig\ /(enc)?\ /?(.+)" :
92- var url = decoded (request, 1 )
93- if " twimg.com" notin url:
94- url.insert (twimg)
95- if not url.startsWith (https):
96- url.insert (https)
97- url.add (" ?name=orig" )
98-
99- let uri = parseUri (url)
100- cond isTwitterUrl (uri) == true
101-
102- let code = await proxyMedia (request, url)
103- check code
125+ let url = getPicUrl (request)
126+ cond isTwitterUrl (parseUri (url)) == true
127+ check await proxyMedia (request, url & " ?name=orig" )
104128
105129 get re " ^\ /pic\ /(enc)?\ /?(.+)" :
106- var url = decoded (request, 1 )
107- if " twimg.com" notin url:
108- url.insert (twimg)
109- if not url.startsWith (https):
110- url.insert (https)
111-
112- let uri = parseUri (url)
113- cond isTwitterUrl (uri) == true
114-
115- let code = await proxyMedia (request, url)
116- check code
130+ let url = getPicUrl (request)
131+ cond isTwitterUrl (parseUri (url)) == true
132+ check await proxyMedia (request, url)
117133
118134 get re " ^\ /video\ /(enc)?\ /?(.+)\ /(.+)$" :
119135 let url = decoded (request, 2 )
@@ -123,8 +139,7 @@ proc createMediaRouter*(cfg: Config) =
123139 resp showError (" Failed to verify signature" , cfg)
124140
125141 if " .mp4" in url or " .ts" in url or " .m4s" in url:
126- let code = await proxyMedia (request, url)
127- check code
142+ check await proxyMedia (request, url)
128143
129144 var content: string
130145 if " .vmap" in url:
0 commit comments