feat: use Playwright challenge and reuse cookies scraper (#17)

* adjust eslint

* playground depreciation warning

* adjust some tests

* playwright simulate run instead just raw phin

* adjusted ci

* vibe debugging

* update all deps, small ci changes, and strict dockerized build
This commit is contained in:
Indrawan I.
2026-03-13 11:05:40 +07:00
committed by GitHub
parent 09b49f1649
commit e63195035d
37 changed files with 847 additions and 473 deletions

View File

@@ -4,9 +4,6 @@ import c from "../../utils/options";
import { logger } from "../../utils/logger";
import { maybeError } from "../../utils/modifier";
import { Request, Response } from "express";
import LustPress from "../../LustPress";
const lust = new LustPress();
export async function randomEporner(req: Request, res: Response) {
/**

View File

@@ -1,7 +1,7 @@
import { scrapeContent } from "../../scraper/eporner/epornerSearchController";
import c from "../../utils/options";
import { logger } from "../../utils/logger";
import { maybeError, spacer } from "../../utils/modifier";
import { maybeError } from "../../utils/modifier";
import { Request, Response } from "express";
export async function searchEporner(req: Request, res: Response) {
@@ -45,28 +45,28 @@ export async function searchEporner(req: Request, res: Response) {
if (isNaN(page)) throw Error("Parameter page must be a number");
const slug = key
.toLowerCase()
.trim()
.replace(/\s+/g, "-");
.toLowerCase()
.trim()
.replace(/\s+/g, "-");
const url =
page === 1
? `${c.EPORNER}/tag/${slug}/`
: `${c.EPORNER}/tag/${slug}/${page}/`;
? `${c.EPORNER}/tag/${slug}/`
: `${c.EPORNER}/tag/${slug}/${page}/`;
const data = await scrapeContent(url);
logger.info({
path: req.path,
query: req.query,
method: req.method,
ip: req.ip,
useragent: req.get("User-Agent")
path: req.path,
query: req.query,
method: req.method,
ip: req.ip,
useragent: req.get("User-Agent")
});
return res.json(data);
} catch (err) {
} catch (err) {
const e = err as Error;
res.status(400).json(maybeError(false, e.message));
}
}
}

View File

@@ -14,7 +14,7 @@ function getRelatedApiUrl(videoId: string, page = 1, count = 50): string {
const thousand = Math.floor(id / 1_000) * 1_000;
return (
`https://txxx.com/api/json/videos_related2/` +
"https://txxx.com/api/json/videos_related2/" +
`432000/${count}/${million}/${thousand}/${id}.all.${page}.json`
);
}

View File

@@ -6,53 +6,53 @@ import { logger } from "../../utils/logger";
const lust = new LustPress();
export async function randomTxxx(req: Request, res: Response) {
try {
const apiUrl =
try {
const apiUrl =
"https://txxx.com/api/json/videos2/14400/str/most-popular/60/..1.all..day.json";
const buffer = await lust.fetchBody(apiUrl);
const rawData = JSON.parse(buffer.toString("utf-8"));
const buffer = await lust.fetchBody(apiUrl);
const rawData = JSON.parse(buffer.toString("utf-8"));
const videos = Array.isArray(rawData.videos)
? rawData.videos
: [];
const videos = Array.isArray(rawData.videos)
? rawData.videos
: [];
if (videos.length === 0) {
throw new Error("No videos returned from upstream");
}
// Pick one random video
const v = videos[Math.floor(Math.random() * videos.length)];
const data = {
video_id: v.video_id,
title: v.title,
dir: v.dir,
duration: v.duration,
views: v.video_viewed,
rating: v.rating,
uploaded: v.post_date,
likes: v.likes,
dislikes: v.dislikes,
image: v.scr,
categories: v.categories ? v.categories.split(",") : [],
embed: `https://txxx.com/embed/${v.video_id}/`,
};
logger.info({
path: req.path,
method: req.method,
ip: req.ip,
useragent: req.get("User-Agent"),
});
return res.json({
success: true,
data,
source: apiUrl,
});
} catch (err) {
const e = err as Error;
return res.status(400).json(maybeError(false, e.message));
if (videos.length === 0) {
throw new Error("No videos returned from upstream");
}
// Pick one random video
const v = videos[Math.floor(Math.random() * videos.length)];
const data = {
video_id: v.video_id,
title: v.title,
dir: v.dir,
duration: v.duration,
views: v.video_viewed,
rating: v.rating,
uploaded: v.post_date,
likes: v.likes,
dislikes: v.dislikes,
image: v.scr,
categories: v.categories ? v.categories.split(",") : [],
embed: `https://txxx.com/embed/${v.video_id}/`,
};
logger.info({
path: req.path,
method: req.method,
ip: req.ip,
useragent: req.get("User-Agent"),
});
return res.json({
success: true,
data,
source: apiUrl,
});
} catch (err) {
const e = err as Error;
return res.status(400).json(maybeError(false, e.message));
}
}

View File

@@ -5,59 +5,59 @@ import { maybeError } from "../../utils/modifier";
const lust = new LustPress();
export async function searchTxxx(req: Request, res: Response) {
try {
const key = String(req.query.key || "").trim();
const page = Number(req.query.page || 1);
try {
const key = String(req.query.key || "").trim();
const page = Number(req.query.page || 1);
if (!key) {
return res.json({
success: false,
error: "Parameter key is required",
});
}
if (!key) {
return res.json({
success: false,
error: "Parameter key is required",
});
}
if (Number.isNaN(page)) {
return res.json({
success: false,
error: "Parameter page must be a number",
});
}
if (Number.isNaN(page)) {
return res.json({
success: false,
error: "Parameter page must be a number",
});
}
const apiUrl =
`https://txxx.com/api/videos2.php` +
const apiUrl =
"https://txxx.com/api/videos2.php" +
`?params=259200/str/relevance/60/search..${page}.all..` +
`&s=${encodeURIComponent(key)}`;
// Fetch from API directly
const buffer = await lust.fetchBody(apiUrl);
const rawData = JSON.parse(buffer.toString("utf-8"));
// Fetch from API directly
const buffer = await lust.fetchBody(apiUrl);
const rawData = JSON.parse(buffer.toString("utf-8"));
const videos = Array.isArray(rawData.videos) ? rawData.videos : [];
const videos = Array.isArray(rawData.videos) ? rawData.videos : [];
const data = videos.map((v: any) => ({
video_id: v.video_id,
title: v.title,
dir: v.dir,
duration: v.duration,
views: v.video_viewed,
rating: v.rating,
uploaded: v.post_date,
likes: v.likes,
dislikes: v.dislikes,
image: v.scr,
categories: v.categories ? v.categories.split(",") : [],
embed: `https://txxx.com/embed/${v.video_id}/`,
}));
const data = videos.map((v: any) => ({
video_id: v.video_id,
title: v.title,
dir: v.dir,
duration: v.duration,
views: v.video_viewed,
rating: v.rating,
uploaded: v.post_date,
likes: v.likes,
dislikes: v.dislikes,
image: v.scr,
categories: v.categories ? v.categories.split(",") : [],
embed: `https://txxx.com/embed/${v.video_id}/`,
}));
return res.json({
success: true,
total_count: String(rawData.total_count ?? videos.length),
pages: Number(rawData.pages ?? 1),
page,
data,
});
} catch (err) {
const e = err as Error;
return res.json(maybeError(false, e.message));
}
return res.json({
success: true,
total_count: String(rawData.total_count ?? videos.length),
pages: Number(rawData.pages ?? 1),
page,
data,
});
} catch (err) {
const e = err as Error;
return res.json(maybeError(false, e.message));
}
}