ci: tdd
ci: tdd
This commit is contained in:
2
.dockerignore
Normal file
2
.dockerignore
Normal file
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
npm-debug.log
|
||||
14
.env.schema
Normal file
14
.env.schema
Normal file
@@ -0,0 +1,14 @@
|
||||
# railway, fly.dev, heroku, vercel or any free service
|
||||
RAILWAY = sinkaroid
|
||||
|
||||
# default port
|
||||
PORT = 3000
|
||||
|
||||
# backend storage, default is redis, if not set it will consume memory storage
|
||||
REDIS_URL = redis://default:somenicepassword@redis-666.c10.us-east-6-6.ec666.cloud.redislabs.com:1337
|
||||
|
||||
# ttl expire cache (in X hour)
|
||||
EXPIRE_CACHE = 1
|
||||
|
||||
# you must identify your origin, if not set it will use default
|
||||
USER_AGENT = "lustpress/1.6.0 Node.js/16.9.1"
|
||||
2
.eslintignore
Normal file
2
.eslintignore
Normal file
@@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
build/
|
||||
41
.eslintrc.json
Normal file
41
.eslintrc.json
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"env": {
|
||||
"es2021": true,
|
||||
"node": true,
|
||||
"commonjs": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 12,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"linebreak-style": 0,
|
||||
"quotes": [
|
||||
"error",
|
||||
"double"
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"no-empty": "error",
|
||||
"no-func-assign": "error",
|
||||
"no-case-declarations": "off",
|
||||
"no-unreachable": "error",
|
||||
"no-eval": "error",
|
||||
"no-global-assign": "error",
|
||||
"@typescript-eslint/no-explicit-any": ["off"],
|
||||
"indent": [
|
||||
"error",
|
||||
2
|
||||
]
|
||||
}
|
||||
}
|
||||
30
.github/workflows/pornhub.yml
vendored
Normal file
30
.github/workflows/pornhub.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: Pornhub test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [14.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Pornhub test
|
||||
run: npm run test:pornhub
|
||||
29
.github/workflows/status.yml
vendored
Normal file
29
.github/workflows/status.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: Check status
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [14.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
- name: Build
|
||||
run: npm run build
|
||||
- name: Check status code
|
||||
run: npm run test
|
||||
22
CLOSING_REMARKS.md
Normal file
22
CLOSING_REMARKS.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Closing remarks
|
||||
|
||||
I hope you have found this project useful. All the major credit really goes to the all the doujin sites, which
|
||||
allows this services to operate.
|
||||
|
||||
- [PornHub](https://pornhub.com)
|
||||
- [Xnxx](https://xnxx.com)
|
||||
- [RedTube](https://redtube.com)
|
||||
- [Xvideos](https://xvideos.com)
|
||||
- [Xhamster](https://xhamster.com)
|
||||
- [YouPorn](https://youporn.com)
|
||||
- [JavHD](https://javhd.com)
|
||||
|
||||
Core dependencies:
|
||||
- [express](https://github.com/expressjs/express)
|
||||
- [cheerio](https://cheerio.js.org/)
|
||||
- [keyv](https://github.com/jaredwray/keyv)
|
||||
|
||||
# Alternative-links
|
||||
Just in case if https://lust.scathach.id down, here some alternative deployment
|
||||
|
||||
- TBA
|
||||
76
CODE_OF_CONDUCT.md
Normal file
76
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at anakmancasan@gmail.com. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
10
CONTRIBUTING.md
Normal file
10
CONTRIBUTING.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Contributing
|
||||
|
||||
## Making major changes
|
||||
Please first discuss the change you wish to make via issue,
|
||||
email, or any other method with the owners before making a change.
|
||||
|
||||
Please note we have a code of conduct, follow it in all your interactions with the project.
|
||||
|
||||
## Making minor changes
|
||||
@sinkaroid are not the best coder, so there are sure some problematic coding decision, every slight of changes will helps however. I always happy to receive Pull requests to improve things.
|
||||
9
Dockerfile
Normal file
9
Dockerfile
Normal file
@@ -0,0 +1,9 @@
|
||||
FROM node:latest
|
||||
|
||||
WORKDIR /srv/app
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
EXPOSE 3000
|
||||
CMD ["node", "build/src/index.js"]
|
||||
5
SECURITY.md
Normal file
5
SECURITY.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Lustpress Security
|
||||
|
||||
## Reporting vulnerabilities
|
||||
|
||||
To report sensitive vulnerabilities, alert the author by email at anakmancasan@gmail.com.
|
||||
69
package.json
Normal file
69
package.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"name": "lustpress",
|
||||
"version": "1.6.0-alpha",
|
||||
"description": "RESTful and experimental API for PornHub and other porn sites, which official is lack even isn't exist.",
|
||||
"main": "build/src/index.js",
|
||||
"scripts": {
|
||||
"build": "rimraf build && tsc",
|
||||
"start": "node build/src/index.js",
|
||||
"test": "ts-node test/test.ts",
|
||||
"test:mock": "ts-node test/mock.ts",
|
||||
"start:prod": "npm run build && node build/src/index.js",
|
||||
"start:dev": "ts-node-dev src/index.ts",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"build:freshdoc": "rimraf docs && rimraf template && rimraf theme.zip",
|
||||
"build:template": " npm run build:freshdoc && curl https://codeload.github.com/ScathachGrip/apidocjs-theme/zip/refs/tags/v11 -o theme.zip && unzip theme.zip && mv apidocjs-theme-11 template",
|
||||
"build:apidoc": "npm run build:template && apidoc -i src -o ./docs -t ./template/template-scarlet",
|
||||
"test:pornhub": "start-server-and-test 3000 \"curl -v http://localhost:3000/pornhub/random | jq '.'\"",
|
||||
"test:xnxx": "start-server-and-test 3000 \"curl -v http://localhost:3000/xnxx/random | jq '.'\"",
|
||||
"test:redtube": "start-server-and-test 3000 \"curl -v http://localhost:3000/redtube/random | jq '.'\"",
|
||||
"test:xvideos": "start-server-and-test 3000 \"curl -v http://localhost:3000/xvideos/random | jq '.'\"",
|
||||
"test:xhamster": "start-server-and-test 3000 \"curl -v http://localhost:3000/xhamster/random | jq '.'\"",
|
||||
"test:youporn": "start-server-and-test 3000 \"curl -v http://localhost:3000/youporn/random | jq '.'\""
|
||||
},
|
||||
"apidoc": {
|
||||
"title": "Lustpress API Documentation",
|
||||
"url": "https://lust.scathach.id",
|
||||
"sampleUrl": "https://lust.scathach.id",
|
||||
"name": "Lustpress"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "sinkaroid",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/sinkaroid/lustpress.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@keyv/redis": "^2.5.7",
|
||||
"cheerio": "^1.0.0-rc.12",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.0.3",
|
||||
"express": "^4.18.2",
|
||||
"express-rate-limit": "^6.7.0",
|
||||
"express-slow-down": "^1.6.0",
|
||||
"keyv": "^4.5.2",
|
||||
"phin": "^3.7.0",
|
||||
"pino": "^8.11.0",
|
||||
"pino-pretty": "^10.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cors": "^2.8.13",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/express-slow-down": "^1.3.2",
|
||||
"@types/node": "^18.15.11",
|
||||
"@typescript-eslint/eslint-plugin": "^5.58.0",
|
||||
"@typescript-eslint/parser": "^5.58.0",
|
||||
"apidoc": "0.29.0",
|
||||
"eslint": "^8.38.0",
|
||||
"rimraf": "^5.0.0",
|
||||
"start-server-and-test": "^2.0.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"ts-node-dev": "^2.0.0",
|
||||
"typescript": "5.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
}
|
||||
153
src/LustPress.ts
Normal file
153
src/LustPress.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import p, { IResponse } from "phin";
|
||||
import Keyv from "keyv";
|
||||
import pkg from "../package.json";
|
||||
|
||||
|
||||
const keyv = new Keyv(process.env.REDIS_URL);
|
||||
|
||||
keyv.on("error", err => console.log("Connection Error", err));
|
||||
const ttl = 1000 * 60 * 60 * Number(process.env.EXPIRE_CACHE);
|
||||
|
||||
|
||||
class LustPress {
|
||||
url: string;
|
||||
useragent: string;
|
||||
constructor() {
|
||||
this.url = "";
|
||||
this.useragent = `${pkg.name}/${pkg.version} Node.js/16.9.1`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch body from url and check if it's cached
|
||||
* @param url url to fetch
|
||||
* @returns Buffer
|
||||
*/
|
||||
async fetchBody(url: string): Promise<Buffer> {
|
||||
const cached = await keyv.get(url);
|
||||
|
||||
if (cached) {
|
||||
console.log("Fetching from cache");
|
||||
return cached;
|
||||
} else if (url.includes("/random")) {
|
||||
console.log("Random should not be cached");
|
||||
const res = await p({
|
||||
url: url,
|
||||
"headers": {
|
||||
"User-Agent": process.env.USER_AGENT || `${pkg.name}/${pkg.version} Node.js/16.9.1`,
|
||||
},
|
||||
followRedirects: true
|
||||
});
|
||||
return res.body;
|
||||
} else {
|
||||
console.log("Fetching from source");
|
||||
const res = await p({
|
||||
url: url,
|
||||
"headers": {
|
||||
"User-Agent": process.env.USER_AGENT || `${pkg.name}/${pkg.version} Node.js/16.9.1`,
|
||||
},
|
||||
followRedirects: true
|
||||
});
|
||||
await keyv.set(url, res.body, ttl);
|
||||
return res.body;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* remove html tag and bunch of space
|
||||
* @param str string to remove html tag
|
||||
* @returns string
|
||||
*/
|
||||
removeHtmlTag(str: string): string {
|
||||
str = str.replace(/(\r\n|\n|\r)/gm, "");
|
||||
str = str.replace(/\s+/g, "");
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove html tag without space
|
||||
* @param str string to remove html tag
|
||||
* @returns string
|
||||
*/
|
||||
removeHtmlTagWithoutSpace(str: string): string {
|
||||
str = str.replace(/(\r\n|\n|\r|\t)/gm, "");
|
||||
str = str.replace(/\\/g, "");
|
||||
return str.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* remove all single quote on array
|
||||
* @param arr array to remove single quote
|
||||
* @returns string[]
|
||||
*/
|
||||
removeAllSingleQuoteOnArray(arr: string[]): string[] {
|
||||
return arr.map((item) => item.replace(/'/g, ""));
|
||||
}
|
||||
|
||||
/**
|
||||
* time ago converter
|
||||
* @param input date to convert
|
||||
* @returns string
|
||||
*/
|
||||
timeAgo(input: Date) {
|
||||
const date = new Date(input);
|
||||
const formatter: any = new Intl.RelativeTimeFormat("en");
|
||||
const ranges: { [key: string]: number } = {
|
||||
years: 3600 * 24 * 365,
|
||||
months: 3600 * 24 * 30,
|
||||
weeks: 3600 * 24 * 7,
|
||||
days: 3600 * 24,
|
||||
hours: 3600,
|
||||
minutes: 60,
|
||||
seconds: 1
|
||||
};
|
||||
const secondsElapsed = (date.getTime() - Date.now()) / 1000;
|
||||
for (const key in ranges) {
|
||||
if (ranges[key] < Math.abs(secondsElapsed)) {
|
||||
const delta = secondsElapsed / ranges[key];
|
||||
return formatter.format(Math.round(delta), key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* convert seconds to minute
|
||||
* @param seconds seconds to convert
|
||||
* @returns string
|
||||
*/
|
||||
secondToMinute(seconds: number): string {
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const second = seconds % 60;
|
||||
return `${minutes}min, ${second}sec`;
|
||||
}
|
||||
|
||||
/**
|
||||
* get current process memory usage
|
||||
* @returns object
|
||||
*/
|
||||
currentProccess() {
|
||||
const arr = [1, 2, 3, 4, 5, 6, 9, 7, 8, 9, 10];
|
||||
arr.reverse();
|
||||
const rss = process.memoryUsage().rss / 1024 / 1024;
|
||||
const heap = process.memoryUsage().heapUsed / 1024 / 1024;
|
||||
const heaptotal = process.memoryUsage().heapTotal / 1024 / 1024;
|
||||
return {
|
||||
rss: `${Math.round(rss * 100) / 100} MB`,
|
||||
heap: `${Math.round(heap * 100) / 100}/${Math.round(heaptotal * 100) / 100} MB`
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* fetch this server location
|
||||
* @returns <Promise<string>>
|
||||
*/
|
||||
async getServer(): Promise<string> {
|
||||
const raw = await p({
|
||||
"url": "http://ip-api.com/json",
|
||||
"parse": "json"
|
||||
}) as IResponse;
|
||||
const data = raw.body as unknown as { country: string, regionName: string };
|
||||
return `${data.country}, ${data.regionName}`;
|
||||
}
|
||||
}
|
||||
|
||||
export default LustPress;
|
||||
55
src/controller/pornhub/pornhubGet.ts
Normal file
55
src/controller/pornhub/pornhubGet.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { scrapeContent } from "../../scraper/pornhub/pornhubGetController";
|
||||
import c from "../../utils/options";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { maybeError } from "../../utils/modifier";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export async function getPornhub(req: Request, res: Response) {
|
||||
try {
|
||||
const id = req.query.id as string;
|
||||
if (!id) throw Error("Parameter id is required");
|
||||
|
||||
/**
|
||||
* @api {get} /pornhub/get?id=:id Get Pornhub
|
||||
* @apiName Get pornhub
|
||||
* @apiGroup pornhub
|
||||
* @apiDescription Get a pornhub video based on id
|
||||
*
|
||||
* @apiParam {String} id Video ID
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* HTTP/1.1 400 Bad Request
|
||||
*
|
||||
* @apiExample {curl} curl
|
||||
* curl -i https://lust.scathach.id/pornhub/get?id=ph63c4e1dc48fe7
|
||||
*
|
||||
* @apiExample {js} JS/TS
|
||||
* import axios from "axios"
|
||||
*
|
||||
* axios.get("https://lust.scathach.id/pornhub/get?id=ph63c4e1dc48fe7")
|
||||
* .then(res => console.log(res.data))
|
||||
* .catch(err => console.error(err))
|
||||
*
|
||||
* @apiExample {python} Python
|
||||
* import aiohttp
|
||||
* async with aiohttp.ClientSession() as session:
|
||||
* async with session.get("https://lust.scathach.id/pornhub/get?id=ph63c4e1dc48fe7") as resp:
|
||||
* print(await resp.json())
|
||||
*/
|
||||
|
||||
const url = `${c.PORNHUB}/view_video.php?viewkey=${id}`;
|
||||
const data = await scrapeContent(url);
|
||||
logger.info({
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
useragent: req.get("User-Agent")
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
res.status(400).json(maybeError(false, e.message));
|
||||
}
|
||||
}
|
||||
55
src/controller/pornhub/pornhubGetRelated.ts
Normal file
55
src/controller/pornhub/pornhubGetRelated.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { scrapeContent } from "../../scraper/pornhub/pornhubSearchController";
|
||||
import c from "../../utils/options";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { maybeError } from "../../utils/modifier";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export async function relatedPornhub(req: Request, res: Response) {
|
||||
try {
|
||||
const id = req.query.id as string;
|
||||
if (!id) throw Error("Parameter id is required");
|
||||
|
||||
/**
|
||||
* @api {get} /pornhub/get?id=:id Get Pornhub related videos
|
||||
* @apiName Get pornhub related videos
|
||||
* @apiGroup pornhub
|
||||
* @apiDescription Get a related pornhub videos based on id
|
||||
*
|
||||
* @apiParam {String} id Video ID
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* HTTP/1.1 400 Bad Request
|
||||
*
|
||||
* @apiExample {curl} curl
|
||||
* curl -i https://lust.scathach.id/pornhub/get?id=ph63c4e1dc48fe7
|
||||
*
|
||||
* @apiExample {js} JS/TS
|
||||
* import axios from "axios"
|
||||
*
|
||||
* axios.get("https://lust.scathach.id/pornhub/get?id=ph63c4e1dc48fe7")
|
||||
* .then(res => console.log(res.data))
|
||||
* .catch(err => console.error(err))
|
||||
*
|
||||
* @apiExample {python} Python
|
||||
* import aiohttp
|
||||
* async with aiohttp.ClientSession() as session:
|
||||
* async with session.get("https://lust.scathach.id/pornhub/get?id=ph63c4e1dc48fe7") as resp:
|
||||
* print(await resp.json())
|
||||
*/
|
||||
|
||||
const url = `${c.PORNHUB}/view_video.php?viewkey=${id}`;
|
||||
const data = await scrapeContent(url);
|
||||
logger.info({
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
useragent: req.get("User-Agent")
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
res.status(400).json(maybeError(false, e.message));
|
||||
}
|
||||
}
|
||||
50
src/controller/pornhub/pornhubRandom.ts
Normal file
50
src/controller/pornhub/pornhubRandom.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { Request, Response } from "express";
|
||||
import { scrapeContent } from "../../scraper/pornhub/pornhubGetController";
|
||||
import c from "../../utils/options";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { maybeError } from "../../utils/modifier";
|
||||
|
||||
export async function randomPornhub(req: Request, res: Response) {
|
||||
try {
|
||||
/**
|
||||
* @api {get} /pornhub/random Random pornhub video
|
||||
* @apiName Random pornhub
|
||||
* @apiGroup pornhub
|
||||
* @apiDescription Gets random pornhub video
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* HTTP/1.1 400 Bad Request
|
||||
*
|
||||
* @apiExample {curl} curl
|
||||
* curl -i https://lust.scathach.id/pornhub/random
|
||||
*
|
||||
* @apiExample {js} JS/TS
|
||||
* import axios from "axios"
|
||||
*
|
||||
* axios.get("https://lust.scathach.id/pornhub/random")
|
||||
* .then(res => console.log(res.data))
|
||||
* .catch(err => console.error(err))
|
||||
*
|
||||
* @apiExample {python} Python
|
||||
* import aiohttp
|
||||
* async with aiohttp.ClientSession() as session:
|
||||
* async with session.get("https://lust.scathach.id/pornhub/random") as resp:
|
||||
* print(await resp.json())
|
||||
*
|
||||
*/
|
||||
const url = `${c.PORNHUB}/video/random`;
|
||||
const data = await scrapeContent(url);
|
||||
logger.info({
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
useragent: req.get("User-Agent")
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
res.status(400).json(maybeError(false, e.message));
|
||||
}
|
||||
}
|
||||
66
src/controller/pornhub/pornhubSearch.ts
Normal file
66
src/controller/pornhub/pornhubSearch.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { scrapeContent } from "../../scraper/pornhub/pornhubSearchController";
|
||||
import c from "../../utils/options";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { maybeError, spacer } from "../../utils/modifier";
|
||||
import { Request, Response } from "express";
|
||||
const sorting = ["mr", "mv", "tr", "lg"];
|
||||
|
||||
export async function searchPornhub(req: Request, res: Response) {
|
||||
try {
|
||||
/**
|
||||
* @api {get} /pornhub/search Search pornhub videos
|
||||
* @apiName Search pornhub
|
||||
* @apiGroup pornhub
|
||||
* @apiDescription Search pornhub videos
|
||||
* @apiParam {String} key Keyword to search
|
||||
* @apiParam {Number} [page=1] Page number
|
||||
* @apiParam {String} [sort=mr] Sort by
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* HTTP/1.1 400 Bad Request
|
||||
*
|
||||
* @apiExample {curl} curl
|
||||
* curl -i https://lust.scathach.id/pornhub/search?key=milf
|
||||
* curl -i https://lust.scathach.id/pornhub/search?key=milf&page=2&sort=mr
|
||||
*
|
||||
* @apiExample {js} JS/TS
|
||||
* import axios from "axios"
|
||||
*
|
||||
* axios.get("https://lust.scathach.id/pornhub/search?key=milf")
|
||||
* .then(res => console.log(res.data))
|
||||
* .catch(err => console.error(err))
|
||||
*
|
||||
* @apiExample {python} Python
|
||||
* import aiohttp
|
||||
* async with aiohttp.ClientSession() as session:
|
||||
* async with session.get("https://lust.scathach.id/pornhub/search?key=milf") as resp:
|
||||
* print(await resp.json())
|
||||
*/
|
||||
|
||||
const key = req.query.key as string;
|
||||
const page = req.query.page || 1;
|
||||
const sort = req.query.sort as string;
|
||||
if (!key) throw Error("Parameter key is required");
|
||||
if (isNaN(Number(page))) throw Error("Parameter page must be a number");
|
||||
|
||||
let url;
|
||||
if (!sort) url = `${c.PORNHUB}/video/search?search=${spacer(key)}`;
|
||||
else if (!sorting.includes(sort)) url = `${c.PORNHUB}/video/search?search=${spacer(key)}&page=${page}`;
|
||||
|
||||
else url = `${c.PORNHUB}/video/search?search=${spacer(key)}&o=${sort}&page=${page}`;
|
||||
console.log(url);
|
||||
const data = await scrapeContent(url);
|
||||
logger.info({
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
useragent: req.get("User-Agent")
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
res.status(400).json(maybeError(false, e.message));
|
||||
}
|
||||
}
|
||||
56
src/controller/redtube/redtubeGet.ts
Normal file
56
src/controller/redtube/redtubeGet.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { scrapeContent } from "../../scraper/redtube/redtubeGetController";
|
||||
import c from "../../utils/options";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { maybeError } from "../../utils/modifier";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export async function getRedtube(req: Request, res: Response) {
|
||||
try {
|
||||
const id = req.query.id as string;
|
||||
if (!id) throw Error("Parameter id is required");
|
||||
if (isNaN(Number(id))) throw Error("Parameter id must be a number");
|
||||
|
||||
/**
|
||||
* @api {get} /redtube/get?id=:id Get Redtube
|
||||
* @apiName Get redtube
|
||||
* @apiGroup redtube
|
||||
* @apiDescription Get a redtube video based on id
|
||||
*
|
||||
* @apiParam {String} id Video ID
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* HTTP/1.1 400 Bad Request
|
||||
*
|
||||
* @apiExample {curl} curl
|
||||
* curl -i https://lust.scathach.id/redtube/get?id=42763661
|
||||
*
|
||||
* @apiExample {js} JS/TS
|
||||
* import axios from "axios"
|
||||
*
|
||||
* axios.get("https://lust.scathach.id/redtube/get?id=42763661")
|
||||
* .then(res => console.log(res.data))
|
||||
* .catch(err => console.error(err))
|
||||
*
|
||||
* @apiExample {python} Python
|
||||
* import aiohttp
|
||||
* async with aiohttp.ClientSession() as session:
|
||||
* async with session.get("https://lust.scathach.id/redtube/get?id=42763661") as resp:
|
||||
* print(await resp.json())
|
||||
*/
|
||||
|
||||
const url = `${c.REDTUBE}/${id}`;
|
||||
const data = await scrapeContent(url);
|
||||
logger.info({
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
useragent: req.get("User-Agent")
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
res.status(400).json(maybeError(false, e.message));
|
||||
}
|
||||
}
|
||||
54
src/controller/redtube/redtubeGetRelated.ts
Normal file
54
src/controller/redtube/redtubeGetRelated.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { scrapeContent } from "../../scraper/redtube/redtubeSearchController";
|
||||
import c from "../../utils/options";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { maybeError } from "../../utils/modifier";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export async function relatedRedtube(req: Request, res: Response) {
|
||||
try {
|
||||
/**
|
||||
* @api {get} /redtube/get?id=:id Get redtube related videos
|
||||
* @apiName Get redtube related videos
|
||||
* @apiGroup redtube
|
||||
* @apiDescription Get a related redtube videos based on id
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* HTTP/1.1 400 Bad Request
|
||||
*
|
||||
* @apiExample {curl} curl
|
||||
* curl -i https://lust.scathach.id/redtube/get?id=41698751
|
||||
*
|
||||
* @apiExample {js} JS/TS
|
||||
* import axios from "axios"
|
||||
*
|
||||
* axios.get("https://lust.scathach.id/redtube/get?id=41698751")
|
||||
* .then(res => console.log(res.data))
|
||||
* .catch(err => console.error(err))
|
||||
*
|
||||
* @apiExample {python} Python
|
||||
* import aiohttp
|
||||
* async with aiohttp.ClientSession() as session:
|
||||
* async with session.get("https://lust.scathach.id/redtube/get?id=41698751") as resp:
|
||||
* print(await resp.json())
|
||||
*/
|
||||
|
||||
const id = req.query.id as string;
|
||||
if (!id) throw Error("Parameter key is required");
|
||||
if (isNaN(Number(id))) throw Error("Parameter id must be a number");
|
||||
|
||||
const url = `${c.REDTUBE}/${id}`;
|
||||
const data = await scrapeContent(url);
|
||||
logger.info({
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
useragent: req.get("User-Agent")
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
res.status(400).json(maybeError(false, e.message));
|
||||
}
|
||||
}
|
||||
65
src/controller/redtube/redtubeRandom.ts
Normal file
65
src/controller/redtube/redtubeRandom.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { scrapeContent } from "../../scraper/redtube/redtubeGetController";
|
||||
import c from "../../utils/options";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { maybeError } from "../../utils/modifier";
|
||||
import { Request, Response } from "express";
|
||||
import { load } from "cheerio";
|
||||
import LustPress from "../../LustPress";
|
||||
|
||||
const lust = new LustPress();
|
||||
|
||||
export async function randomRedtube(req: Request, res: Response) {
|
||||
try {
|
||||
|
||||
|
||||
/**
|
||||
* @api {get} /redtube/random Get random redtube
|
||||
* @apiName Get random redtube
|
||||
* @apiGroup redtube
|
||||
* @apiDescription Get a random redtube video
|
||||
*
|
||||
* @apiParam {String} id Video ID
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* HTTP/1.1 400 Bad Request
|
||||
*
|
||||
* @apiExample {curl} curl
|
||||
* curl -i https://lust.scathach.id/redtube/random
|
||||
*
|
||||
* @apiExample {js} JS/TS
|
||||
* import axios from "axios"
|
||||
*
|
||||
* axios.get("https://lust.scathach.id/redtube/random")
|
||||
* .then(res => console.log(res.data))
|
||||
* .catch(err => console.error(err))
|
||||
*
|
||||
* @apiExample {python} Python
|
||||
* import aiohttp
|
||||
* async with aiohttp.ClientSession() as session:
|
||||
* async with session.get("https://lust.scathach.id/redtube/random") as resp:
|
||||
* print(await resp.json())
|
||||
*/
|
||||
const resolve = await lust.fetchBody(c.REDTUBE);
|
||||
const $ = load(resolve);
|
||||
const search = $("a.video_link")
|
||||
.map((i, el) => {
|
||||
return $(el).attr("href");
|
||||
}).get();
|
||||
const random = Math.floor(Math.random() * search.length);
|
||||
|
||||
const url = c.REDTUBE + search[random];
|
||||
const data = await scrapeContent(url);
|
||||
logger.info({
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
useragent: req.get("User-Agent")
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
res.status(400).json(maybeError(false, e.message));
|
||||
}
|
||||
}
|
||||
58
src/controller/redtube/redtubeSearch.ts
Normal file
58
src/controller/redtube/redtubeSearch.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { scrapeContent } from "../../scraper/redtube/redtubeSearchController";
|
||||
import c from "../../utils/options";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { maybeError, spacer } from "../../utils/modifier";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export async function searchRedtube(req: Request, res: Response) {
|
||||
try {
|
||||
/**
|
||||
* @api {get} /redtube/search Search redtube videos
|
||||
* @apiName Search redtube
|
||||
* @apiGroup redtube
|
||||
* @apiDescription Search redtube videos
|
||||
* @apiParam {String} key Keyword to search
|
||||
* @apiParam {Number} [page=1] Page number
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* HTTP/1.1 400 Bad Request
|
||||
*
|
||||
* @apiExample {curl} curl
|
||||
* curl -i https://lust.scathach.id/redtube/search?key=milf
|
||||
* curl -i https://lust.scathach.id/redtube/search?key=milf&page=2
|
||||
*
|
||||
* @apiExample {js} JS/TS
|
||||
* import axios from "axios"
|
||||
*
|
||||
* axios.get("https://lust.scathach.id/redtube/search?key=milf")
|
||||
* .then(res => console.log(res.data))
|
||||
* .catch(err => console.error(err))
|
||||
*
|
||||
* @apiExample {python} Python
|
||||
* import aiohttp
|
||||
* async with aiohttp.ClientSession() as session:
|
||||
* async with session.get("https://lust.scathach.id/redtube/search?key=milf") as resp:
|
||||
* print(await resp.json())
|
||||
*/
|
||||
|
||||
const key = req.query.key as string;
|
||||
const page = req.query.page || 1;
|
||||
if (!key) throw Error("Parameter key is required");
|
||||
if (isNaN(Number(page))) throw Error("Parameter page must be a number");
|
||||
|
||||
const url = `${c.REDTUBE}/?search=${spacer(key)}&page=${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")
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
res.status(400).json(maybeError(false, e.message));
|
||||
}
|
||||
}
|
||||
55
src/controller/xhamster/xhamsterGet.ts
Normal file
55
src/controller/xhamster/xhamsterGet.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { scrapeContent } from "../../scraper/xhamster/xhamsterGetController";
|
||||
import c from "../../utils/options";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { maybeError } from "../../utils/modifier";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export async function getXhamster(req: Request, res: Response) {
|
||||
try {
|
||||
const id = req.query.id as string;
|
||||
if (!id) throw Error("Parameter id is required");
|
||||
|
||||
/**
|
||||
* @api {get} /xhamster/get?id=:id Get xhamster
|
||||
* @apiName Get xhamster
|
||||
* @apiGroup xhamster
|
||||
* @apiDescription Get a xhamster video based on id
|
||||
*
|
||||
* @apiParam {String} id Video ID
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* HTTP/1.1 400 Bad Request
|
||||
*
|
||||
* @apiExample {curl} curl
|
||||
* curl -i https://lust.scathach.id/xhamster/get?id=videos/horny-makima-tests-new-toy-and-cums-intensely-xhAa5wx
|
||||
*
|
||||
* @apiExample {js} JS/TS
|
||||
* import axios from "axios"
|
||||
*
|
||||
* axios.get("https://lust.scathach.id/xhamster/get?id=videos/horny-makima-tests-new-toy-and-cums-intensely-xhAa5wx")
|
||||
* .then(res => console.log(res.data))
|
||||
* .catch(err => console.error(err))
|
||||
*
|
||||
* @apiExample {python} Python
|
||||
* import aiohttp
|
||||
* async with aiohttp.ClientSession() as session:
|
||||
* async with session.get("https://lust.scathach.id/xhamster/get?id=videos/horny-makima-tests-new-toy-and-cums-intensely-xhAa5wx") as resp:
|
||||
* print(await resp.json())
|
||||
*/
|
||||
|
||||
const url = `${c.XHAMSTER}/${id}`;
|
||||
const data = await scrapeContent(url);
|
||||
logger.info({
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
useragent: req.get("User-Agent")
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
res.status(400).json(maybeError(false, e.message));
|
||||
}
|
||||
}
|
||||
55
src/controller/xhamster/xhamsterGetRelated.ts
Normal file
55
src/controller/xhamster/xhamsterGetRelated.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { scrapeContent } from "../../scraper/xhamster/xhamsterSearchController";
|
||||
import c from "../../utils/options";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { maybeError } from "../../utils/modifier";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export async function relatedXhamster(req: Request, res: Response) {
|
||||
try {
|
||||
const id = req.query.id as string;
|
||||
if (!id) throw Error("Parameter id is required");
|
||||
|
||||
/**
|
||||
* @api {get} /xhamster/get?id=:id Get related xhamster
|
||||
* @apiName Get related xhamster
|
||||
* @apiGroup xhamster
|
||||
* @apiDescription Get a xhamster video based on related id
|
||||
*
|
||||
* @apiParam {String} id Video ID
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* HTTP/1.1 400 Bad Request
|
||||
*
|
||||
* @apiExample {curl} curl
|
||||
* curl -i https://lust.scathach.id/xhamster/related?id=videos/horny-makima-tests-new-toy-and-cums-intensely-xhAa5wx
|
||||
*
|
||||
* @apiExample {js} JS/TS
|
||||
* import axios from "axios"
|
||||
*
|
||||
* axios.get("https://lust.scathach.id/xhamster/related?id=videos/horny-makima-tests-new-toy-and-cums-intensely-xhAa5wx")
|
||||
* .then(res => console.log(res.data))
|
||||
* .catch(err => console.error(err))
|
||||
*
|
||||
* @apiExample {python} Python
|
||||
* import aiohttp
|
||||
* async with aiohttp.ClientSession() as session:
|
||||
* async with session.get("https://lust.scathach.id/xhamster/related?id=videos/horny-makima-tests-new-toy-and-cums-intensely-xhAa5wx") as resp:
|
||||
* print(await resp.json())
|
||||
*/
|
||||
|
||||
const url = `${c.XHAMSTER}/${id}`;
|
||||
const data = await scrapeContent(url);
|
||||
logger.info({
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
useragent: req.get("User-Agent")
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
res.status(400).json(maybeError(false, e.message));
|
||||
}
|
||||
}
|
||||
63
src/controller/xhamster/xhamsterRandom.ts
Normal file
63
src/controller/xhamster/xhamsterRandom.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { scrapeContent } from "../../scraper/xhamster/xhamsterGetController";
|
||||
import c from "../../utils/options";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { maybeError } from "../../utils/modifier";
|
||||
import { Request, Response } from "express";
|
||||
import { load } from "cheerio";
|
||||
import LustPress from "../../LustPress";
|
||||
|
||||
const lust = new LustPress();
|
||||
|
||||
export async function randomXhamster(req: Request, res: Response) {
|
||||
try {
|
||||
|
||||
|
||||
/**
|
||||
* @api {get} /xhamster/random Get random xhamster
|
||||
* @apiName Get random xhamster
|
||||
* @apiGroup xhamster
|
||||
* @apiDescription Get a random xhamster video
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* HTTP/1.1 400 Bad Request
|
||||
*
|
||||
* @apiExample {curl} curl
|
||||
* curl -i https://lust.scathach.id/xhamster/random
|
||||
*
|
||||
* @apiExample {js} JS/TS
|
||||
* import axios from "axios"
|
||||
*
|
||||
* axios.get("https://lust.scathach.id/xhamster/random")
|
||||
* .then(res => console.log(res.data))
|
||||
* .catch(err => console.error(err))
|
||||
*
|
||||
* @apiExample {python} Python
|
||||
* import aiohttp
|
||||
* async with aiohttp.ClientSession() as session:
|
||||
* async with session.get("https://lust.scathach.id/xhamster/random") as resp:
|
||||
* print(await resp.json())
|
||||
*/
|
||||
const resolve = await lust.fetchBody(`${c.XHAMSTER}/newest`);
|
||||
const $ = load(resolve);
|
||||
const search = $("a.root-9d8b4.video-thumb-info__name.role-pop.with-dropdown")
|
||||
.map((i, el) => $(el).attr("href"))
|
||||
.get();
|
||||
|
||||
const search_ = search.map((el) => el.replace(c.XHAMSTER, ""));
|
||||
const random = Math.floor(Math.random() * search_.length);
|
||||
const url = c.XHAMSTER + search_[random];
|
||||
const data = await scrapeContent(url);
|
||||
logger.info({
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
useragent: req.get("User-Agent")
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
res.status(400).json(maybeError(false, e.message));
|
||||
}
|
||||
}
|
||||
58
src/controller/xhamster/xhamsterSearch.ts
Normal file
58
src/controller/xhamster/xhamsterSearch.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { scrapeContent } from "../../scraper/xhamster/xhamsterSearchController";
|
||||
import c from "../../utils/options";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { maybeError, spacer } from "../../utils/modifier";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export async function searchXhamster(req: Request, res: Response) {
|
||||
try {
|
||||
/**
|
||||
* @api {get} /xhamster/search Search xhamster videos
|
||||
* @apiName Search xhamster
|
||||
* @apiGroup xhamster
|
||||
* @apiDescription Search xhamster videos
|
||||
* @apiParam {String} key Keyword to search
|
||||
* @apiParam {Number} [page=1] Page number
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* HTTP/1.1 400 Bad Request
|
||||
*
|
||||
* @apiExample {curl} curl
|
||||
* curl -i https://lust.scathach.id/xhamster/search?key=milf
|
||||
* curl -i https://lust.scathach.id/xhamster/search?key=milf&page=2
|
||||
*
|
||||
* @apiExample {js} JS/TS
|
||||
* import axios from "axios"
|
||||
*
|
||||
* axios.get("https://lust.scathach.id/xhamster/search?key=milf")
|
||||
* .then(res => console.log(res.data))
|
||||
* .catch(err => console.error(err))
|
||||
*
|
||||
* @apiExample {python} Python
|
||||
* import aiohttp
|
||||
* async with aiohttp.ClientSession() as session:
|
||||
* async with session.get("https://lust.scathach.id/xhamster/search?key=milf") as resp:
|
||||
* print(await resp.json())
|
||||
*/
|
||||
|
||||
const key = req.query.key as string;
|
||||
const page = req.query.page || 1;
|
||||
if (!key) throw Error("Parameter key is required");
|
||||
if (isNaN(Number(page))) throw Error("Parameter page must be a number");
|
||||
|
||||
const url = `${c.XHAMSTER}/search/${spacer(key)}?page=${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")
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
res.status(400).json(maybeError(false, e.message));
|
||||
}
|
||||
}
|
||||
55
src/controller/xnxx/xnxxGet.ts
Normal file
55
src/controller/xnxx/xnxxGet.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { scrapeContent } from "../../scraper/xnxx/xnxxGetController";
|
||||
import c from "../../utils/options";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { maybeError } from "../../utils/modifier";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export async function getXnxx(req: Request, res: Response) {
|
||||
try {
|
||||
const id = req.query.id as string;
|
||||
if (!id) throw Error("Parameter id is required");
|
||||
|
||||
/**
|
||||
* @api {get} /xnxx/get?id=:id Get xnxx
|
||||
* @apiName Get xnxx
|
||||
* @apiGroup xnxx
|
||||
* @apiDescription Get a xnxx video based on id
|
||||
*
|
||||
* @apiParam {String} id Video ID
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* HTTP/1.1 400 Bad Request
|
||||
*
|
||||
* @apiExample {curl} curl
|
||||
* curl -i https://lust.scathach.id/xnxx/get?id=video-17vah71a/makima_y_denji
|
||||
*
|
||||
* @apiExample {js} JS/TS
|
||||
* import axios from "axios"
|
||||
*
|
||||
* axios.get("https://lust.scathach.id/xnxx/get?id=video-17vah71a/makima_y_denji")
|
||||
* .then(res => console.log(res.data))
|
||||
* .catch(err => console.error(err))
|
||||
*
|
||||
* @apiExample {python} Python
|
||||
* import aiohttp
|
||||
* async with aiohttp.ClientSession() as session:
|
||||
* async with session.get("https://lust.scathach.id/xnxx/get?id=video-17vah71a/makima_y_denji") as resp:
|
||||
* print(await resp.json())
|
||||
*/
|
||||
|
||||
const url = `${c.XNXX}/${id}`;
|
||||
const data = await scrapeContent(url);
|
||||
logger.info({
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
useragent: req.get("User-Agent")
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
res.status(400).json(maybeError(false, e.message));
|
||||
}
|
||||
}
|
||||
55
src/controller/xnxx/xnxxGetRelated.ts
Normal file
55
src/controller/xnxx/xnxxGetRelated.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { scrapeContent } from "../../scraper/xnxx/xnxxGetRelatedController";
|
||||
import c from "../../utils/options";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { maybeError } from "../../utils/modifier";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export async function relatedXnxx(req: Request, res: Response) {
|
||||
try {
|
||||
const id = req.query.id as string;
|
||||
if (!id) throw Error("Parameter id is required");
|
||||
|
||||
/**
|
||||
* @api {get} /xnxx/get?id=:id Get related xnxx
|
||||
* @apiName Get related xnxx
|
||||
* @apiGroup xnxx
|
||||
* @apiDescription Get a xnxx video based on related id
|
||||
*
|
||||
* @apiParam {String} id Video ID
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* HTTP/1.1 400 Bad Request
|
||||
*
|
||||
* @apiExample {curl} curl
|
||||
* curl -i https://lust.scathach.id/xnxx/related?id=video-17vah71a/makima_y_denji
|
||||
*
|
||||
* @apiExample {js} JS/TS
|
||||
* import axios from "axios"
|
||||
*
|
||||
* axios.get("https://lust.scathach.id/xnxx/related?id=video-17vah71a/makima_y_denji")
|
||||
* .then(res => console.log(res.data))
|
||||
* .catch(err => console.error(err))
|
||||
*
|
||||
* @apiExample {python} Python
|
||||
* import aiohttp
|
||||
* async with aiohttp.ClientSession() as session:
|
||||
* async with session.get("https://lust.scathach.id/xnxx/related?id=video-17vah71a/makima_y_denji") as resp:
|
||||
* print(await resp.json())
|
||||
*/
|
||||
|
||||
const url = `${c.XNXX}/${id}`;
|
||||
const data = await scrapeContent(url);
|
||||
logger.info({
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
useragent: req.get("User-Agent")
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
res.status(400).json(maybeError(false, e.message));
|
||||
}
|
||||
}
|
||||
63
src/controller/xnxx/xnxxRandom.ts
Normal file
63
src/controller/xnxx/xnxxRandom.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { scrapeContent } from "../../scraper/xnxx/xnxxGetController";
|
||||
import c from "../../utils/options";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { maybeError } from "../../utils/modifier";
|
||||
import { Request, Response } from "express";
|
||||
import { load } from "cheerio";
|
||||
import LustPress from "../../LustPress";
|
||||
|
||||
const lust = new LustPress();
|
||||
|
||||
export async function randomXnxx(req: Request, res: Response) {
|
||||
try {
|
||||
|
||||
|
||||
/**
|
||||
* @api {get} /xnxx/random Get random xnxx
|
||||
* @apiName Get random xnxx
|
||||
* @apiGroup xnxx
|
||||
* @apiDescription Get a random xnxx video
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* HTTP/1.1 400 Bad Request
|
||||
*
|
||||
* @apiExample {curl} curl
|
||||
* curl -i https://lust.scathach.id/xnxx/random
|
||||
*
|
||||
* @apiExample {js} JS/TS
|
||||
* import axios from "axios"
|
||||
*
|
||||
* axios.get("https://lust.scathach.id/xnxx/random")
|
||||
* .then(res => console.log(res.data))
|
||||
* .catch(err => console.error(err))
|
||||
*
|
||||
* @apiExample {python} Python
|
||||
* import aiohttp
|
||||
* async with aiohttp.ClientSession() as session:
|
||||
* async with session.get("https://lust.scathach.id/xnxx/random") as resp:
|
||||
* print(await resp.json())
|
||||
*/
|
||||
const resolve = await lust.fetchBody("https://www.xnxx.com/search/random/random");
|
||||
const $ = load(resolve);
|
||||
const search = $("div.mozaique > div")
|
||||
.map((i, el) => {
|
||||
return $(el).find("a").attr("href");
|
||||
}).get();
|
||||
const random = Math.floor(Math.random() * search.length);
|
||||
|
||||
const url = c.XNXX + search[random];
|
||||
const data = await scrapeContent(url);
|
||||
logger.info({
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
useragent: req.get("User-Agent")
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
res.status(400).json(maybeError(false, e.message));
|
||||
}
|
||||
}
|
||||
58
src/controller/xnxx/xnxxSearch.ts
Normal file
58
src/controller/xnxx/xnxxSearch.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { scrapeContent } from "../../scraper/xnxx/xnxxSearchController";
|
||||
import c from "../../utils/options";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { maybeError, spacer } from "../../utils/modifier";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export async function searchXnxx(req: Request, res: Response) {
|
||||
try {
|
||||
/**
|
||||
* @api {get} /xnxx/search Search xnxx videos
|
||||
* @apiName Search xnxx
|
||||
* @apiGroup xnxx
|
||||
* @apiDescription Search xnxx videos
|
||||
* @apiParam {String} key Keyword to search
|
||||
* @apiParam {Number} [page=0] Page number
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* HTTP/1.1 400 Bad Request
|
||||
*
|
||||
* @apiExample {curl} curl
|
||||
* curl -i https://lust.scathach.id/xnxx/search?key=milf
|
||||
* curl -i https://lust.scathach.id/xnxx/search?key=milf&page=2
|
||||
*
|
||||
* @apiExample {js} JS/TS
|
||||
* import axios from "axios"
|
||||
*
|
||||
* axios.get("https://lust.scathach.id/xnxx/search?key=milf")
|
||||
* .then(res => console.log(res.data))
|
||||
* .catch(err => console.error(err))
|
||||
*
|
||||
* @apiExample {python} Python
|
||||
* import aiohttp
|
||||
* async with aiohttp.ClientSession() as session:
|
||||
* async with session.get("https://lust.scathach.id/xnxx/search?key=milf") as resp:
|
||||
* print(await resp.json())
|
||||
*/
|
||||
|
||||
const key = req.query.key as string;
|
||||
const page = req.query.page || 0;
|
||||
if (!key) throw Error("Parameter key is required");
|
||||
if (isNaN(Number(page))) throw Error("Parameter page must be a number");
|
||||
|
||||
const url = `${c.XNXX}/search/${spacer(key)}/${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")
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
res.status(400).json(maybeError(false, e.message));
|
||||
}
|
||||
}
|
||||
55
src/controller/xvideos/xvideosGet.ts
Normal file
55
src/controller/xvideos/xvideosGet.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { scrapeContent } from "../../scraper/xvideos/xvideosGetController";
|
||||
import c from "../../utils/options";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { maybeError } from "../../utils/modifier";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export async function getXvideos(req: Request, res: Response) {
|
||||
try {
|
||||
const id = req.query.id as string;
|
||||
if (!id) throw Error("Parameter id is required");
|
||||
|
||||
/**
|
||||
* @api {get} /xvideos/get?id=:id Get xvideos
|
||||
* @apiName Get xvideos
|
||||
* @apiGroup xvideos
|
||||
* @apiDescription Get a xvideos video based on id
|
||||
*
|
||||
* @apiParam {String} id Video ID
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* HTTP/1.1 400 Bad Request
|
||||
*
|
||||
* @apiExample {curl} curl
|
||||
* curl -i https://lust.scathach.id/xvideos/get?id=video73564387/cute_hentai_maid_with_pink_hair_fucking_uncensored_
|
||||
*
|
||||
* @apiExample {js} JS/TS
|
||||
* import axios from "axios"
|
||||
*
|
||||
* axios.get("https://lust.scathach.id/xvideos/get?id=video73564387/cute_hentai_maid_with_pink_hair_fucking_uncensored_")
|
||||
* .then(res => console.log(res.data))
|
||||
* .catch(err => console.error(err))
|
||||
*
|
||||
* @apiExample {python} Python
|
||||
* import aiohttp
|
||||
* async with aiohttp.ClientSession() as session:
|
||||
* async with session.get("https://lust.scathach.id/xvideos/get?id=video73564387/cute_hentai_maid_with_pink_hair_fucking_uncensored_") as resp:
|
||||
* print(await resp.json())
|
||||
*/
|
||||
|
||||
const url = `${c.XVIDEOS}/${id}`;
|
||||
const data = await scrapeContent(url);
|
||||
logger.info({
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
useragent: req.get("User-Agent")
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
res.status(400).json(maybeError(false, e.message));
|
||||
}
|
||||
}
|
||||
55
src/controller/xvideos/xvideosGetRelated.ts
Normal file
55
src/controller/xvideos/xvideosGetRelated.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { scrapeContent } from "../../scraper/xvideos/xvideosGetRelatedController";
|
||||
import c from "../../utils/options";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { maybeError } from "../../utils/modifier";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export async function relatedXvideos(req: Request, res: Response) {
|
||||
try {
|
||||
const id = req.query.id as string;
|
||||
if (!id) throw Error("Parameter id is required");
|
||||
|
||||
/**
|
||||
* @api {get} /xvideos/get?id=:id Get related xvideos
|
||||
* @apiName Get related xvideos
|
||||
* @apiGroup xvideos
|
||||
* @apiDescription Get a xvideos video based on related id
|
||||
*
|
||||
* @apiParam {String} id Video ID
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* HTTP/1.1 400 Bad Request
|
||||
*
|
||||
* @apiExample {curl} curl
|
||||
* curl -i https://lust.scathach.id/xvideos/related?id=video73564387/cute_hentai_maid_with_pink_hair_fucking_uncensored_
|
||||
*
|
||||
* @apiExample {js} JS/TS
|
||||
* import axios from "axios"
|
||||
*
|
||||
* axios.get("https://lust.scathach.id/xvideos/related?id=video73564387/cute_hentai_maid_with_pink_hair_fucking_uncensored_")
|
||||
* .then(res => console.log(res.data))
|
||||
* .catch(err => console.error(err))
|
||||
*
|
||||
* @apiExample {python} Python
|
||||
* import aiohttp
|
||||
* async with aiohttp.ClientSession() as session:
|
||||
* async with session.get("https://lust.scathach.id/xvideos/related?id=video73564387/cute_hentai_maid_with_pink_hair_fucking_uncensored_") as resp:
|
||||
* print(await resp.json())
|
||||
*/
|
||||
|
||||
const url = `${c.XVIDEOS}/${id}`;
|
||||
const data = await scrapeContent(url);
|
||||
logger.info({
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
useragent: req.get("User-Agent")
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
res.status(400).json(maybeError(false, e.message));
|
||||
}
|
||||
}
|
||||
63
src/controller/xvideos/xvideosRandom.ts
Normal file
63
src/controller/xvideos/xvideosRandom.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { scrapeContent } from "../../scraper/xvideos/xvideosGetController";
|
||||
import c from "../../utils/options";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { maybeError } from "../../utils/modifier";
|
||||
import { Request, Response } from "express";
|
||||
import { load } from "cheerio";
|
||||
import LustPress from "../../LustPress";
|
||||
|
||||
const lust = new LustPress();
|
||||
|
||||
export async function randomXvideos(req: Request, res: Response) {
|
||||
try {
|
||||
/**
|
||||
* @api {get} /xvideos/random Get random xvideos
|
||||
* @apiName Get random xvideos
|
||||
* @apiGroup xvideos
|
||||
* @apiDescription Get a random xvideos video
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* HTTP/1.1 400 Bad Request
|
||||
*
|
||||
* @apiExample {curl} curl
|
||||
* curl -i https://lust.scathach.id/xvideos/random
|
||||
*
|
||||
* @apiExample {js} JS/TS
|
||||
* import axios from "axios"
|
||||
*
|
||||
* axios.get("https://lust.scathach.id/xvideos/random")
|
||||
* .then(res => console.log(res.data))
|
||||
* .catch(err => console.error(err))
|
||||
*
|
||||
* @apiExample {python} Python
|
||||
* import aiohttp
|
||||
* async with aiohttp.ClientSession() as session:
|
||||
* async with session.get("https://lust.scathach.id/xvideos/random") as resp:
|
||||
* print(await resp.json())
|
||||
*/
|
||||
const resolve = await lust.fetchBody(c.XVIDEOS);
|
||||
const $ = load(resolve);
|
||||
const search = $("div.thumb-under")
|
||||
.find("a")
|
||||
.map((i, el) => $(el).attr("href"))
|
||||
.get();
|
||||
const filtered = search.filter((el) => el.includes("/video"));
|
||||
const filtered_ = filtered.filter((el) => !el.includes("THUMBNUM"));
|
||||
const random = Math.floor(Math.random() * filtered_.length);
|
||||
|
||||
const url = c.XVIDEOS + filtered[random];
|
||||
const data = await scrapeContent(url);
|
||||
logger.info({
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
useragent: req.get("User-Agent")
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
res.status(400).json(maybeError(false, e.message));
|
||||
}
|
||||
}
|
||||
58
src/controller/xvideos/xvideosSearch.ts
Normal file
58
src/controller/xvideos/xvideosSearch.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { scrapeContent } from "../../scraper/xvideos/xvideosSearchController";
|
||||
import c from "../../utils/options";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { maybeError, spacer } from "../../utils/modifier";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export async function searchXvideos(req: Request, res: Response) {
|
||||
try {
|
||||
/**
|
||||
* @api {get} /xvideos/search Search xvideos videos
|
||||
* @apiName Search xvideos
|
||||
* @apiGroup xvideos
|
||||
* @apiDescription Search xvideos videos
|
||||
* @apiParam {String} key Keyword to search
|
||||
* @apiParam {Number} [page=0] Page number
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* HTTP/1.1 400 Bad Request
|
||||
*
|
||||
* @apiExample {curl} curl
|
||||
* curl -i https://lust.scathach.id/xvideos/search?key=milf
|
||||
* curl -i https://lust.scathach.id/xvideos/search?key=milf&page=2
|
||||
*
|
||||
* @apiExample {js} JS/TS
|
||||
* import axios from "axios"
|
||||
*
|
||||
* axios.get("https://lust.scathach.id/xvideos/search?key=milf")
|
||||
* .then(res => console.log(res.data))
|
||||
* .catch(err => console.error(err))
|
||||
*
|
||||
* @apiExample {python} Python
|
||||
* import aiohttp
|
||||
* async with aiohttp.ClientSession() as session:
|
||||
* async with session.get("https://lust.scathach.id/xvideos/search?key=milf") as resp:
|
||||
* print(await resp.json())
|
||||
*/
|
||||
|
||||
const key = req.query.key as string;
|
||||
const page = req.query.page || 0;
|
||||
if (!key) throw Error("Parameter key is required");
|
||||
if (isNaN(Number(page))) throw Error("Parameter page must be a number");
|
||||
|
||||
const url = `${c.XVIDEOS}/?k=${spacer(key)}&p=${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")
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
res.status(400).json(maybeError(false, e.message));
|
||||
}
|
||||
}
|
||||
55
src/controller/youporn/youpornGet.ts
Normal file
55
src/controller/youporn/youpornGet.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { scrapeContent } from "../../scraper/youporn/youpornGetController";
|
||||
import c from "../../utils/options";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { maybeError } from "../../utils/modifier";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export async function getYouporn(req: Request, res: Response) {
|
||||
try {
|
||||
const id = req.query.id as string;
|
||||
if (!id) throw Error("Parameter id is required");
|
||||
|
||||
/**
|
||||
* @api {get} /youporn/get?id=:id Get youporn
|
||||
* @apiName Get youporn
|
||||
* @apiGroup youporn
|
||||
* @apiDescription Get a youporn video based on id
|
||||
*
|
||||
* @apiParam {String} id Video ID
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* HTTP/1.1 400 Bad Request
|
||||
*
|
||||
* @apiExample {curl} curl
|
||||
* curl -i https://lust.scathach.id/youporn/get?id=16621192/chainsaw-man-fuck-makima-3d-porn-60-fps
|
||||
*
|
||||
* @apiExample {js} JS/TS
|
||||
* import axios from "axios"
|
||||
*
|
||||
* axios.get("https://lust.scathach.id/youporn/get?id=16621192/chainsaw-man-fuck-makima-3d-porn-60-fps")
|
||||
* .then(res => console.log(res.data))
|
||||
* .catch(err => console.error(err))
|
||||
*
|
||||
* @apiExample {python} Python
|
||||
* import aiohttp
|
||||
* async with aiohttp.ClientSession() as session:
|
||||
* async with session.get("https://lust.scathach.id/youporn/get?id=16621192/chainsaw-man-fuck-makima-3d-porn-60-fps") as resp:
|
||||
* print(await resp.json())
|
||||
*/
|
||||
|
||||
const url = `${c.YOUPORN}/watch/${id}`;
|
||||
const data = await scrapeContent(url);
|
||||
logger.info({
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
useragent: req.get("User-Agent")
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
res.status(400).json(maybeError(false, e.message));
|
||||
}
|
||||
}
|
||||
55
src/controller/youporn/youpornGetRelated.ts
Normal file
55
src/controller/youporn/youpornGetRelated.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { scrapeContent } from "../../scraper/youporn/youpornSearchController";
|
||||
import c from "../../utils/options";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { maybeError } from "../../utils/modifier";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export async function relatedYouporn(req: Request, res: Response) {
|
||||
try {
|
||||
const id = req.query.id as string;
|
||||
if (!id) throw Error("Parameter id is required");
|
||||
|
||||
/**
|
||||
* @api {get} /youporn/get?id=:id Get related youporn
|
||||
* @apiName Get related youporn
|
||||
* @apiGroup youporn
|
||||
* @apiDescription Get a youporn video based on related id
|
||||
*
|
||||
* @apiParam {String} id Video ID
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* HTTP/1.1 400 Bad Request
|
||||
*
|
||||
* @apiExample {curl} curl
|
||||
* curl -i https://lust.scathach.id/youporn/related?id=16621192/chainsaw-man-fuck-makima-3d-porn-60-fps
|
||||
*
|
||||
* @apiExample {js} JS/TS
|
||||
* import axios from "axios"
|
||||
*
|
||||
* axios.get("https://lust.scathach.id/youporn/related?id=16621192/chainsaw-man-fuck-makima-3d-porn-60-fps")
|
||||
* .then(res => console.log(res.data))
|
||||
* .catch(err => console.error(err))
|
||||
*
|
||||
* @apiExample {python} Python
|
||||
* import aiohttp
|
||||
* async with aiohttp.ClientSession() as session:
|
||||
* async with session.get("https://lust.scathach.id/youporn/related?id=16621192/chainsaw-man-fuck-makima-3d-porn-60-fps") as resp:
|
||||
* print(await resp.json())
|
||||
*/
|
||||
|
||||
const url = `${c.YOUPORN}/watch/${id}`;
|
||||
const data = await scrapeContent(url);
|
||||
logger.info({
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
useragent: req.get("User-Agent")
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
res.status(400).json(maybeError(false, e.message));
|
||||
}
|
||||
}
|
||||
62
src/controller/youporn/youpornRandom.ts
Normal file
62
src/controller/youporn/youpornRandom.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { scrapeContent } from "../../scraper/youporn/youpornGetController";
|
||||
import c from "../../utils/options";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { maybeError } from "../../utils/modifier";
|
||||
import { Request, Response } from "express";
|
||||
import { load } from "cheerio";
|
||||
import LustPress from "../../LustPress";
|
||||
|
||||
const lust = new LustPress();
|
||||
|
||||
export async function randomYouporn(req: Request, res: Response) {
|
||||
try {
|
||||
|
||||
|
||||
/**
|
||||
* @api {get} /youporn/random Get random youporn
|
||||
* @apiName Get random youporn
|
||||
* @apiGroup youporn
|
||||
* @apiDescription Get a random youporn video
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* HTTP/1.1 400 Bad Request
|
||||
*
|
||||
* @apiExample {curl} curl
|
||||
* curl -i https://lust.scathach.id/youporn/random
|
||||
*
|
||||
* @apiExample {js} JS/TS
|
||||
* import axios from "axios"
|
||||
*
|
||||
* axios.get("https://lust.scathach.id/youporn/random")
|
||||
* .then(res => console.log(res.data))
|
||||
* .catch(err => console.error(err))
|
||||
*
|
||||
* @apiExample {python} Python
|
||||
* import aiohttp
|
||||
* async with aiohttp.ClientSession() as session:
|
||||
* async with session.get("https://lust.scathach.id/youporn/random") as resp:
|
||||
* print(await resp.json())
|
||||
*/
|
||||
const resolve = await lust.fetchBody(`${c.YOUPORN}`);
|
||||
const $ = load(resolve);
|
||||
const search = $("a[href^='/watch/']")
|
||||
.map((i, el) => {
|
||||
return $(el).attr("href");
|
||||
}).get();
|
||||
const random = Math.floor(Math.random() * search.length);
|
||||
const url = c.YOUPORN + search[random];
|
||||
const data = await scrapeContent(url);
|
||||
logger.info({
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
useragent: req.get("User-Agent")
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
res.status(400).json(maybeError(false, e.message));
|
||||
}
|
||||
}
|
||||
58
src/controller/youporn/youpornSearch.ts
Normal file
58
src/controller/youporn/youpornSearch.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { scrapeContent } from "../../scraper/youporn/youpornSearchController";
|
||||
import c from "../../utils/options";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { maybeError, spacer } from "../../utils/modifier";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export async function searchYouporn(req: Request, res: Response) {
|
||||
try {
|
||||
/**
|
||||
* @api {get} /youporn/search Search youporn videos
|
||||
* @apiName Search youporn
|
||||
* @apiGroup youporn
|
||||
* @apiDescription Search youporn videos
|
||||
* @apiParam {String} key Keyword to search
|
||||
* @apiParam {Number} [page=1] Page number
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* HTTP/1.1 400 Bad Request
|
||||
*
|
||||
* @apiExample {curl} curl
|
||||
* curl -i https://lust.scathach.id/youporn/search?key=milf
|
||||
* curl -i https://lust.scathach.id/youporn/search?key=milf&page=2
|
||||
*
|
||||
* @apiExample {js} JS/TS
|
||||
* import axios from "axios"
|
||||
*
|
||||
* axios.get("https://lust.scathach.id/youporn/search?key=milf")
|
||||
* .then(res => console.log(res.data))
|
||||
* .catch(err => console.error(err))
|
||||
*
|
||||
* @apiExample {python} Python
|
||||
* import aiohttp
|
||||
* async with aiohttp.ClientSession() as session:
|
||||
* async with session.get("https://lust.scathach.id/youporn/search?key=milf") as resp:
|
||||
* print(await resp.json())
|
||||
*/
|
||||
|
||||
const key = req.query.key as string;
|
||||
const page = req.query.page || 1;
|
||||
if (!key) throw Error("Parameter key is required");
|
||||
if (isNaN(Number(page))) throw Error("Parameter page must be a number");
|
||||
|
||||
const url = `${c.YOUPORN}/search/?query=${spacer(key)}&page=${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")
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
res.status(400).json(maybeError(false, e.message));
|
||||
}
|
||||
}
|
||||
52
src/index.ts
Normal file
52
src/index.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import "dotenv/config";
|
||||
import LustPress from "./LustPress";
|
||||
import express from "express";
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import scrapeRoutes from "./router/endpoint";
|
||||
import { slow, limiter } from "./utils/limit-options";
|
||||
import { logger } from "./utils/logger";
|
||||
import * as pkg from "../package.json";
|
||||
|
||||
const lust = new LustPress();
|
||||
const app = express();
|
||||
|
||||
|
||||
app.get("/", slow, limiter, async (req, res) => {
|
||||
res.send({
|
||||
success: true,
|
||||
playground: "https://sinkaroid.github.io/lustpress",
|
||||
endpoint: "https://github.com/sinkaroid/lustpress/blob/master/README.md#routing",
|
||||
date: new Date().toLocaleString(),
|
||||
rss: lust.currentProccess().rss,
|
||||
heap: lust.currentProccess().heap,
|
||||
server: await lust.getServer(),
|
||||
version: `${pkg.version}`,
|
||||
});
|
||||
logger.info({
|
||||
path: req.path,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
useragent: req.get("User-Agent")
|
||||
});
|
||||
});
|
||||
|
||||
app.use(scrapeRoutes());
|
||||
app.use((req: Request, res: Response, next: NextFunction) => {
|
||||
res.status(404);
|
||||
next(Error(`The page not found in path ${req.url} and method ${req.method}`));
|
||||
logger.error({
|
||||
path: req.url,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
useragent: req.get("User-Agent")
|
||||
});
|
||||
});
|
||||
|
||||
app.use((error: any, res: Response) => {
|
||||
res.status(500).json({
|
||||
message: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
});
|
||||
|
||||
app.listen(process.env.PORT || 3000, () => console.log(`${pkg.name} is running on port ${process.env.PORT || 3000}`));
|
||||
29
src/interfaces.ts
Normal file
29
src/interfaces.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
export interface IVideoData {
|
||||
success: boolean;
|
||||
data: {
|
||||
title: string;
|
||||
id: string;
|
||||
image: string;
|
||||
duration: string;
|
||||
views: string;
|
||||
rating: string;
|
||||
uploaded: string;
|
||||
upvoted: string | null;
|
||||
downvoted: string | null;
|
||||
models: string[];
|
||||
tags: string[];
|
||||
};
|
||||
source: string;
|
||||
assets: string[];
|
||||
}
|
||||
|
||||
export interface ISearchVideoData {
|
||||
success: boolean;
|
||||
data: string[];
|
||||
source: string;
|
||||
}
|
||||
|
||||
export interface MaybeError {
|
||||
message: string;
|
||||
}
|
||||
|
||||
72
src/router/endpoint.ts
Normal file
72
src/router/endpoint.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import cors from "cors";
|
||||
import { Router } from "express";
|
||||
import { slow, limiter } from "../utils/limit-options";
|
||||
|
||||
// PornHub
|
||||
import { getPornhub } from "../controller/pornhub/pornhubGet";
|
||||
import { searchPornhub } from "../controller/pornhub/pornhubSearch";
|
||||
import { randomPornhub } from "../controller/pornhub/pornhubRandom";
|
||||
import { relatedPornhub } from "../controller/pornhub/pornhubGetRelated";
|
||||
|
||||
// XNXX
|
||||
import { getXnxx } from "../controller/xnxx/xnxxGet";
|
||||
import { searchXnxx } from "../controller/xnxx/xnxxSearch";
|
||||
import { relatedXnxx } from "../controller/xnxx/xnxxGetRelated";
|
||||
import { randomXnxx } from "../controller/xnxx/xnxxRandom";
|
||||
|
||||
// RedTube
|
||||
import { getRedtube } from "../controller/redtube/redtubeGet";
|
||||
import { searchRedtube } from "../controller/redtube/redtubeSearch";
|
||||
import { relatedRedtube } from "../controller/redtube/redtubeGetRelated";
|
||||
import { randomRedtube } from "../controller/redtube/redtubeRandom";
|
||||
|
||||
// Xvideos
|
||||
import { getXvideos } from "../controller/xvideos/xvideosGet";
|
||||
import { searchXvideos } from "../controller/xvideos/xvideosSearch";
|
||||
import { randomXvideos } from "../controller/xvideos/xvideosRandom";
|
||||
import { relatedXvideos } from "../controller/xvideos/xvideosGetRelated";
|
||||
|
||||
// Xhamster
|
||||
import { getXhamster } from "../controller/xhamster/xhamsterGet";
|
||||
import { searchXhamster } from "../controller/xhamster/xhamsterSearch";
|
||||
import { randomXhamster } from "../controller/xhamster/xhamsterRandom";
|
||||
import { relatedXhamster } from "../controller/xhamster/xhamsterGetRelated";
|
||||
|
||||
// YouPorn
|
||||
import { getYouporn } from "../controller/youporn/youpornGet";
|
||||
import { searchYouporn } from "../controller/youporn/youpornSearch";
|
||||
import { relatedYouporn } from "../controller/youporn/youpornGetRelated";
|
||||
import { randomYouporn } from "../controller/youporn/youpornRandom";
|
||||
|
||||
function scrapeRoutes() {
|
||||
const router = Router();
|
||||
|
||||
router.get("/pornhub/get", cors(), slow, limiter, getPornhub);
|
||||
router.get("/pornhub/search", cors(), slow, limiter, searchPornhub);
|
||||
router.get("/pornhub/random", cors(), slow, limiter, randomPornhub);
|
||||
router.get("/pornhub/related", cors(), slow, limiter, relatedPornhub);
|
||||
router.get("/xnxx/get", cors(), slow, limiter, getXnxx);
|
||||
router.get("/xnxx/search", cors(), slow, limiter, searchXnxx);
|
||||
router.get("/xnxx/related", cors(), slow, limiter, relatedXnxx);
|
||||
router.get("/xnxx/random", cors(), slow, limiter, randomXnxx);
|
||||
router.get("/redtube/get", cors(), slow, limiter, getRedtube);
|
||||
router.get("/redtube/search", cors(), slow, limiter, searchRedtube);
|
||||
router.get("/redtube/related", cors(), slow, limiter, relatedRedtube);
|
||||
router.get("/redtube/random", cors(), slow, limiter, randomRedtube);
|
||||
router.get("/xvideos/get", cors(), slow, limiter, getXvideos);
|
||||
router.get("/xvideos/search", cors(), slow, limiter, searchXvideos);
|
||||
router.get("/xvideos/random", cors(), slow, limiter, randomXvideos);
|
||||
router.get("/xvideos/related", cors(), slow, limiter, relatedXvideos);
|
||||
router.get("/xhamster/get", cors(), slow, limiter, getXhamster);
|
||||
router.get("/xhamster/search", cors(), slow, limiter, searchXhamster);
|
||||
router.get("/xhamster/random", cors(), slow, limiter, randomXhamster);
|
||||
router.get("/xhamster/related", cors(), slow, limiter, relatedXhamster);
|
||||
router.get("/youporn/get", cors(), slow, limiter, getYouporn);
|
||||
router.get("/youporn/search", cors(), slow, limiter, searchYouporn);
|
||||
router.get("/youporn/related", cors(), slow, limiter, relatedYouporn);
|
||||
router.get("/youporn/random", cors(), slow, limiter, randomYouporn);
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
export default scrapeRoutes;
|
||||
78
src/scraper/pornhub/pornhubGetController.ts
Normal file
78
src/scraper/pornhub/pornhubGetController.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { load } from "cheerio";
|
||||
import LustPress from "../../LustPress";
|
||||
import { IVideoData } from "../../interfaces";
|
||||
|
||||
const lust = new LustPress();
|
||||
|
||||
export async function scrapeContent(url: string) {
|
||||
try {
|
||||
const resolve = await lust.fetchBody(url);
|
||||
const $ = load(resolve);
|
||||
|
||||
class PornHub {
|
||||
link: string;
|
||||
id: string;
|
||||
title: string;
|
||||
image: string;
|
||||
duration: string;
|
||||
views: string;
|
||||
rating: string;
|
||||
videoInfo: string;
|
||||
upVote: string;
|
||||
downVote: string;
|
||||
video: string;
|
||||
tags: string[];
|
||||
models: string[];
|
||||
constructor() {
|
||||
this.link = $("link[rel='canonical']").attr("href") || "None";
|
||||
this.id = this.link.split("=")[1] || "None";
|
||||
this.title = $("meta[property='og:title']").attr("content") || "None";
|
||||
this.image = $("meta[property='og:image']").attr("content") || "None";
|
||||
//get <meta property="video:duration" content="
|
||||
this.duration = $("meta[property='video:duration']").attr("content") || "0";
|
||||
this.views = $("div.views > span.count").text() || "None";
|
||||
this.rating = $("div.ratingPercent > span.percent").text() || "None";
|
||||
this.videoInfo = $("div.videoInfo").text() || "None";
|
||||
this.upVote = $("span.votesUp").attr("data-rating") || "None";
|
||||
this.downVote = $("span.votesDown").attr("data-rating") || "None";
|
||||
this.video = $("meta[property='og:video:url']").attr("content") || "None";
|
||||
this.tags = $("div.video-info-row")
|
||||
.find("a")
|
||||
.map((i, el) => {
|
||||
return $(el).text();
|
||||
}).get();
|
||||
this.tags.shift();
|
||||
this.tags = this.tags.map((el) => lust.removeHtmlTagWithoutSpace(el));
|
||||
this.models = $("div.pornstarsWrapper.js-pornstarsWrapper")
|
||||
.find("a")
|
||||
.map((i, el) => {
|
||||
return $(el).attr("data-mxptext");
|
||||
}).get();
|
||||
}
|
||||
}
|
||||
|
||||
const ph = new PornHub();
|
||||
const data: IVideoData = {
|
||||
success: true,
|
||||
data: {
|
||||
title: lust.removeHtmlTagWithoutSpace(ph.title),
|
||||
id: ph.id,
|
||||
image: ph.image,
|
||||
duration: lust.secondToMinute(Number(ph.duration)),
|
||||
views: ph.views,
|
||||
rating: ph.rating,
|
||||
uploaded: ph.videoInfo,
|
||||
upvoted: ph.upVote,
|
||||
downvoted: ph.downVote,
|
||||
models: ph.models,
|
||||
tags: ph.tags.filter((el) => el !== "Suggest" && el !== " Suggest")
|
||||
},
|
||||
source: ph.link,
|
||||
assets: [ph.video, ph.image]
|
||||
};
|
||||
return data;
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
throw Error(e.message);
|
||||
}
|
||||
}
|
||||
59
src/scraper/pornhub/pornhubSearchController.ts
Normal file
59
src/scraper/pornhub/pornhubSearchController.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { load } from "cheerio";
|
||||
import LustPress from "../../LustPress";
|
||||
import c from "../../utils/options";
|
||||
import { ISearchVideoData } from "../../interfaces";
|
||||
|
||||
const lust = new LustPress();
|
||||
|
||||
export async function scrapeContent(url: string) {
|
||||
try {
|
||||
const res = await lust.fetchBody(url);
|
||||
const $ = load(res);
|
||||
|
||||
class PornhubSearch {
|
||||
search: object[];
|
||||
data: object;
|
||||
constructor() {
|
||||
this.search = $("div.wrap")
|
||||
.map((i, el) => {
|
||||
const link = $(el).find("a").attr("href");
|
||||
const id = link?.split("=")[1];
|
||||
const title = $(el).find("a").attr("title");
|
||||
const image = $(el).find("img").attr("src");
|
||||
const duration = $(el).find("var.duration").text();
|
||||
const views = $(el).find("div.videoDetailsBlock").find("span.views").text();
|
||||
return {
|
||||
link: `${c.PORNHUB}${link}`,
|
||||
id: id,
|
||||
title: title,
|
||||
image: image,
|
||||
duration: duration,
|
||||
views: views,
|
||||
video: `${c.PORNHUB}/embed/${id}`,
|
||||
};
|
||||
}).get();
|
||||
|
||||
this.data = this.search.filter((el: any) => {
|
||||
return el.link.includes("javascript:void(0)") === false && el.image?.startsWith("data:image") === false;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const ph = new PornhubSearch();
|
||||
if (ph.search.length === 0) throw Error("No result found");
|
||||
const data = ph.data as string[];
|
||||
const result: ISearchVideoData = {
|
||||
success: true,
|
||||
data: data,
|
||||
source: url,
|
||||
};
|
||||
return result;
|
||||
|
||||
|
||||
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
throw Error(e.message);
|
||||
}
|
||||
}
|
||||
79
src/scraper/redtube/redtubeGetController.ts
Normal file
79
src/scraper/redtube/redtubeGetController.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { load } from "cheerio";
|
||||
import LustPress from "../../LustPress";
|
||||
import { IVideoData } from "../../interfaces";
|
||||
|
||||
const lust = new LustPress();
|
||||
|
||||
export async function scrapeContent(url: string) {
|
||||
try {
|
||||
const resolve = await lust.fetchBody(url);
|
||||
const $ = load(resolve);
|
||||
|
||||
class RedTube {
|
||||
link: string;
|
||||
id: string;
|
||||
title: string;
|
||||
image: string;
|
||||
duration: string;
|
||||
views: string;
|
||||
rating: string;
|
||||
publish: string;
|
||||
upVote: string;
|
||||
downVote: null;
|
||||
video: string;
|
||||
tags: string[];
|
||||
models: string[];
|
||||
constructor() {
|
||||
this.link = $("link[rel='canonical']").attr("href") || "None";
|
||||
this.id = this.link.split("/")[3] || "None";
|
||||
this.title = $("meta[property='og:title']").attr("content") || "None";
|
||||
this.image = $("meta[property='og:image']").attr("content") || "None";
|
||||
this.duration = $("meta[property='og:video:duration']").attr("content") || "0";
|
||||
this.views = $("span.video_view_count").text() || "None";
|
||||
this.rating = $("div.rating_percent.js_rating_percent").attr("data-percent") + "%" || "None";
|
||||
this.publish = $("span.video-infobox-date-added").text().replace("Published on ", "") || "None";
|
||||
this.upVote = this.rating;
|
||||
this.downVote = null;
|
||||
this.video = $("meta[name='twitter:player']").attr("content") || "None";
|
||||
this.tags = $("a.item.video_carousel_item.video_carousel_category, a.item.video_carousel_item.video_carousel_tag")
|
||||
.map((i, el) => {
|
||||
return $(el).text();
|
||||
}).get();
|
||||
this.tags = this.tags.map((el) => lust.removeHtmlTagWithoutSpace(el));
|
||||
this.models = $("div.pornstar-name.pornstarPopupWrapper")
|
||||
.find("a")
|
||||
.map((i, el) => {
|
||||
return $(el).text();
|
||||
}
|
||||
).get();
|
||||
this.models = this.models.map((el) => lust.removeHtmlTagWithoutSpace(el));
|
||||
this.models = this.models.filter((el) => !el.includes("Subscribe") && !el.includes("Rank"))
|
||||
.filter((el, i, arr) => arr.indexOf(el) === i);
|
||||
}
|
||||
}
|
||||
|
||||
const red = new RedTube();
|
||||
const data: IVideoData = {
|
||||
success: true,
|
||||
data: {
|
||||
title: lust.removeHtmlTagWithoutSpace(red.title),
|
||||
id: red.id,
|
||||
image: red.image,
|
||||
duration: lust.secondToMinute(Number(red.duration)),
|
||||
views: red.views,
|
||||
rating: red.rating,
|
||||
uploaded: red.publish,
|
||||
upvoted: red.upVote,
|
||||
downvoted: red.downVote,
|
||||
models: red.models,
|
||||
tags: red.tags
|
||||
},
|
||||
source: red.link,
|
||||
assets: [red.video, red.image]
|
||||
};
|
||||
return data;
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
throw Error(e.message);
|
||||
}
|
||||
}
|
||||
72
src/scraper/redtube/redtubeSearchController.ts
Normal file
72
src/scraper/redtube/redtubeSearchController.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { load } from "cheerio";
|
||||
import LustPress from "../../LustPress";
|
||||
import c from "../../utils/options";
|
||||
import { ISearchVideoData } from "../../interfaces";
|
||||
|
||||
const lust = new LustPress();
|
||||
|
||||
export async function scrapeContent(url: string) {
|
||||
try {
|
||||
const res = await lust.fetchBody(url);
|
||||
const $ = load(res);
|
||||
|
||||
class RedTubeSearch {
|
||||
views: string[];
|
||||
search: object[];
|
||||
data: object;
|
||||
constructor() {
|
||||
this.views = $("span.video_count")
|
||||
.map((i, el) => {
|
||||
const views = $(el).text();
|
||||
return views;
|
||||
}).get();
|
||||
this.search = $("a.video_link")
|
||||
.map((i, el) => {
|
||||
const link = $(el).attr("href");
|
||||
const id = link?.split("/")[1];
|
||||
const title = $(el).find("img").attr("alt");
|
||||
const image = $(el).find("img").attr("data-src");
|
||||
const duration = $(el).find("span.duration").text().split(" ").map((el: string) => {
|
||||
return el.replace(/[^0-9:]/g, "");
|
||||
}).filter((el: string) => {
|
||||
return el.includes(":");
|
||||
}).join(" ");
|
||||
|
||||
return {
|
||||
link: `${c.REDTUBE}${link}`,
|
||||
id: id,
|
||||
title: title,
|
||||
image: image,
|
||||
duration: duration,
|
||||
views: this.views[i],
|
||||
video: `https://embed.redtube.com/?id=${id}`,
|
||||
|
||||
};
|
||||
}).get();
|
||||
|
||||
|
||||
|
||||
|
||||
this.data = this.search.filter((el: any) => {
|
||||
return el.link.includes("javascript:void(0)") === false && el.image?.startsWith("data:image") === false;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const red = new RedTubeSearch();
|
||||
|
||||
if (red.search.length === 0) throw Error("No result found");
|
||||
const data = red.data as string[];
|
||||
const result: ISearchVideoData = {
|
||||
success: true,
|
||||
data: data,
|
||||
source: url,
|
||||
};
|
||||
return result;
|
||||
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
throw Error(e.message);
|
||||
}
|
||||
}
|
||||
83
src/scraper/xhamster/xhamsterGetController.ts
Normal file
83
src/scraper/xhamster/xhamsterGetController.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { load } from "cheerio";
|
||||
import LustPress from "../../LustPress";
|
||||
import { IVideoData } from "../../interfaces";
|
||||
|
||||
const lust = new LustPress();
|
||||
|
||||
export async function scrapeContent(url: string) {
|
||||
try {
|
||||
const resolve = await lust.fetchBody(url);
|
||||
const $ = load(resolve);
|
||||
|
||||
class Xhamster {
|
||||
link: string;
|
||||
id: string;
|
||||
title: string;
|
||||
image: string;
|
||||
duration: any;
|
||||
views: string;
|
||||
rating: string;
|
||||
publish: string;
|
||||
upVote: string;
|
||||
downVote: string;
|
||||
video: string;
|
||||
tags: string[];
|
||||
models: string[];
|
||||
constructor() {
|
||||
this.link = $("link[rel='canonical']").attr("href") || "None";
|
||||
this.id = this.link.split("/")[3] + "/" + this.link.split("/")[4] || "None";
|
||||
this.title = $("meta[property='og:title']").attr("content") || "None";
|
||||
this.image = $("meta[property='og:image']").attr("content") || "None";
|
||||
this.duration = $("script#initials-script").html() || "None";
|
||||
//remove window.initials={ and };
|
||||
this.duration = this.duration.replace("window.initials=", "");
|
||||
this.duration = this.duration.replace(/;/g, "");
|
||||
this.duration = JSON.parse(this.duration);
|
||||
this.duration = this.duration.videoModel.duration || "None";
|
||||
this.views = $("div.header-icons").find("span").first().text() || "None";
|
||||
this.rating = $("div.header-icons").find("span").eq(1).text() || "None";
|
||||
this.publish = $("div.entity-info-container__date").attr("data-tooltip") || "None";
|
||||
this.upVote = $("div.rb-new__info").text().split("/")[0].trim() || "None";
|
||||
this.downVote = $("div.rb-new__info").text().split("/")[1].trim() || "None";
|
||||
this.video = "https://xheve2.com/embed/" + this.link.split("-").pop() || "None";
|
||||
this.tags = $("a.video-tag")
|
||||
.map((i, el) => {
|
||||
return $(el).text();
|
||||
}).get();
|
||||
this.tags = this.tags.map((el) => lust.removeHtmlTagWithoutSpace(el));
|
||||
this.models = $("a.video-tag")
|
||||
.map((i, el) => {
|
||||
return $(el).attr("href");
|
||||
}
|
||||
).get();
|
||||
this.models = this.models.filter((el) => el.startsWith("https://xheve2.com/pornstars/"));
|
||||
this.models = this.models.map((el) => el.replace("https://xheve2.com/pornstars/", ""));
|
||||
}
|
||||
}
|
||||
|
||||
const xh = new Xhamster();
|
||||
const data: IVideoData = {
|
||||
success: true,
|
||||
data: {
|
||||
title: lust.removeHtmlTagWithoutSpace(xh.title),
|
||||
id: xh.id,
|
||||
image: xh.image,
|
||||
duration: lust.secondToMinute(Number(xh.duration)),
|
||||
views: xh.views,
|
||||
rating: xh.rating,
|
||||
uploaded: xh.publish,
|
||||
upvoted: xh.upVote,
|
||||
downvoted: xh.downVote,
|
||||
models: xh.models,
|
||||
tags: xh.tags
|
||||
},
|
||||
source: xh.link,
|
||||
assets: [xh.video, xh.image]
|
||||
};
|
||||
return data;
|
||||
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
throw Error(e.message);
|
||||
}
|
||||
}
|
||||
56
src/scraper/xhamster/xhamsterSearchController.ts
Normal file
56
src/scraper/xhamster/xhamsterSearchController.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { load } from "cheerio";
|
||||
import LustPress from "../../LustPress";
|
||||
import { ISearchVideoData } from "../../interfaces";
|
||||
|
||||
const lust = new LustPress();
|
||||
|
||||
export async function scrapeContent(url: string) {
|
||||
try {
|
||||
const res = await lust.fetchBody(url);
|
||||
const $ = load(res);
|
||||
|
||||
class XhamsterSearch {
|
||||
search: any;
|
||||
constructor() {
|
||||
const views = $("div.video-thumb-views")
|
||||
.map((i, el) => {
|
||||
const views = $(el).text();
|
||||
return views;
|
||||
}).get();
|
||||
const duration = $("span[data-role='video-duration']")
|
||||
.map((i, el) => {
|
||||
const duration = $(el).text();
|
||||
return duration;
|
||||
}).get();
|
||||
this.search = $("a.video-thumb__image-container")
|
||||
.map((i, el) => {
|
||||
const link = $(el).attr("href");
|
||||
|
||||
return {
|
||||
link: `${link}`,
|
||||
id: link?.split("/")[3] + "/" + link?.split("/")[4],
|
||||
title: $(el).find("img").attr("alt"),
|
||||
image: $(el).find("img").attr("src"),
|
||||
duration: duration[i],
|
||||
views: views[i],
|
||||
video: $(el).attr("data-previewvideo"),
|
||||
};
|
||||
}).get();
|
||||
}
|
||||
}
|
||||
|
||||
const xh = new XhamsterSearch();
|
||||
if (xh.search.length === 0) throw Error("No result found");
|
||||
const data = xh.search as unknown as string[];
|
||||
const result: ISearchVideoData = {
|
||||
success: true,
|
||||
data: data,
|
||||
source: url,
|
||||
};
|
||||
return result;
|
||||
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
throw Error(e.message);
|
||||
}
|
||||
}
|
||||
102
src/scraper/xnxx/xnxxGetController.ts
Normal file
102
src/scraper/xnxx/xnxxGetController.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import { load } from "cheerio";
|
||||
import LustPress from "../../LustPress";
|
||||
import { IVideoData } from "../../interfaces";
|
||||
|
||||
const lust = new LustPress();
|
||||
|
||||
export async function scrapeContent(url: string) {
|
||||
try {
|
||||
console.log(url);
|
||||
const resolve = await lust.fetchBody(url);
|
||||
const $ = load(resolve);
|
||||
|
||||
class Xnxx {
|
||||
link: string;
|
||||
id: string;
|
||||
title: string;
|
||||
image: string;
|
||||
duration: string;
|
||||
views: string;
|
||||
uploaded: string;
|
||||
action: string[];
|
||||
upVote: string;
|
||||
downVote: string;
|
||||
favVote: string;
|
||||
tags: string[];
|
||||
models: string[];
|
||||
thumbnail: string;
|
||||
bigimg: string;
|
||||
video: string;
|
||||
constructor() {
|
||||
const thumb = $("script")
|
||||
.map((i, el) => {
|
||||
return $(el).text();
|
||||
}).get()
|
||||
.filter((el) => el.includes("html5player.setThumbSlideBig"))[0] || "None";
|
||||
|
||||
this.thumbnail = thumb.match(/html5player.setThumbSlideBig\((.*?)\)/)?.[1] || "None";
|
||||
this.bigimg = thumb.match(/html5player.setThumbUrl169\((.*?)\)/)?.[1] || "None";
|
||||
this.video = thumb.match(/html5player.setVideoUrlHigh\((.*?)\)/)?.[1] || "None";
|
||||
this.link = $("meta[property='og:url']").attr("content") || "None";
|
||||
this.id = this.link.split(".com/")[1] || "None";
|
||||
this.title = $("meta[property='og:title']").attr("content") || "None";
|
||||
this.image = $("meta[property='og:image']").attr("content") || "None";
|
||||
this.duration = $("meta[property='og:duration']").attr("content") || "None";
|
||||
this.views = $("span.metadata").text() || "None";
|
||||
this.views = this.views.split("-")[2] || "None";
|
||||
this.uploaded = $("script[type='application/ld+json']").text() || "None";
|
||||
this.uploaded = this.uploaded
|
||||
.split("uploadDate")[1]
|
||||
.split("}")[0]
|
||||
.split(":")[1]
|
||||
.replace(/"/g, "")
|
||||
.replace(/,/g, "") || "None";
|
||||
this.action = $("span.vote-actions")
|
||||
.find("span.value")
|
||||
.map((i, el) => {
|
||||
return $(el).text();
|
||||
}).get();
|
||||
|
||||
this.upVote = this.action[0] || "None";
|
||||
this.downVote = this.action[1] || "None";
|
||||
this.favVote = $("span.rating-box.value").text() || "None";
|
||||
this.models = $("a.is-pornstar")
|
||||
.map((i, el) => {
|
||||
return $(el).text();
|
||||
}).get();
|
||||
this.tags = $("div.metadata-row.video-tags")
|
||||
.find("a")
|
||||
.map((i, el) => {
|
||||
return $(el).text();
|
||||
}).get();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const x = new Xnxx();
|
||||
|
||||
const data: IVideoData = {
|
||||
success: true,
|
||||
data: {
|
||||
title: lust.removeHtmlTagWithoutSpace(x.title),
|
||||
id: x.id,
|
||||
image: x.image,
|
||||
duration: lust.secondToMinute(Number(x.duration)),
|
||||
views: lust.removeHtmlTag(x.views),
|
||||
rating: x.favVote,
|
||||
uploaded: x.uploaded.trim(),
|
||||
upvoted: x.upVote,
|
||||
downvoted: x.downVote,
|
||||
models: x.models,
|
||||
tags: x.tags.filter((el) => el !== "Edit tags and models")
|
||||
},
|
||||
source: x.link,
|
||||
assets: lust.removeAllSingleQuoteOnArray([x.thumbnail, x.bigimg, x.video])
|
||||
};
|
||||
return data;
|
||||
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
throw Error(e.message);
|
||||
}
|
||||
}
|
||||
55
src/scraper/xnxx/xnxxGetRelatedController.ts
Normal file
55
src/scraper/xnxx/xnxxGetRelatedController.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { load } from "cheerio";
|
||||
import LustPress from "../../LustPress";
|
||||
import c from "../../utils/options";
|
||||
import { ISearchVideoData } from "../../interfaces";
|
||||
|
||||
const lust = new LustPress();
|
||||
|
||||
export async function scrapeContent(url: string) {
|
||||
try {
|
||||
const res = await lust.fetchBody(url);
|
||||
const $ = load(res);
|
||||
|
||||
class PornhubSearch {
|
||||
search: object[];
|
||||
data: object;
|
||||
constructor() {
|
||||
// in <div id="video-player-bg"> get <script>var video_related=
|
||||
this.search = $("div#video-player-bg")
|
||||
.map((i, el) => {
|
||||
const script = $(el).find("script").html();
|
||||
const video_related = script?.split("var video_related=")[1];
|
||||
//stop and replace everything after the last ];
|
||||
const badJson = video_related?.split("];")[0] + "]";
|
||||
const actualResult = JSON.parse(String(badJson));
|
||||
const result = actualResult.map((el: any) => {
|
||||
return {
|
||||
link: `${c.XNXX}${el.u}`,
|
||||
id: el.u,
|
||||
title: el.t,
|
||||
image: el.i,
|
||||
duration: el.d,
|
||||
views: `${el.n}, ${el.r}`,
|
||||
video: null
|
||||
};
|
||||
});
|
||||
return result;
|
||||
}).get();
|
||||
}
|
||||
}
|
||||
|
||||
const x = new PornhubSearch();
|
||||
if (x.search.length === 0) throw Error("No result found");
|
||||
const data = x.search as unknown as string[];
|
||||
const result: ISearchVideoData = {
|
||||
success: true,
|
||||
data: data,
|
||||
source: url,
|
||||
};
|
||||
return result;
|
||||
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
throw Error(e.message);
|
||||
}
|
||||
}
|
||||
49
src/scraper/xnxx/xnxxSearchController.ts
Normal file
49
src/scraper/xnxx/xnxxSearchController.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { load } from "cheerio";
|
||||
import LustPress from "../../LustPress";
|
||||
import c from "../../utils/options";
|
||||
import { ISearchVideoData } from "../../interfaces";
|
||||
|
||||
const lust = new LustPress();
|
||||
|
||||
export async function scrapeContent(url: string) {
|
||||
try {
|
||||
const res = await lust.fetchBody(url);
|
||||
const $ = load(res);
|
||||
|
||||
class PornhubSearch {
|
||||
search: object[];
|
||||
constructor() {
|
||||
this.search = $("div.mozaique > div")
|
||||
.map((i, el) => {
|
||||
return {
|
||||
link: `${c.XNXX}${$(el).find("a").attr("href")}`,
|
||||
id: $(el).find("a").attr("href"),
|
||||
title: $(el).find("div.thumb-under").text().split("\n")
|
||||
.map((el) => el.trim()).filter((el) => el !== "")[0],
|
||||
image: $(el).find("img").attr("data-src"),
|
||||
duration: $(el).find("div.thumb-under").text().split("\n")
|
||||
.map((el) => el.trim()).filter((el) => el !== "")[2],
|
||||
rating: $(el).find("div.thumb-under").text().split("\n")
|
||||
.map((el) => el.trim()).filter((el) => el !== "")[1],
|
||||
video: null
|
||||
|
||||
};
|
||||
}).get();
|
||||
}
|
||||
}
|
||||
|
||||
const x = new PornhubSearch();
|
||||
if (x.search.length === 0) throw Error("No result found");
|
||||
const data = x.search as unknown as string[];
|
||||
const result: ISearchVideoData = {
|
||||
success: true,
|
||||
data: data,
|
||||
source: url,
|
||||
};
|
||||
return result;
|
||||
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
throw Error(e.message);
|
||||
}
|
||||
}
|
||||
92
src/scraper/xvideos/xvideosGetController.ts
Normal file
92
src/scraper/xvideos/xvideosGetController.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { load } from "cheerio";
|
||||
import LustPress from "../../LustPress";
|
||||
import { IVideoData } from "../../interfaces";
|
||||
|
||||
const lust = new LustPress();
|
||||
|
||||
export async function scrapeContent(url: string) {
|
||||
try {
|
||||
const resolve = await lust.fetchBody(url);
|
||||
const $ = load(resolve);
|
||||
|
||||
class Xvideos {
|
||||
link: string;
|
||||
id: string;
|
||||
title: string;
|
||||
image: string;
|
||||
duration: string;
|
||||
views: string;
|
||||
rating: string;
|
||||
publish: string;
|
||||
upVote: string;
|
||||
downVote: string;
|
||||
video: string;
|
||||
tags: string[];
|
||||
models: string[];
|
||||
thumbnail: string;
|
||||
bigimg: string;
|
||||
constructor() {
|
||||
this.link = $("meta[property='og:url']").attr("content") || "None";
|
||||
this.id = this.link.split("/")[3] + "/" + this.link.split("/")[4] || "None";
|
||||
this.title = $("meta[property='og:title']").attr("content") || "None";
|
||||
this.image = $("meta[property='og:image']").attr("content") || "None";
|
||||
this.duration = $("meta[property='og:duration']").attr("content") || "0";
|
||||
this.views = $("div#v-views").find("strong.mobile-hide").text() || "None";
|
||||
this.rating = $("span.rating-total-txt").text() || "None";
|
||||
this.publish = $("script[type='application/ld+json']").text() || "None";
|
||||
this.publish = this.publish
|
||||
.split("uploadDate")[1]
|
||||
.split("}")[0]
|
||||
.split(":")[1]
|
||||
.replace(/"/g, "")
|
||||
.replace(/,/g, "") || "None";
|
||||
this.upVote = $("span.rating-good-nbr").text() || "None";
|
||||
this.downVote = $("span.rating-bad-nbr").text() || "None";
|
||||
const thumb = $("script")
|
||||
.map((i, el) => {
|
||||
return $(el).text();
|
||||
}).get()
|
||||
.filter((el) => el.includes("html5player.setThumbSlideBig"))[0] || "None";
|
||||
this.thumbnail = thumb.match(/html5player.setThumbSlideBig\((.*?)\)/)?.[1] || "None";
|
||||
this.bigimg = thumb.match(/html5player.setThumbUrl169\((.*?)\)/)?.[1] || "None";
|
||||
this.video = thumb.match(/html5player.setVideoUrlHigh\((.*?)\)/)?.[1] || "None";
|
||||
this.tags = $("a.is-keyword.btn.btn-default")
|
||||
.map((i, el) => {
|
||||
return $(el).text();
|
||||
}).get();
|
||||
this.models = $("li.model")
|
||||
.map((i, el) => {
|
||||
return $(el).find("a").attr("href") || "None";
|
||||
}
|
||||
).get();
|
||||
this.models = this.models.map((el) => el.split("/")[2]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const xv = new Xvideos();
|
||||
const data: IVideoData = {
|
||||
success: true,
|
||||
data: {
|
||||
title: lust.removeHtmlTagWithoutSpace(xv.title),
|
||||
id: xv.id,
|
||||
image: xv.image,
|
||||
duration: lust.secondToMinute(Number(xv.duration)),
|
||||
views: lust.removeHtmlTag(xv.views),
|
||||
rating: xv.rating,
|
||||
uploaded: xv.publish,
|
||||
upvoted: xv.upVote,
|
||||
downvoted: xv.downVote,
|
||||
models: xv.models,
|
||||
tags: xv.tags,
|
||||
},
|
||||
source: xv.link,
|
||||
assets: lust.removeAllSingleQuoteOnArray([xv.thumbnail, xv.bigimg, xv.video])
|
||||
};
|
||||
return data;
|
||||
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
throw Error(e.message);
|
||||
}
|
||||
}
|
||||
54
src/scraper/xvideos/xvideosGetRelatedController.ts
Normal file
54
src/scraper/xvideos/xvideosGetRelatedController.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { load } from "cheerio";
|
||||
import LustPress from "../../LustPress";
|
||||
import c from "../../utils/options";
|
||||
import { ISearchVideoData } from "../../interfaces";
|
||||
|
||||
const lust = new LustPress();
|
||||
|
||||
export async function scrapeContent(url: string) {
|
||||
try {
|
||||
const res = await lust.fetchBody(url);
|
||||
const $ = load(res);
|
||||
console.log(url);
|
||||
|
||||
class XvideosSearch {
|
||||
search: object[];
|
||||
data: object;
|
||||
constructor() {
|
||||
this.search = $("div#video-player-bg")
|
||||
.map((i, el) => {
|
||||
const script = $(el).find("script").html();
|
||||
const video_related = script?.split("var video_related=")[1];
|
||||
const badJson = video_related?.split("];")[0] + "]";
|
||||
const actualResult = JSON.parse(String(badJson));
|
||||
const result = actualResult.map((el: any) => {
|
||||
return {
|
||||
link: `${c.XVIDEOS}${el.u}`,
|
||||
id: el.u,
|
||||
title: el.t,
|
||||
image: el.i,
|
||||
duration: el.d,
|
||||
views: `${el.n}, ${el.r}`,
|
||||
video: null
|
||||
};
|
||||
});
|
||||
return result;
|
||||
}).get();
|
||||
}
|
||||
}
|
||||
|
||||
const x = new XvideosSearch();
|
||||
if (x.search.length === 0) throw Error("No result found");
|
||||
const data = x.search as unknown as string[];
|
||||
const result: ISearchVideoData = {
|
||||
success: true,
|
||||
data: data,
|
||||
source: url,
|
||||
};
|
||||
return result;
|
||||
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
throw Error(e.message);
|
||||
}
|
||||
}
|
||||
66
src/scraper/xvideos/xvideosSearchController.ts
Normal file
66
src/scraper/xvideos/xvideosSearchController.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { load } from "cheerio";
|
||||
import LustPress from "../../LustPress";
|
||||
import c from "../../utils/options";
|
||||
import { ISearchVideoData } from "../../interfaces";
|
||||
|
||||
const lust = new LustPress();
|
||||
|
||||
export async function scrapeContent(url: string) {
|
||||
try {
|
||||
console.log(url);
|
||||
const res = await lust.fetchBody(url);
|
||||
const $ = load(res);
|
||||
|
||||
class XvideosSearch {
|
||||
search: object[];
|
||||
constructor() {
|
||||
const data = $("div.thumb-under")
|
||||
.map((i, el) => {
|
||||
return {
|
||||
title: $(el).find("a").attr("title"),
|
||||
duration: $(el).find("span.duration")
|
||||
.map((i, el) => {
|
||||
return $(el).text();
|
||||
}).get()[0],
|
||||
};
|
||||
}).get();
|
||||
this.search = $("div.mozaique.cust-nb-cols")
|
||||
.find("div.thumb")
|
||||
.map((i, el) => {
|
||||
return {
|
||||
link: `${c.XVIDEOS}${$(el).find("a").attr("href")}` || "None",
|
||||
id: $(el).find("a").attr("href") || "None",
|
||||
image: $(el).find("img").attr("data-src") || "None",
|
||||
title: data[i].title || "None",
|
||||
duration: data[i].duration === data[i + 1]?.duration
|
||||
? ""
|
||||
: data[i].duration || "None",
|
||||
rating: null,
|
||||
video: null
|
||||
};
|
||||
}).get();
|
||||
|
||||
this.search = this.search.filter((el: any) => {
|
||||
return !el.id.includes("THUMBNUM");
|
||||
});
|
||||
this.search = this.search.filter((el: any) => {
|
||||
return el.id.includes("/video");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const xv = new XvideosSearch();
|
||||
if (xv.search.length === 0) throw Error("No result found");
|
||||
const data = xv.search as unknown as string[];
|
||||
const result: ISearchVideoData = {
|
||||
success: true,
|
||||
data: data,
|
||||
source: url,
|
||||
};
|
||||
return result;
|
||||
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
throw Error(e.message);
|
||||
}
|
||||
}
|
||||
73
src/scraper/youporn/youpornGetController.ts
Normal file
73
src/scraper/youporn/youpornGetController.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { load } from "cheerio";
|
||||
import LustPress from "../../LustPress";
|
||||
import { IVideoData } from "../../interfaces";
|
||||
|
||||
const lust = new LustPress();
|
||||
|
||||
export async function scrapeContent(url: string) {
|
||||
try {
|
||||
const resolve = await lust.fetchBody(url);
|
||||
const $ = load(resolve);
|
||||
|
||||
class YouPorn {
|
||||
link: string;
|
||||
id: string;
|
||||
title: string;
|
||||
image: string;
|
||||
duration: string;
|
||||
views: string;
|
||||
rating: string;
|
||||
publish: string;
|
||||
upVote: string;
|
||||
downVote: null;
|
||||
video: string;
|
||||
tags: string[];
|
||||
models: string[];
|
||||
constructor() {
|
||||
this.link = $("link[rel='canonical']").attr("href") || "None";
|
||||
this.id = this.link.replace("https://www.youporn.com/watch/", "") || "None";
|
||||
this.title = $("meta[property='og:title']").attr("content") || "None";
|
||||
this.image = $("meta[property='og:image']").attr("content") || "None";
|
||||
this.duration = $("meta[property='video:duration']").attr("content") || "0";
|
||||
this.views = $("div.feature.infoValueBlock").find("div[data-value]").attr("data-value") || "0";
|
||||
this.rating = $("div.feature").find("span").text().replace(/[^0-9.,%]/g, "") || "0";
|
||||
this.publish = $("div.video-uploaded").find("span").text() || "None";
|
||||
this.upVote = this.views;
|
||||
this.downVote = null;
|
||||
this.video = `https://www.youporn.com/embed/${this.id}`;
|
||||
this.tags = $("a[data-espnode='category_tag'], a[data-espnode='porntag_tag']")
|
||||
.map((i, el) => {
|
||||
return $(el).text();
|
||||
}).get();
|
||||
this.models = $("a[data-espnode='pornstar_tag']")
|
||||
.map((i, el) => {
|
||||
return $(el).text();
|
||||
}).get();
|
||||
}
|
||||
}
|
||||
|
||||
const yp = new YouPorn();
|
||||
const data: IVideoData = {
|
||||
success: true,
|
||||
data: {
|
||||
title: lust.removeHtmlTagWithoutSpace(yp.title),
|
||||
id: yp.id,
|
||||
image: yp.image,
|
||||
duration: lust.secondToMinute(Number(yp.duration)),
|
||||
views: yp.views,
|
||||
rating: yp.rating,
|
||||
uploaded: yp.publish,
|
||||
upvoted: yp.upVote,
|
||||
downvoted: yp.downVote,
|
||||
models: yp.models,
|
||||
tags: yp.tags
|
||||
},
|
||||
source: yp.link,
|
||||
assets: [yp.video, yp.image]
|
||||
};
|
||||
return data;
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
throw Error(e.message);
|
||||
}
|
||||
}
|
||||
53
src/scraper/youporn/youpornSearchController.ts
Normal file
53
src/scraper/youporn/youpornSearchController.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { load } from "cheerio";
|
||||
import LustPress from "../../LustPress";
|
||||
import c from "../../utils/options";
|
||||
import { ISearchVideoData } from "../../interfaces";
|
||||
|
||||
const lust = new LustPress();
|
||||
|
||||
export async function scrapeContent(url: string) {
|
||||
try {
|
||||
const res = await lust.fetchBody(url);
|
||||
const $ = load(res);
|
||||
|
||||
class YouPornSearch {
|
||||
dur: string[];
|
||||
search: object[];
|
||||
constructor() {
|
||||
this.dur = $("div.video-duration").map((i, el) => {
|
||||
return $(el).text();
|
||||
}).get();
|
||||
this.search = $("a[href^='/watch/']")
|
||||
.map((i, el) => {
|
||||
const link = $(el).attr("href");
|
||||
const id = `${link}`.split("/")[2] + "/" + `${link}`.split("/")[3];
|
||||
const title = $(el).find("div.video-box-title").text();
|
||||
const image = $(el).find("img").attr("data-thumbnail");
|
||||
return {
|
||||
link: `${c.YOUPORN}${link}`,
|
||||
id: id,
|
||||
title: lust.removeHtmlTagWithoutSpace(title),
|
||||
image: image,
|
||||
duration: this.dur[i],
|
||||
views: null,
|
||||
video: `https://www.youporn.com/embed/${id}`,
|
||||
};
|
||||
}).get();
|
||||
}
|
||||
}
|
||||
|
||||
const yp = new YouPornSearch();
|
||||
if (yp.search.length === 0) throw Error("No result found");
|
||||
const data = yp.search as unknown as string[];
|
||||
const result: ISearchVideoData = {
|
||||
success: true,
|
||||
data: data,
|
||||
source: url,
|
||||
};
|
||||
return result;
|
||||
|
||||
} catch (err) {
|
||||
const e = err as Error;
|
||||
throw Error(e.message);
|
||||
}
|
||||
}
|
||||
17
src/utils/limit-options.ts
Normal file
17
src/utils/limit-options.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import rateLimit from "express-rate-limit";
|
||||
import slowDown from "express-slow-down";
|
||||
|
||||
const limiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000,
|
||||
max: 50,
|
||||
message: "Too nasty, please slow down"
|
||||
});
|
||||
|
||||
const slow = slowDown({
|
||||
delayAfter: 50,
|
||||
windowMs: 15 * 60 * 1000,
|
||||
delayMs: 1000,
|
||||
maxDelayMs: 20000,
|
||||
});
|
||||
|
||||
export { limiter, slow };
|
||||
8
src/utils/logger.ts
Normal file
8
src/utils/logger.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import pino from "pino";
|
||||
|
||||
export const logger = pino({
|
||||
level: "info",
|
||||
transport: {
|
||||
target: "pino-pretty"
|
||||
},
|
||||
});
|
||||
39
src/utils/modifier.ts
Normal file
39
src/utils/modifier.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Auto space on url
|
||||
* @param str the string to be spaced
|
||||
* @returns string
|
||||
*/
|
||||
export function spacer(str: string) {
|
||||
return str.replace(/\s/g, "+");
|
||||
}
|
||||
|
||||
/**
|
||||
* Error handler
|
||||
* @param success when success is false, it will return error
|
||||
* @param message error message
|
||||
* @returns object
|
||||
*/
|
||||
export function maybeError(success: boolean, message: string) {
|
||||
return { success, message };
|
||||
}
|
||||
|
||||
export function timeAgo(input: Date) {
|
||||
const date = new Date(input);
|
||||
const formatter: any = new Intl.RelativeTimeFormat("en");
|
||||
const ranges: { [key: string]: number } = {
|
||||
years: 3600 * 24 * 365,
|
||||
months: 3600 * 24 * 30,
|
||||
weeks: 3600 * 24 * 7,
|
||||
days: 3600 * 24,
|
||||
hours: 3600,
|
||||
minutes: 60,
|
||||
seconds: 1
|
||||
};
|
||||
const secondsElapsed = (date.getTime() - Date.now()) / 1000;
|
||||
for (const key in ranges) {
|
||||
if (ranges[key] < Math.abs(secondsElapsed)) {
|
||||
const delta = secondsElapsed / ranges[key];
|
||||
return formatter.format(Math.round(delta), key);
|
||||
}
|
||||
}
|
||||
}
|
||||
9
src/utils/options.ts
Normal file
9
src/utils/options.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export default {
|
||||
PORNHUB: "https://www.pornhub.com",
|
||||
XNXX: "https://www.xnxx.com",
|
||||
REDTUBE: "https://www.redtube.com",
|
||||
XVIDEOS: "https://www.xvideos.com",
|
||||
XHAMSTER: "https://xheve2.com",
|
||||
YOUPORN: "https://www.youporn.com",
|
||||
JAVHD: "https://javhd.today"
|
||||
};
|
||||
20
test/mock.ts
Normal file
20
test/mock.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import p from "phin";
|
||||
import { load } from "cheerio";
|
||||
|
||||
const url = "https://www.pornhub.com/view_video.php?viewkey=ph63c4e1dc48fe7";
|
||||
|
||||
async function test() {
|
||||
const res = await p({
|
||||
url: url,
|
||||
"headers": {
|
||||
"User-Agent": process.env.USER_AGENT || "lustpress/1.6.0 Node.js/16.9.1",
|
||||
},
|
||||
});
|
||||
|
||||
const $ = load(res.body);
|
||||
const title = $("meta[property='og:title']").attr("content");
|
||||
console.log(title);
|
||||
console.log(res.statusCode);
|
||||
}
|
||||
|
||||
test().catch(console.error);
|
||||
14
test/test.ts
Normal file
14
test/test.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import c from "../src/utils/options";
|
||||
import p from "phin";
|
||||
|
||||
for (const url of
|
||||
[c.PORNHUB, c.XNXX, c.REDTUBE, c.XVIDEOS, c.XHAMSTER, c.YOUPORN]) {
|
||||
p({ url }).then(res => {
|
||||
if (res.statusCode !== 200) {
|
||||
console.log(`${url} is not available, status code: ${res.statusCode}, check the sites or your own user-agent`);
|
||||
}
|
||||
else {
|
||||
console.log(`${url} is available, can be scraped`);
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user