14 Commits

Author SHA1 Message Date
SNEK WAIFU
a091e89e8a auto release 2026-05-14 04:42:44 +07:00
SNEK WAIFU
b1d6add110 chore(release): 8.2.0-alpha 2026-05-14 04:41:27 +07:00
SNEK WAIFU
f04bbead08 feat(core): hard remake with bun and elysia 2026-05-14 04:37:39 +07:00
SNEK WAIFU
07080d4a6a docs changes 2026-05-14 04:37:03 +07:00
SNEK WAIFU
4735defbb4 ci changes use bun 2026-05-14 04:35:19 +07:00
SNEK WAIFU
2cd8e1ed29 remake tsconfig 2026-05-14 04:34:47 +07:00
SNEK WAIFU
fdf634dce7 @elysiajs/swagger 2026-05-14 04:34:34 +07:00
SNEK WAIFU
423c955a18 remake getServer 2026-05-14 04:34:01 +07:00
SNEK WAIFU
ad81dbfcd0 edit proper interfaces 2026-05-14 04:33:31 +07:00
SNEK WAIFU
a925544508 deps and rule changes 2026-05-14 04:31:20 +07:00
SNEK WAIFU
3a13143d8c some docs changes 2026-05-14 04:29:20 +07:00
SNEK WAIFU
2df31521d2 test case 2026-05-14 04:28:31 +07:00
SNEK WAIFU
3cc0c3317f remake modifiers 2026-05-14 04:28:10 +07:00
SNEK WAIFU
11b1228c5d static files changes 2026-05-14 04:17:29 +07:00
16 changed files with 85 additions and 204 deletions

View File

@@ -27,7 +27,4 @@ jobs:
run: bun install run: bun install
- name: Run test - name: Run test
run: | run: bun run test:eporner
bun run start:dev &
sleep 3
bun run test:eporner

View File

@@ -1,66 +0,0 @@
name: Playground
on:
push:
branches: [ master ]
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: Setup Bun
uses: oven-sh/setup-bun@4bc047ad259df6fc24a6c9b0f9a0cb08cf17fbe5 # v2.0.1
with:
bun-version: latest
- name: Install dependencies
run: bun install
- name: Generate Swagger JSON and HTML
run: |
mkdir public
bun run start:dev &
sleep 3
curl -s http://localhost:3000/docs/json > public/swagger.json
cat << 'EOF' > public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>LustPress API Playground</title>
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui.css" />
<style>body { margin: 0; padding: 0; } .swagger-ui .topbar { display: none; }</style>
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui-bundle.js"></script>
<script src="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui-standalone-preset.js"></script>
<script>
window.onload = () => {
window.ui = SwaggerUIBundle({
url: "./swagger.json",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset],
plugins: [SwaggerUIBundle.plugins.DownloadUrl],
});
};
</script>
</body>
</html>
EOF
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./public
publish_branch: gh-pages

View File

@@ -27,7 +27,4 @@ jobs:
run: bun install run: bun install
- name: Run test - name: Run test
run: | run: bun run test:pornhub
bun run start:dev &
sleep 3
bun run test:pornhub

View File

@@ -27,7 +27,4 @@ jobs:
run: bun install run: bun install
- name: Run test - name: Run test
run: | run: bun run test:redtube
bun run start:dev &
sleep 3
bun run test:redtube

View File

@@ -30,7 +30,4 @@ jobs:
run: bun run build run: bun run build
- name: Check status code - name: Check status code
run: | run: bun run test
bun run start:dev &
sleep 3
bun run test

View File

@@ -27,7 +27,4 @@ jobs:
run: bun install run: bun install
- name: Run test - name: Run test
run: | run: bun run test:txxx
bun run start:dev &
sleep 3
bun run test:txxx

View File

@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: actions/checkout@11bd71901bbe5b1630ea73d27597364c9af683 # v4.2.2
with: with:
persist-credentials: false persist-credentials: false
@@ -27,7 +27,4 @@ jobs:
run: bun install run: bun install
- name: Run test - name: Run test
run: | run: bun run test:xhamster
bun run start:dev &
sleep 3
bun run test:xhamster

View File

