This commit is contained in:
Paul Wilde 2024-10-16 13:47:32 +01:00
commit 04efcce3c1
9 changed files with 301 additions and 0 deletions

BIN
Pimvidious Executable file

Binary file not shown.

14
Pimvidious.nimble Normal file
View 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
View file

@ -0,0 +1 @@
-d:ssl

54
src/Pimvidious.nim Normal file
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 720 B

2
src/resources/html_end Normal file
View file

@ -0,0 +1,2 @@
</body>
</html>

12
src/resources/html_start Normal file
View 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>