hotdreams-ts
v0.1.1
Published
TypeScript/Node.js port of hotdreams — scraping video metadata (title, duration, thumbnail, preview, views, rating...) from several adult video sites.
Maintainers
Readme
Table of contents
- Installation
- Quickstart
- Custom User-Agent and proxies
- Custom cookies folder
- Direct video URL
- Supported sites
- API shape
- Differences from the Python package
- License
Installation
npm install hotdreams-tsRequires Node.js 18+.
Quickstart
import { pornhub } from 'hotdreams-ts';
const result = await pornhub.searchPorn('lorena aquino');
console.log(result.videoCount, 'videos found');
for (const video of result.videos) {
console.log(video.title, video.url);
}Every site module exposes the same shape: searchPorn(query, pageLimit?, pageNumber?) returns a
DataVideos object (.videos, .videoCount, .pageCount, ...), and most modules also expose
getVideoEmbed(url) for fetching a single video's embed data and suggestions. Both are async
and return a Promise.
Custom User-Agent and proxies
By default every site module ships with working headers. If you need to override the User-Agent (e.g. to look like a different browser) or route requests through a proxy (e.g. to avoid IP-based rate limiting), call these once, before or after importing the site modules you plan to use — they apply to every site the lib supports:
import { setUserAgent, setProxies, clearProxies, pornhub } from 'hotdreams-ts';
setUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36');
setProxies({ http: 'http://user:pass@host:port', https: 'http://user:pass@host:port' });
// ... use the lib normally
await pornhub.searchPorn('lorena aquino');
// to stop using the proxy
clearProxies();setUserAgent/setProxies retroactively update sessions already created, and also apply to any
session created afterwards, so import order doesn't matter.
Custom cookies folder
Every site module persists the cookies it picks up (e.g. anti-bot/session cookies) to a small
JSON file per site, and reuses them on the next call. By default those files are written to a
hotdreams/cookies folder inside the OS temp dir, so nothing is left behind in your project
directory. Call setCookiesDir() to store them somewhere else instead:
import { setCookiesDir, eporner } from 'hotdreams-ts';
setCookiesDir('/home/me/.hotdreams_cookies');
// ... use the lib normally
await eporner.searchPorn('lorena aquino');Direct video URL
getDirectUrl(url) resolves a video page URL to the direct, playable/downloadable video file
URL, by shelling out to the yt-dlp CLI. Unlike the Python
package (which depends on the yt-dlp Python package), this needs the yt-dlp binary
installed and on your PATH — it isn't an npm package, so npm install can't pull it in for you:
# pipx install yt-dlp, or brew install yt-dlp, or see
# https://github.com/yt-dlp/yt-dlp#installationimport { getDirectUrl } from 'hotdreams-ts';
await getDirectUrl('https://noodlemagazine.com/watch/-154109474_456276135');
// -> 'https://cdn77-pr.pvvstream.pro/videos/720/-154109474_456276135.mp4?...'Supported hosts today: pornhub.com, xvideos.com, tnaflix.com, spankbang.com,
playvids.com, noodlemagazine.com, eporner.com — these are the sites yt-dlp ships a dedicated
extractor for. Calling it on any other host (pornone, ukdevilz, fpo.xxx, viralpornhub,
nudes7) throws, since yt-dlp's generic extractor doesn't reliably resolve those.
Supported sites
| site | data video | preview | embed url video | suggested videos | |----------------|:----------:|:-------:|:----------------:|:-----------------:| | pornhub | ✅ | ✅ | ✅ | ✅ | | xvideos | ✅ | x | ✅ | x | | tnaflix | ✅ | ✅ | ✅ | ✅ | | noodlemagazine | ✅ | ✅ | ✅ | ✅ | | ukdevilz | ✅ | x | ✅ | | | pornone | ✅ | x | ✅ | | | eporner | ✅ | x | ✅ | ✅ | | fpo | ✅ | ✅ | ✅ | ✅ |
And sites from OnlyFans leaks, under only:
| site | data video | preview |
|-------------------------|:----------:|:-------:|
| only.nudes7 | ✅ | x |
| only.viralpornhub | ✅ | ✅ |
import { only } from 'hotdreams-ts';
await only.nudes7.searchPorn('mia monroe');
await only.viralpornhub.searchPorn('mia monroe');spankbang and playvids from the Python package aren't ported here: spankbang is blocked by
Cloudflare (marked broken upstream too) and playvids never got embed/pagination support upstream.
API shape
interface VideoData {
title: string | null;
time: string | null;
pageNumber: number | null;
url: string | null;
urlSource: string | null; // the search/listing page this was scraped from
thumbnail: string | null;
siteName: string;
preview?: string | null;
stats?: string | null;
index?: number | null; // position within its listing page
views?: string | null;
viewsCount?: number | null;
rating?: string | null;
ratingValue?: number | null;
uploadDate?: string | null;
uploadDateSeconds?: number | null;
duration?: number | null;
durationSeconds?: number | null;
}
interface EmbedVideo {
url: string | null;
title: string | null;
time: string | null;
thumbnail: string | null;
views: string | null;
timePublished: string | null;
siteName: string;
suggestionsCount: number;
suggestions: VideoData[] | null;
rating?: string | null;
durationSeconds?: number | string | null;
uploadDate?: string | null;
viewsCount?: number | string | null;
likes?: number | string | null;
person?: string | null;
tags?: string | null;
}
interface DataVideos {
siteName: string;
urlBase: string;
query: string;
ping: number; // seconds the search took
videos: VideoData[];
videosPerPage: number | null;
pageCount: number | null;
videoCount: number;
urlSearch: string;
success: boolean;
}Differences from the Python package
This is a straight functional port, not a redesign, but a few things were adapted to fit Node/TypeScript conventions:
- All field names are
camelCaseinstead ofsnake_case(e.g.videoCountinstead oflen_videos,urlSourceinstead ofurl_font,indexinstead ofindice). sucesswas corrected tosuccess,videos_sugestions/len_videos_sugestionstosuggestions/suggestionsCount.- HTTP + cookie-jar + proxy handling uses
axios+tough-cookieinstead ofrequests/mechanicalsoup/requests-html. - HTML parsing uses
cheerioinstead ofBeautifulSoup. getDirectUrl()shells out to theyt-dlpCLI instead of importing theyt-dlpPython package — install it separately, see Direct video URL.- A couple of quirky-but-intentional field mappings from the Python source were kept as-is (e.g.
noodlemagazine's embed puts the raw views string intoviewsCountrather thanviews, andukdevilz's search results put view stats into.statsand duration into.durationinstead of.views/.durationSeconds) — this mirrors the original per-site inconsistency rather than "fixing" it into something the Python examples in the upstream README no longer match.
License
BSD 3-Clause — see LICENSE.