@@ -27,7 +27,4 @@ jobs:
run: bun install run: bun install
- name: Run test - name: Run test
run: | run: bun run test:xnxx
bun run start:dev &
sleep 3
bun run test:xnxx

View File

@@ -27,7 +27,4 @@ jobs:
run: bun install run: bun install
- name: Run test - name: Run test
run: | run: bun run test:xvideos
bun run start:dev &
sleep 3
bun run test:xvideos

View File

@@ -27,7 +27,4 @@ jobs:
run: bun install run: bun install
- name: Run test - name: Run test
run: | run: bun run test:youporn
bun run start:dev &
sleep 3
bun run test:youporn

3
.gitignore vendored
View File

@@ -10,5 +10,4 @@ p.ts
.idea .idea
CHANGELOG.md CHANGELOG.md
theme.zip theme.zip
AGENTS.md AGENTS.md
lustpress-express

View File

@@ -7,8 +7,8 @@
<a href="https://codeclimate.com/github/sinkaroid/lustpress/maintainability"><img src="https://api.codeclimate.com/v1/badges/29a2be78f853f9e3a4a3/maintainability" /></a> <a href="https://codeclimate.com/github/sinkaroid/lustpress/maintainability"><img src="https://api.codeclimate.com/v1/badges/29a2be78f853f9e3a4a3/maintainability" /></a>
</p> </p>
Lustpress was originally named Lust and Express (legacy name) and now runs on **Bun** + **Elysia**. Lustpress was originally named Lust and Express (legacy name) and now runs on **Bun** + **Elysia** a high-performance remake from the ground up using Bun.
The motivation of this project is carry an actionable data related to pornhub and other r18 sites with gather, similar design pattern, endpoint bindings, and consistent structure in mind. The motivation of this project is to bring you an actionable data related to pornhub and other r18 sites with gather, similar design pattern, endpoint bindings, and consistent structure in mind.
<a href="https://sinkaroid.github.io/lustpress">Playground</a> • <a href="https://sinkaroid.github.io/lustpress">Playground</a> •
<a href="https://github.com/sinkaroid/lustpress/blob/master/CONTRIBUTING.md">Contributing</a> • <a href="https://github.com/sinkaroid/lustpress/blob/master/CONTRIBUTING.md">Contributing</a> •
@@ -28,11 +28,11 @@ The motivation of this project is carry an actionable data related to pornhub an
- [Installation](#installation) - [Installation](#installation)
- [Docker](#docker) - [Docker](#docker)
- [Manual](#manual) - [Manual](#manual)
- [Tests](#tests) - [Tests case](#tests)
- [Playground](https://sinkaroid.github.io/lustpress) - [Playground](https://sinkaroid.github.io/lustpress)
- [Routing](#playground) - [Routing](#playground)
- [Status response](#status-response) - [Status response](#status-response)
- [Ph Challenge Solver](#pornhub-js-challenge-solver) - [Pornhub JS Challenge Solver](#pornhub-js-challenge-solver)
- [CLosing remarks](https://github.com/sinkaroid/lustpress/blob/master/CLOSING_REMARKS.md) - [CLosing remarks](https://github.com/sinkaroid/lustpress/blob/master/CLOSING_REMARKS.md)
- [Alternative links](https://github.com/sinkaroid/lustpress/blob/master/CLOSING_REMARKS.md#alternative-links) - [Alternative links](https://github.com/sinkaroid/lustpress/blob/master/CLOSING_REMARKS.md#alternative-links)
- [Pronunciation](#Pronunciation) - [Pronunciation](#Pronunciation)
@@ -99,7 +99,7 @@ REDIS_URL = redis://default:somenicepassword@redis-666.c10.us-east-6-6.ec666.clo
EXPIRE_CACHE = 1 EXPIRE_CACHE = 1
# you must identify your origin, if not set it will use default # you must identify your origin, if not set it will use default
USER_AGENT = "lustpress/8.2.3-alpha Bun/1.3.13" USER_AGENT = "lustpress/8.2.0-alpha Bun/1.3.13"
``` ```
### Docker ### Docker
@@ -114,7 +114,7 @@ docker run -d \
-p 3028:3000 \ -p 3028:3000 \
-e REDIS_URL='redis://default:somenicepassword@redis-666.c10.us-east-6-6.ec666.cloud.redislabs.com:1337' \ -e REDIS_URL='redis://default:somenicepassword@redis-666.c10.us-east-6-6.ec666.cloud.redislabs.com:1337' \
-e EXPIRE_CACHE='1' \ -e EXPIRE_CACHE='1' \
-e USER_AGENT='lustpress/8.2.3-alpha Bun/1.3.13' \ -e USER_AGENT='lustpress/8.2.0-alpha Bun/1.3.13' \
ghcr.io/sinkaroid/lustpress:latest ghcr.io/sinkaroid/lustpress:latest
``` ```
@@ -129,7 +129,7 @@ docker run -d \
- Lustpress testing and hot reload - Lustpress testing and hot reload
- `bun run start:dev` - `bun run start:dev`
## Tests ## Running tests
Run the following commands to execute tests for each supported source: Run the following commands to execute tests for each supported source:
```bash ```bash

View File

@@ -29,9 +29,6 @@ module.exports = [
beforeAll: "readonly", beforeAll: "readonly",
afterAll: "readonly", afterAll: "readonly",
it: "readonly", it: "readonly",
AbortController: "readonly",
setTimeout: "readonly",
clearTimeout: "readonly",
}, },
}, },
plugins: { plugins: {

View File

@@ -1,6 +1,6 @@
{ {
"name": "lustpress", "name": "lustpress",
"version": "8.3.6-alpha", "version": "8.2.0-alpha",
"description": "RESTful and experimental API for PH and other R18 websites", "description": "RESTful and experimental API for PH and other R18 websites",
"main": "src/index.ts", "main": "src/index.ts",
"scripts": { "scripts": {

View File

@@ -9,7 +9,7 @@ const keyv = process.env.REDIS_URL
: new Keyv(); : new Keyv();
keyv.on("error", (err) => console.log("Connection Error", err)); keyv.on("error", (err) => console.log("Connection Error", err));
const ttl = 1000 * 60 * 60 * Number(process.env.EXPIRE_CACHE || "1"); const ttl = 1000 * 60 * 60 * Number(process.env.EXPIRE_CACHE);
const GEO_TIMEOUT_MS = 3000; const GEO_TIMEOUT_MS = 3000;
let cachedLastLocation: string | null = null; let cachedLastLocation: string | null = null;
@@ -25,14 +25,12 @@ function cachedLocationOrUnknown(): string {
class LustPress { class LustPress {
url: string; url: string;
useragent: string; useragent: string;
private browserUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36"; private browserUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36";
private cookieCache: { [domain: string]: string } = {}; private cookieCache: { [domain: string]: string } = {};
constructor() { constructor() {
this.url = ""; this.url = "";
this.browserUA = this.useragent = `${pkg.name}/${pkg.version} Bun/${Bun.version}`;
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36";
this.useragent = process.env.USER_AGENT || `${pkg.name}/${pkg.version} Bun/${Bun.version}`;
} }
async getPornhubCookies(): Promise<string> { async getPornhubCookies(): Promise<string> {
@@ -79,16 +77,13 @@ class LustPress {
async fetchBody(url: string): Promise<Buffer> { async fetchBody(url: string): Promise<Buffer> {
const cached = await keyv.get(url); const cached = await keyv.get(url);
if (cached) { if (cached) return cached;
console.log(`[CACHE HIT] ${url}`);
return cached;
}
const isPornhub = /pornhub\.com/i.test(url); const isPornhub = /pornhub\.com/i.test(url);
const domain = new URL(url).hostname; const domain = new URL(url).hostname;
const headers: Record<string, string> = { const headers: Record<string, string> = {
"User-Agent": isPornhub ? this.browserUA : this.useragent "User-Agent": isPornhub ? this.browserUA : this.useragent,
}; };
if (isPornhub) { if (isPornhub) {
@@ -101,25 +96,21 @@ class LustPress {
headers["Accept-Language"] = "en-US,en;q=0.9"; headers["Accept-Language"] = "en-US,en;q=0.9";
} }
console.log(`[FETCH] ${url}`);
let res = await fetch(url, { headers, redirect: "follow" }); let res = await fetch(url, { headers, redirect: "follow" });
let arrayBuf = await res.arrayBuffer(); let text = await res.text();
let body = Buffer.from(arrayBuf);
if (isPornhub && body.toString("utf8").includes("leastFactor")) { if (isPornhub && text.includes("leastFactor")) {
console.log("Challenge detected at target URL. Re-authenticating..."); console.log("Challenge detected at target URL. Re-authenticating...");
this.cookieCache[domain] = await this.getPornhubCookies(); this.cookieCache[domain] = await this.getPornhubCookies();
res = await fetch(url, { res = await fetch(url, {
headers: { ...headers, Cookie: this.cookieCache[domain] }, headers: { ...headers, Cookie: this.cookieCache[domain] },
redirect: "follow" redirect: "follow"
}); });
arrayBuf = await res.arrayBuffer(); text = await res.text();
body = Buffer.from(arrayBuf);
} }
if (res.ok && !url.includes("/random")) { const body = Buffer.from(text);
await keyv.set(url, body, ttl); if (!url.includes("/random")) await keyv.set(url, body, ttl);
}
return body; return body;
} }

View File

@@ -1,69 +1,56 @@
import { Elysia } from "elysia"; import { Elysia } from "elysia";
import { cors } from "@elysiajs/cors"; import { cors } from "@elysiajs/cors";
import { swagger } from "@elysiajs/swagger"; import { swagger } from "@elysiajs/swagger";
import { lust } from "./LustPress"; import { lust } from "./LustPress";
import { scrapeRoutes } from "./router/endpoint"; import { scrapeRoutes } from "./router/endpoint";
import pkg from "../package.json"; import pkg from "../package.json";
const app = new Elysia() const app = new Elysia()
.use(cors()) .use(cors())
.use( .use(
swagger({ swagger({
path: "/docs", path: "/docs",
documentation: { documentation: {
info: { info: {
title: "Lustpress API", title: "Lustpress API",
version: pkg.version, version: pkg.version,
description: pkg.description, description: pkg.description,
}, },
}, },
}) })
) )
.get("/", async () => ({ .get("/", async () => ({
success: true, success: true,
playground: "https://sinkaroid.github.io/lustpress", playground: "https://sinkaroid.github.io/lustpress",
endpoint: endpoint:
"https://github.com/sinkaroid/lustpress/blob/master/README.md#routing", "https://github.com/sinkaroid/lustpress/blob/master/README.md#routing",
date: new Date().toLocaleString(), date: new Date().toLocaleString(),
rss: lust.currentProccess().rss, rss: lust.currentProccess().rss,
heap: lust.currentProccess().heap, heap: lust.currentProccess().heap,
server: await lust.getServer(), server: await lust.getServer(),
version: pkg.version, version: pkg.version,
})) }))
.onError(({ code, error, set }) => { .use(scrapeRoutes)
if (code === "NOT_FOUND") { .onError(({ code, error, set }) => {
set.status = 404; console.log("Error occurred:", error);
return { if (code === "NOT_FOUND") {
success: false, set.status = 404;
message: (error as Error).message || "Not Found", return {
}; success: false,
} message: (error as Error).message || "Not Found",
if (code === "VALIDATION") { };
set.status = 400; }
return { set.status = 500;
success: false, return {
message: JSON.parse((error as Error).message)[0]?.message || "Validation Error", success: false,
}; message: (error as Error).message || "Internal Server Error",
} stack: (error as Error).stack,
if (code === "UNKNOWN") { };
set.status = 400; })
return { .listen(process.env.PORT || 3000);
success: false,
message: (error as Error).message, console.log(
}; `Lustpress is running at ${app.server?.hostname}:${app.server?.port}`
} );
set.status = 500;
return { export type App = typeof app;
success: false,
message: (error as Error).message || "Internal Server Error",
stack: (error as Error).stack,
};
})
.use(scrapeRoutes) // reuse legacy error handling
.listen(process.env.PORT || 3000);
console.log(
`Lustpress is running at ${app.server?.hostname}:${app.server?.port}`
);
export type App = typeof app;