first
This commit is contained in:
commit
04efcce3c1
9 changed files with 301 additions and 0 deletions
BIN
Pimvidious
Executable file
BIN
Pimvidious
Executable file
Binary file not shown.
14
Pimvidious.nimble
Normal file
14
Pimvidious.nimble
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Package
|
||||
|
||||
version = "0.1.0"
|
||||
author = "Paul Wilde"
|
||||
description = "Porninvidious re-made in Nim"
|
||||
license = "BSD-3-Clause"
|
||||
srcDir = "src"
|
||||
bin = @["Pimvidious"]
|
||||
|
||||
|
||||
# Dependencies
|
||||
|
||||
requires "nim >= 2.0.0"
|
||||
requires "mummy"
|
1
nim.cfg
Normal file
1
nim.cfg
Normal file
|
@ -0,0 +1 @@
|
|||
-d:ssl
|
54
src/Pimvidious.nim
Normal file
54
src/Pimvidious.nim
Normal file
|
@ -0,0 +1,54 @@
|
|||
import mummy, mummy/routers
|
||||
import tables, strutils, mimetypes
|
||||
|
||||
import genpage
|
||||
|
||||
let ip = "0.0.0.0"
|
||||
let port = 8069
|
||||
|
||||
var mimes = newMimeTypes()
|
||||
|
||||
proc pageHandler(request: Request) =
|
||||
echo request.uri
|
||||
var headers: HttpHeaders
|
||||
let content = genpage(path=request.uri)
|
||||
headers["content-type"] = "text/html"
|
||||
request.respond(200,headers, content)
|
||||
|
||||
proc videoHandler(request: Request) =
|
||||
echo request.uri
|
||||
var headers: HttpHeaders
|
||||
let content = genpage(path=request.uri, video_uri=request.uri)
|
||||
headers["content-type"] = "text/html"
|
||||
request.respond(200, headers, content)
|
||||
|
||||
proc staticFileHandler(request: Request) =
|
||||
var file = request.uri[1..^1]
|
||||
if file == "favicon.ico": file = "favicon.png"
|
||||
{.gcsafe.}:
|
||||
if RESOURCES.hasKey(file):
|
||||
var headers: HttpHeaders
|
||||
let ext = file.split(".")[^1]
|
||||
let content_type = mimes.getMimetype(ext)
|
||||
headers["content-type"] = content_type
|
||||
request.respond(200, headers, RESOURCES[file])
|
||||
else:
|
||||
request.respond(404)
|
||||
|
||||
|
||||
proc createRouter(): Router =
|
||||
var router: Router
|
||||
router.get("/", pageHandler)
|
||||
router.get("/video.*/*", videoHandler)
|
||||
router.get("/video.*/*/*/*", videoHandler)
|
||||
router.get("/favicon.ico", staticFileHandler)
|
||||
router.get("/favicon.png", staticFileHandler)
|
||||
router.get("/css.css", staticFileHandler)
|
||||
return router
|
||||
|
||||
|
||||
when isMainModule:
|
||||
let server = newServer(createRouter())
|
||||
echo "Connected to ", ip, ":", port
|
||||
server.serve(Port(port),address=ip)
|
||||
|
128
src/genpage.nim
Normal file
128
src/genpage.nim
Normal file
|
@ -0,0 +1,128 @@
|
|||
import httpclient
|
||||
import os, strutils, strformat, tables, re, sugar, uri
|
||||
|
||||
type
|
||||
Urls = seq[string]
|
||||
Thumbs = seq[string]
|
||||
|
||||
proc cacheResources(): Table[string, string] =
|
||||
echo "Compiling Resources..."
|
||||
var res: Table[string,string]
|
||||
for file in walkDir("src/resources"):
|
||||
let path = file.path.split("/")[^1]
|
||||
res[path] = staticRead(file.path.replace("src/",""))
|
||||
echo "Done"
|
||||
return res
|
||||
|
||||
const RESOURCES*: Table[string, string] = cacheResources()
|
||||
|
||||
proc request(uri: string): string =
|
||||
var client = newHttpClient()
|
||||
try:
|
||||
echo "Requesting ", uri
|
||||
let content = client.getContent(uri)
|
||||
return content
|
||||
except:
|
||||
return "Not Found"
|
||||
finally:
|
||||
client.close()
|
||||
|
||||
proc parseUrls(content: string): seq[string] =
|
||||
let url_re = re"""<div class="thumb"><a href=\"(.*)\">"""
|
||||
let urls = collect:
|
||||
for url in content.findAll(url_re):
|
||||
url.split("\"")[3]
|
||||
return urls
|
||||
|
||||
proc parseThumbs(content: string): seq[string] =
|
||||
let thumb_re = re"""data-src="(.*)""""
|
||||
let thumbs = collect:
|
||||
for thumb in content.findAll(thumb_re):
|
||||
thumb.split("\"")[1]
|
||||
return thumbs
|
||||
|
||||
proc setUpVideo(content: string, video_uri: string): (string, Urls, Thumbs) =
|
||||
let rel_vids_re = re"""video_related=(.+)"""
|
||||
let vid_url_re = re"""setVideoUrl(High|Low)(.*)"""
|
||||
let vid_hls_re = re"""setVideoHLS(.*)"""
|
||||
let vid_thumb_re = re"""setThumbUrl(.*)"""
|
||||
let related_vids = collect:
|
||||
for vid in content.findAll(rel_vids_re):
|
||||
vid.replace(",","\n")
|
||||
let videos = content.findAll(vid_url_re)
|
||||
var video = videos[0].split("'")[1]
|
||||
let video_low = videos[1].split("'")[1]
|
||||
var video_hls: string
|
||||
var video_hls_matches = content.findAll(vid_hls_re)
|
||||
if video_hls_matches.len > 0:
|
||||
video_hls = video_hls_matches[0].split("'")[1]
|
||||
if video == "":
|
||||
video = video_low
|
||||
let video_thumb = content.findAll(vid_thumb_re)[0].split("'")[1]
|
||||
var HTML_VIDEO = fmt"""<center><video poster="{video_thumb}" controls autoplay loop><source src="{video}"/></video></center><center id='infoblock'>"""
|
||||
if video_hls != "":
|
||||
HTML_VIDEO &= fmt"""<h3>HD Stream: </h3><input type='text' size='50' value='{video_hls}' /><br/>(use with a video player)"""
|
||||
HTML_VIDEO &= "</center>"
|
||||
var thumbs: Thumbs = @[]
|
||||
var urls: Urls = @[]
|
||||
for vid in related_vids:
|
||||
thumbs = collect:
|
||||
for thumb in vid.findAll(re""""i":(.*)"""):
|
||||
thumb.replace("\\","").replace("\"i\":","").replace("\"","")
|
||||
urls = collect:
|
||||
for url in vid.findAll(re""""u":(.*)"""):
|
||||
url.replace("\\","").replace("\"u\":","").replace("\"","")
|
||||
return (HTML_VIDEO, urls, thumbs)
|
||||
|
||||
proc getPagination(content:string): string =
|
||||
let page_re = re"""<div class="pagination ">(.*)</div>"""
|
||||
let pagination = content.findAll(page_re)
|
||||
if pagination.len > 0:
|
||||
return pagination[0]
|
||||
|
||||
proc compilePage(content: string, video_uri: string = "", search_query: string = ""): string =
|
||||
var
|
||||
page: string = ""
|
||||
urls: Urls
|
||||
thumbs: Thumbs
|
||||
pagination: string = getPagination(content)
|
||||
page &= RESOURCES["html_start"].replace("**QUERY**",search_query)
|
||||
if video_uri != "":
|
||||
var video: string
|
||||
(video, urls, thumbs) = setUpVideo(content, video_uri)
|
||||
page &= fmt"<center>{video}</center>"
|
||||
else:
|
||||
urls = parseUrls(content)
|
||||
thumbs = parseThumbs(content)
|
||||
if pagination != "":
|
||||
page &= pagination
|
||||
page &= "<section>"
|
||||
for idx, url in urls:
|
||||
let title = url.split("/")[^1].replace("_"," ")
|
||||
if title == "THUMBNUM":
|
||||
# This was in Porninvidious, but that uses cut instead of last index,
|
||||
# so probably not necessary here. Leaving a note in case
|
||||
echo "THUMBNUM - do something else here"
|
||||
page &= fmt"""<div><a href="{url}"><img src="{thumbs[idx]}"/><br/>{title}</a></div>"""
|
||||
page &= "</section>"
|
||||
if pagination != "":
|
||||
page &= pagination
|
||||
page &= "<style>"
|
||||
page &= RESOURCES["css.css"]
|
||||
page &= "</style>"
|
||||
page &= RESOURCES["html_end"]
|
||||
return page
|
||||
|
||||
const XV = "https://www.xvideos.com"
|
||||
proc genpage*(path: string, video_uri: string = ""): string =
|
||||
let content = request(XV & path)
|
||||
var search_query: string
|
||||
if video_uri == "":
|
||||
let search = parseUri(path)
|
||||
for item in search.query.split("&"):
|
||||
let parm = item.split("=")
|
||||
if parm[0] == "k":
|
||||
search_query = parm[1]
|
||||
return compilePage(content, search_query = search_query)
|
||||
else:
|
||||
return compilePage(content, video_uri)
|
90
src/resources/css.css
Normal file
90
src/resources/css.css
Normal file
|
@ -0,0 +1,90 @@
|
|||
h1 {
|
||||
color: white;
|
||||
margin-top: 4vh;
|
||||
margin-left: 4%;
|
||||
}
|
||||
h3 {
|
||||
color: white;
|
||||
display: inline;
|
||||
}
|
||||
#infoblock {
|
||||
margin-bottom: 8vh;
|
||||
color: white;
|
||||
}
|
||||
a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #111;
|
||||
}
|
||||
form {
|
||||
text-align: center;
|
||||
margin-bottom: 10vh;
|
||||
}
|
||||
|
||||
section {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
div {
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 1em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
video {
|
||||
margin-top: -4vh;
|
||||
max-width:100%;
|
||||
min-width:80%;
|
||||
margin-bottom: 0.5vh;
|
||||
}
|
||||
.pagination ul li {
|
||||
display:inline-block;
|
||||
margin-right:20px;
|
||||
}
|
||||
.pagination ul li.no-page {
|
||||
display:none;
|
||||
}
|
||||
.pagination ul li a.active {
|
||||
color:grey;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1200px) { /* Half screen */
|
||||
section {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
div {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1000px) { /* Mobile */
|
||||
section {
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
}
|
||||
|
||||
h1, #infoblock, div {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
form input {
|
||||
font-size: 2rem;
|
||||
}
|
||||
video {
|
||||
min-width:100%;}
|
||||
}
|
BIN
src/resources/favicon.png
Normal file
BIN
src/resources/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 720 B |
2
src/resources/html_end
Normal file
2
src/resources/html_end
Normal file
|
@ -0,0 +1,2 @@
|
|||
</body>
|
||||
</html>
|
12
src/resources/html_start
Normal file
12
src/resources/html_start
Normal file
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Pimvidious</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1><a href="/">Pimvidious</a></h1>
|
||||
<form method="get" action="/">
|
||||
<input type="text" size="30" name="k" value="**QUERY**" />
|
||||
<input type="submit" value="Search"/>
|
||||
</form>
|
Loading…
Reference in a new issue