mirror of
https://github.com/zedeus/nitter.git
synced 2024-05-19 17:48:10 +00:00
4cdb8f78cb
This change is to work around the issue that chromium based browsers have handling the "name=orig" parameter appended to URLs. This parameter is needed to retrieve the full resolution image from twitter, but causes those browsers to fill in "jpg_name=orig" as the extension on the filename. This change adds a new endpoint, "/pic/orig/<encoded media>". This new endpoint will internally fetch the URL with ":orig" appended on the end for the full res image. Externally, the endpoint will serve the image without the extra parameter to expose the real extension to the browser. This new endpoint is used when rendering tweets with attached images. The old endpoint is still in place for all other proxied images, and for any legacy links. I also updated the "?name=small" parameter to ":small" since that seems to be the new pattern for image sizing. This should fix issue #458.
142 lines
3.5 KiB
Nim
142 lines
3.5 KiB
Nim
# SPDX-License-Identifier: AGPL-3.0-only
|
|
import uri, strutils, httpclient, os, hashes, base64, re
|
|
import asynchttpserver, asyncstreams, asyncfile, asyncnet
|
|
|
|
import jester
|
|
|
|
import router_utils
|
|
import ".."/[types, formatters, utils]
|
|
|
|
export asynchttpserver, asyncstreams, asyncfile, asyncnet
|
|
export httpclient, os, strutils, asyncstreams, base64, re
|
|
|
|
const
|
|
m3u8Mime* = "application/vnd.apple.mpegurl"
|
|
maxAge* = "max-age=604800"
|
|
|
|
proc safeFetch*(url: string): Future[string] {.async.} =
|
|
let client = newAsyncHttpClient()
|
|
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()
|
|
|
|
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\/orig\/(enc)?\/?(.+)":
|
|
var url = decoded(request, 1)
|
|
if "twimg.com" notin url:
|
|
url.insert(twimg)
|
|
if not url.startsWith(https):
|
|
url.insert(https)
|
|
url.add(":orig")
|
|
|
|
let uri = parseUri(url)
|
|
cond isTwitterUrl(uri) == true
|
|
|
|
let code = await proxyMedia(request, url)
|
|
check code
|
|
|
|
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))
|
|
if m3u8.len > 0:
|
|
content = await safeFetch(url)
|
|
else:
|
|
resp Http404
|
|
|
|
if ".m3u8" in url:
|
|
let vid = await safeFetch(url)
|
|
content = proxifyVideo(vid, cookiePref(proxyVideos))
|
|
|
|
resp content, m3u8Mime
|