@soyaxell09/zenbot-scraper 1.0.11 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -9
- package/package.json +6 -2
- package/src/index.js +5 -0
- package/src/nsfw/index.js +10 -7
- package/src/nsfw/rule34.js +15 -9
- package/src/nsfw/xhammerdl.js +162 -0
- package/src/nsfw/xhamstersearch.js +41 -0
- package/src/scrapers/apk.js +67 -73
- package/src/scrapers/index.js +3 -1
- package/src/scrapers/instagram.js +106 -0
- package/src/scrapers/spotidown.js +89 -0
- package/src/tools/index.js +1 -0
- package/src/tools/screenshot.js +34 -0
- package/test.js +0 -21
package/README.md
CHANGED
|
@@ -62,6 +62,28 @@ const result = await tiktokDownload('https://www.tiktok.com/@user/video/123')
|
|
|
62
62
|
// → { nowatermark, watermark, audio, music: { title, author, url }, stats: { plays, likes, comments, shares } }
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
+
### Instagram
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
import { igDownload } from '@soyaxell09/zenbot-scraper'
|
|
69
|
+
|
|
70
|
+
const result = await igDownload('https://www.instagram.com/reel/xxx/')
|
|
71
|
+
// → { type, items: [{ type: 'video'|'image', url }] }
|
|
72
|
+
// type: 'reel' | 'post' | 'story' | 'video' | 'profile'
|
|
73
|
+
// Soporta: Videos, Fotos, Reels, Stories y Perfil (públicos)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Instagram
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
import { igDownload } from '@soyaxell09/zenbot-scraper'
|
|
80
|
+
|
|
81
|
+
const result = await igDownload('https://www.instagram.com/reel/xxx/')
|
|
82
|
+
// → { type, items: [{ type: 'video'|'image', url }] }
|
|
83
|
+
// type: 'reel' | 'post' | 'story' | 'video' | 'profile'
|
|
84
|
+
// Soporta: Videos, Fotos, Reels, Stories y Perfil (públicos)
|
|
85
|
+
```
|
|
86
|
+
|
|
65
87
|
### Facebook
|
|
66
88
|
|
|
67
89
|
```js
|
|
@@ -127,6 +149,16 @@ const file = await gdriveDownload('https://drive.google.com/file/d/1ABC.../view'
|
|
|
127
149
|
// → { fileId, buffer, contentType, size, url }
|
|
128
150
|
```
|
|
129
151
|
|
|
152
|
+
### Spotify (SpotiDown)
|
|
153
|
+
|
|
154
|
+
```js
|
|
155
|
+
import { spotidownTrack } from '@soyaxell09/zenbot-scraper'
|
|
156
|
+
|
|
157
|
+
const result = await spotidownTrack('https://open.spotify.com/track/xxx')
|
|
158
|
+
// → { name, artist, album, year, duration, cover, mp3, coverHd }
|
|
159
|
+
// mp3 y coverHd son URLs de descarga directa
|
|
160
|
+
```
|
|
161
|
+
|
|
130
162
|
---
|
|
131
163
|
|
|
132
164
|
## 🔍 Search
|
|
@@ -267,6 +299,15 @@ const cats = newsCategories()
|
|
|
267
299
|
// → ['es', 'en', 'pt', 'tech', 'sports', 'science', 'world']
|
|
268
300
|
```
|
|
269
301
|
|
|
302
|
+
### Screenshot
|
|
303
|
+
|
|
304
|
+
```js
|
|
305
|
+
import { screenshot } from '@soyaxell09/zenbot-scraper'
|
|
306
|
+
|
|
307
|
+
const result = await screenshot('https://github.com', 1280, 720)
|
|
308
|
+
// → { url, width, height, size, buffer }
|
|
309
|
+
```
|
|
310
|
+
|
|
270
311
|
### Upload (Catbox.moe)
|
|
271
312
|
|
|
272
313
|
```js
|
|
@@ -323,7 +364,6 @@ const result = await phDownload('https://www.pornhub.com/view_video.php?viewkey=
|
|
|
323
364
|
// Convertir a mp4 con ffmpeg (requiere ffmpeg instalado)
|
|
324
365
|
const video = await phDownloadBuffer('https://www.pornhub.com/view_video.php?viewkey=xxx', '720')
|
|
325
366
|
// → { title, thumb, duration, uploadDate, buffer, quality }
|
|
326
|
-
// quality: calidad elegida ('720', '480', '360') — máx 5 minutos por descarga
|
|
327
367
|
```
|
|
328
368
|
|
|
329
369
|
### XVideos Search
|
|
@@ -344,6 +384,29 @@ const result = await xvideosDownload('https://www.xvideos.com/video.xxx/...')
|
|
|
344
384
|
// → { title, thumb, duration, views, uploadDate, download: { low, high, hls } }
|
|
345
385
|
```
|
|
346
386
|
|
|
387
|
+
### XHamster Search
|
|
388
|
+
|
|
389
|
+
```js
|
|
390
|
+
import { xhamsterSearch } from '@soyaxell09/zenbot-scraper'
|
|
391
|
+
|
|
392
|
+
const results = await xhamsterSearch('query', 10)
|
|
393
|
+
// → [{ title, url, thumb, preview, duration, views }]
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### XHamster Download
|
|
397
|
+
|
|
398
|
+
```js
|
|
399
|
+
import { xhamsterDownload, xhamsterDownloadBuffer } from '@soyaxell09/zenbot-scraper'
|
|
400
|
+
|
|
401
|
+
// Info + calidades HLS
|
|
402
|
+
const result = await xhamsterDownload('https://xhamster.com/videos/...')
|
|
403
|
+
// → { title, thumb, duration, views, download: { '144p', '240p', '480p', '720p', '1080p' } }
|
|
404
|
+
|
|
405
|
+
// Convertir a mp4 con ffmpeg (requiere ffmpeg instalado)
|
|
406
|
+
const video = await xhamsterDownloadBuffer('https://xhamster.com/videos/...', '480p')
|
|
407
|
+
// → { title, thumb, duration, views, buffer, quality }
|
|
408
|
+
```
|
|
409
|
+
|
|
347
410
|
### Rule34 (yande.re + paheal)
|
|
348
411
|
|
|
349
412
|
```js
|
|
@@ -351,12 +414,12 @@ import { rule34Search, rule34Random } from '@soyaxell09/zenbot-scraper'
|
|
|
351
414
|
|
|
352
415
|
// Buscar por tags (usa yande.re, cae a paheal si no hay resultados)
|
|
353
416
|
const results = await rule34Search('cat_girl', 10)
|
|
354
|
-
// → [{ id, url, preview, tags, score, width, height }]
|
|
417
|
+
// → [{ id, url, full, preview, tags, score, width, height }]
|
|
355
418
|
|
|
356
419
|
// Imagen random (usa paheal, con o sin tags)
|
|
357
420
|
const random = await rule34Random('cat_girl')
|
|
358
421
|
const any = await rule34Random()
|
|
359
|
-
// → { id, url, preview, tags, score, width, height }
|
|
422
|
+
// → { id, url, full, preview, tags, score, width, height }
|
|
360
423
|
```
|
|
361
424
|
|
|
362
425
|
---
|
|
@@ -364,11 +427,11 @@ const any = await rule34Random()
|
|
|
364
427
|
## 📦 Importación por módulo
|
|
365
428
|
|
|
366
429
|
```js
|
|
367
|
-
import { ytDownload, tiktokDownload, translate, weather } from '@soyaxell09/zenbot-scraper'
|
|
368
|
-
import { ytDownload, fbDownload } from '@soyaxell09/zenbot-scraper/scrapers'
|
|
369
|
-
import { googleSearch, spotify }
|
|
370
|
-
import { translate, weather, news } from '@soyaxell09/zenbot-scraper/tools'
|
|
371
|
-
import { xnxxSearch, phSearch, rule34Random }
|
|
430
|
+
import { ytDownload, tiktokDownload, igDownload, translate, weather } from '@soyaxell09/zenbot-scraper'
|
|
431
|
+
import { ytDownload, fbDownload, igDownload } from '@soyaxell09/zenbot-scraper/scrapers'
|
|
432
|
+
import { googleSearch, spotify } from '@soyaxell09/zenbot-scraper/search'
|
|
433
|
+
import { translate, weather, news, screenshot } from '@soyaxell09/zenbot-scraper/tools'
|
|
434
|
+
import { xnxxSearch, phSearch, rule34Random, xhamsterSearch } from '@soyaxell09/zenbot-scraper/nsfw'
|
|
372
435
|
```
|
|
373
436
|
|
|
374
437
|
---
|
|
@@ -377,7 +440,7 @@ import { xnxxSearch, phSearch, rule34Random } from '@soyaxell09/zenb
|
|
|
377
440
|
|
|
378
441
|
- Node.js >= 18.0.0
|
|
379
442
|
- Dependencias: `axios`, `cheerio`, `@distube/ytdl-core`, `form-data`
|
|
380
|
-
- Para `phDownloadBuffer`: requiere `ffmpeg` instalado en el sistema
|
|
443
|
+
- Para `phDownloadBuffer` y `xhamsterDownloadBuffer`: requiere `ffmpeg` instalado en el sistema
|
|
381
444
|
|
|
382
445
|
---
|
|
383
446
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soyaxell09/zenbot-scraper",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Scrapers de descarga y búsqueda para bots de WhatsApp — YouTube, TikTok, Facebook, Twitter, Pinterest, MediaFire, GitHub, APK, Google Drive, XNXX, PornHub, XVideos, Rule34 y más.",
|
|
3
|
+
"version": "1.0.13",
|
|
4
|
+
"description": "Scrapers de descarga y búsqueda para bots de WhatsApp — YouTube, TikTok, Instagram, Facebook, Twitter, Pinterest, MediaFire, GitHub, APK, Google Drive, XNXX, PornHub, XVideos, XHamster, Rule34, Screenshot y más.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"exports": {
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"downloader",
|
|
22
22
|
"youtube",
|
|
23
23
|
"tiktok",
|
|
24
|
+
"instagram",
|
|
24
25
|
"facebook",
|
|
25
26
|
"twitter",
|
|
26
27
|
"pinterest",
|
|
@@ -36,12 +37,14 @@
|
|
|
36
37
|
"qr",
|
|
37
38
|
"news",
|
|
38
39
|
"upload",
|
|
40
|
+
"screenshot",
|
|
39
41
|
"stickers",
|
|
40
42
|
"gdrive",
|
|
41
43
|
"google-drive",
|
|
42
44
|
"xnxx",
|
|
43
45
|
"pornhub",
|
|
44
46
|
"xvideos",
|
|
47
|
+
"xhamster",
|
|
45
48
|
"rule34",
|
|
46
49
|
"nsfw"
|
|
47
50
|
],
|
|
@@ -51,6 +54,7 @@
|
|
|
51
54
|
"@distube/ytdl-core": "^4.16.0",
|
|
52
55
|
"axios": "^1.6.0",
|
|
53
56
|
"cheerio": "^1.0.0",
|
|
57
|
+
"node-fetch": "^3.3.2",
|
|
54
58
|
"form-data": "^4.0.0"
|
|
55
59
|
},
|
|
56
60
|
"engines": {
|
package/src/index.js
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
export { ytInfo, ytDownload, ytSearch } from './scrapers/youtube.js'
|
|
9
9
|
export { ytInfoV2, ytDownloadV2, getFileSizeV2 } from './scrapers/youtubev2.js'
|
|
10
10
|
export { tiktokInfo, tiktokDownload } from './scrapers/tiktok.js'
|
|
11
|
+
export { igDownload } from './scrapers/instagram.js'
|
|
12
|
+
export { spotidownTrack } from './scrapers/spotidown.js'
|
|
11
13
|
export { fbDownload } from './scrapers/facebook.js'
|
|
12
14
|
export { tweetInfo, tweetDownload } from './scrapers/twitter.js'
|
|
13
15
|
export { mediafireInfo } from './scrapers/mediafire.js'
|
|
@@ -29,6 +31,7 @@ export { qrGenerate, qrRead } from './tools
|
|
|
29
31
|
export { shortenUrl, expandUrl } from './tools/urlshortener.js'
|
|
30
32
|
export { news, newsCategories } from './tools/news.js'
|
|
31
33
|
export { upload } from './tools/upload.js'
|
|
34
|
+
export { screenshot } from './tools/screenshot.js'
|
|
32
35
|
export { xnxxSearch } from './nsfw/xnxxsearch.js'
|
|
33
36
|
export { xnxxDownload } from './nsfw/xnxxdl.js'
|
|
34
37
|
export { phSearch } from './nsfw/phsearch.js'
|
|
@@ -36,3 +39,5 @@ export { phDownload, phDownloadBuffer } from './nsfw/
|
|
|
36
39
|
export { xvideosSearch } from './nsfw/xvideossearch.js'
|
|
37
40
|
export { xvideosDownload } from './nsfw/xvideosdl.js'
|
|
38
41
|
export { rule34Search, rule34Random } from './nsfw/rule34.js'
|
|
42
|
+
export { xhamsterSearch } from './nsfw/xhamstersearch.js'
|
|
43
|
+
export { xhamsterDownload, xhamsterDownloadBuffer } from './nsfw/xhammerdl.js'
|
package/src/nsfw/index.js
CHANGED
|
@@ -5,10 +5,13 @@
|
|
|
5
5
|
* Deja los créditos we 🗣️
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
export { xnxxSearch }
|
|
9
|
-
export { xnxxDownload }
|
|
10
|
-
export { phSearch }
|
|
11
|
-
export { phDownload, phDownloadBuffer }
|
|
12
|
-
export { xvideosSearch }
|
|
13
|
-
export { xvideosDownload }
|
|
14
|
-
export { rule34Search, rule34Random }
|
|
8
|
+
export { xnxxSearch } from './xnxxsearch.js'
|
|
9
|
+
export { xnxxDownload } from './xnxxdl.js'
|
|
10
|
+
export { phSearch } from './phsearch.js'
|
|
11
|
+
export { phDownload, phDownloadBuffer } from './phdl.js'
|
|
12
|
+
export { xvideosSearch } from './xvideossearch.js'
|
|
13
|
+
export { xvideosDownload } from './xvideosdl.js'
|
|
14
|
+
export { rule34Search, rule34Random } from './rule34.js'
|
|
15
|
+
export { xhamsterSearch } from './xhamstersearch.js'
|
|
16
|
+
export { xhamsterDownload, xhamsterDownloadBuffer } from './xhammerdl.js'
|
|
17
|
+
export { hentaiImgSearch, hentaiImgRandom } from './hentaiimg.js'
|
package/src/nsfw/rule34.js
CHANGED
|
@@ -10,6 +10,19 @@ import * as cheerio from 'cheerio';
|
|
|
10
10
|
|
|
11
11
|
const UA = 'Mozilla/5.0 (Linux; Android 11; Redmi Note 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36';
|
|
12
12
|
|
|
13
|
+
function mapYande(p) {
|
|
14
|
+
return {
|
|
15
|
+
id: String(p.id),
|
|
16
|
+
url: p.sample_url || p.file_url || '',
|
|
17
|
+
full: p.file_url || '',
|
|
18
|
+
preview: p.preview_url || '',
|
|
19
|
+
tags: p.tags || '',
|
|
20
|
+
score: p.score || 0,
|
|
21
|
+
width: p.width || 0,
|
|
22
|
+
height: p.height || 0
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
13
26
|
async function yandereSearch(tags, limit = 10) {
|
|
14
27
|
const { data } = await axios.get('https://yande.re/post.json', {
|
|
15
28
|
params: { tags, limit },
|
|
@@ -17,15 +30,7 @@ async function yandereSearch(tags, limit = 10) {
|
|
|
17
30
|
timeout: 12000
|
|
18
31
|
});
|
|
19
32
|
if (!Array.isArray(data) || !data.length) return [];
|
|
20
|
-
return data.map(
|
|
21
|
-
id: String(p.id),
|
|
22
|
-
url: p.file_url || '',
|
|
23
|
-
preview: p.preview_url || '',
|
|
24
|
-
tags: p.tags || '',
|
|
25
|
-
score: p.score || 0,
|
|
26
|
-
width: p.width || 0,
|
|
27
|
-
height: p.height || 0
|
|
28
|
-
}));
|
|
33
|
+
return data.map(mapYande);
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
function parsePaheal(xml) {
|
|
@@ -37,6 +42,7 @@ function parsePaheal(xml) {
|
|
|
37
42
|
items.push({
|
|
38
43
|
id: $(el).attr('id'),
|
|
39
44
|
url: file_url,
|
|
45
|
+
full: file_url,
|
|
40
46
|
preview: $(el).attr('preview_url') || '',
|
|
41
47
|
tags: $(el).attr('tags') || '',
|
|
42
48
|
score: parseInt($(el).attr('score')) || 0,
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* © Created by AxelDev09 🔥
|
|
3
|
+
* GitHub: https://github.com/AxelDev09
|
|
4
|
+
* Instagram: @axeldev09
|
|
5
|
+
* Deja los créditos we 🗣️
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import axios from 'axios';
|
|
9
|
+
import * as cheerio from 'cheerio';
|
|
10
|
+
import { execFile } from 'child_process';
|
|
11
|
+
import { promisify } from 'util';
|
|
12
|
+
import { mkdtemp, readFile, rm } from 'fs/promises';
|
|
13
|
+
import { join } from 'path';
|
|
14
|
+
import { tmpdir } from 'os';
|
|
15
|
+
|
|
16
|
+
const execFileAsync = promisify(execFile);
|
|
17
|
+
|
|
18
|
+
const UA = 'Mozilla/5.0 (Linux; Android 11; Redmi Note 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36';
|
|
19
|
+
|
|
20
|
+
function secsToTime(s) {
|
|
21
|
+
if (!s) return null;
|
|
22
|
+
const h = Math.floor(s / 3600);
|
|
23
|
+
const m = Math.floor((s % 3600) / 60);
|
|
24
|
+
const sec = s % 60;
|
|
25
|
+
if (h > 0) return `${h}:${String(m).padStart(2, '0')}:${String(sec).padStart(2, '0')}`;
|
|
26
|
+
return `${m}:${String(sec).padStart(2, '0')}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const XH_UA = 'Mozilla/5.0 (Linux; Android 11; Redmi Note 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36';
|
|
30
|
+
|
|
31
|
+
export async function xhamsterDownloadBuffer(url, quality = '720p') {
|
|
32
|
+
const { data } = await axios.get(url, {
|
|
33
|
+
headers: { 'User-Agent': XH_UA },
|
|
34
|
+
timeout: 12000
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const $ = cheerio.load(data);
|
|
38
|
+
const scripts = $('script').map((_, el) => $(el).html()).get();
|
|
39
|
+
|
|
40
|
+
let title = null, thumb = null, duration = null, views = null;
|
|
41
|
+
for (const s of scripts) {
|
|
42
|
+
if (!s || !s.includes('"title"') || !s.includes('"duration"')) continue;
|
|
43
|
+
try {
|
|
44
|
+
const match = s.match(/"title":"([^"]+)","thumbUrl":"([^"]+)","duration":(\d+),"views":(\d+)/);
|
|
45
|
+
if (match) {
|
|
46
|
+
title = match[1].replace(/\\u([0-9a-fA-F]{4})/g, (_, h) => String.fromCharCode(parseInt(h, 16)));
|
|
47
|
+
thumb = match[2].replace(/\\\//g, '/');
|
|
48
|
+
duration = secsToTime(parseInt(match[3]));
|
|
49
|
+
views = parseInt(match[4]);
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
} catch {}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!title) title = $('meta[property="og:title"]').attr('content') || null;
|
|
56
|
+
if (!thumb) thumb = $('meta[property="og:image"]').attr('content') || null;
|
|
57
|
+
|
|
58
|
+
const mp4Matches = [...new Set(data.match(/https?:\/\/[^\s"'\\]+\.mp4[^\s"'\\]*/g) || [])];
|
|
59
|
+
const masterUrl = mp4Matches.find(u => u.includes('480p') || u.includes('hls4'));
|
|
60
|
+
if (!masterUrl) throw new Error('No se encontró stream de video.');
|
|
61
|
+
|
|
62
|
+
const baseUrl = masterUrl.substring(0, masterUrl.lastIndexOf('/') + 1);
|
|
63
|
+
const { data: m3u8 } = await axios.get(masterUrl, {
|
|
64
|
+
headers: { 'User-Agent': XH_UA, 'Referer': 'https://xhamster.com/' },
|
|
65
|
+
timeout: 10000
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const lines = m3u8.split('\n');
|
|
69
|
+
let streamUrl = null;
|
|
70
|
+
for (let i = 0; i < lines.length; i++) {
|
|
71
|
+
if (lines[i].startsWith('#EXT-X-STREAM-INF')) {
|
|
72
|
+
const qualMatch = lines[i + 1]?.trim().match(/(\d+p)/);
|
|
73
|
+
const q = qualMatch ? qualMatch[1] : null;
|
|
74
|
+
if (q === quality || (!streamUrl && q)) {
|
|
75
|
+
const next = lines[i + 1]?.trim();
|
|
76
|
+
streamUrl = next.startsWith('http') ? next : baseUrl + next;
|
|
77
|
+
if (q === quality) break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!streamUrl) throw new Error('No se encontró la calidad solicitada.');
|
|
83
|
+
|
|
84
|
+
const tmpDir = await mkdtemp(join(tmpdir(), 'xhdl-'));
|
|
85
|
+
const outPath = join(tmpDir, 'video.mp4');
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
await execFileAsync('ffmpeg', [
|
|
89
|
+
'-v', 'quiet',
|
|
90
|
+
'-y',
|
|
91
|
+
'-user_agent', XH_UA,
|
|
92
|
+
'-headers', 'Referer: https://xhamster.com/\r\n',
|
|
93
|
+
'-i', streamUrl,
|
|
94
|
+
'-c', 'copy',
|
|
95
|
+
outPath
|
|
96
|
+
], { timeout: 120000 });
|
|
97
|
+
const buffer = await readFile(outPath);
|
|
98
|
+
return { title, thumb, duration, views, buffer, quality };
|
|
99
|
+
} finally {
|
|
100
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function xhamsterDownload(url) {
|
|
105
|
+
const { data } = await axios.get(url, {
|
|
106
|
+
headers: { 'User-Agent': UA },
|
|
107
|
+
timeout: 12000
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const $ = cheerio.load(data);
|
|
111
|
+
const scripts = $('script').map((_, el) => $(el).html()).get();
|
|
112
|
+
|
|
113
|
+
let title = null, thumb = null, duration = null, views = null;
|
|
114
|
+
|
|
115
|
+
for (const s of scripts) {
|
|
116
|
+
if (!s || !s.includes('"title"') || !s.includes('"duration"')) continue;
|
|
117
|
+
try {
|
|
118
|
+
const match = s.match(/"title":"([^"]+)","thumbUrl":"([^"]+)","duration":(\d+),"views":(\d+)/);
|
|
119
|
+
if (match) {
|
|
120
|
+
title = match[1].replace(/\\u([0-9a-fA-F]{4})/g, (_, h) => String.fromCharCode(parseInt(h, 16)));
|
|
121
|
+
thumb = match[2].replace(/\\\//g, '/');
|
|
122
|
+
duration = secsToTime(parseInt(match[3]));
|
|
123
|
+
views = parseInt(match[4]);
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
} catch {}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!title) title = $('meta[property="og:title"]').attr('content') || null;
|
|
130
|
+
if (!thumb) thumb = $('meta[property="og:image"]').attr('content') || null;
|
|
131
|
+
|
|
132
|
+
const mp4Matches = [...new Set(data.match(/https?:\/\/[^\s"'\\]+\.mp4[^\s"'\\]*/g) || [])];
|
|
133
|
+
const masterUrl = mp4Matches.find(u => u.includes('480p') || u.includes('hls4'));
|
|
134
|
+
|
|
135
|
+
if (!masterUrl) throw new Error('No se encontró stream de video.');
|
|
136
|
+
|
|
137
|
+
const baseUrl = masterUrl.substring(0, masterUrl.lastIndexOf('/') + 1);
|
|
138
|
+
|
|
139
|
+
const { data: m3u8 } = await axios.get(masterUrl, {
|
|
140
|
+
headers: { 'User-Agent': UA, 'Referer': 'https://xhamster.com/' },
|
|
141
|
+
timeout: 10000
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const download = {};
|
|
145
|
+
const lines = m3u8.split('\n');
|
|
146
|
+
for (let i = 0; i < lines.length; i++) {
|
|
147
|
+
if (lines[i].startsWith('#EXT-X-STREAM-INF')) {
|
|
148
|
+
const resMatch = lines[i].match(/RESOLUTION=(\d+x\d+)/);
|
|
149
|
+
const nextLine = lines[i + 1]?.trim();
|
|
150
|
+
if (nextLine && resMatch) {
|
|
151
|
+
const qualMatch = nextLine.match(/(\d+p)/);
|
|
152
|
+
const quality = qualMatch ? qualMatch[1] : resMatch[1];
|
|
153
|
+
const streamUrl = nextLine.startsWith('http') ? nextLine : baseUrl + nextLine;
|
|
154
|
+
download[quality] = { quality, url: streamUrl, format: 'hls' };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (!Object.keys(download).length) throw new Error('No se encontraron calidades.');
|
|
160
|
+
|
|
161
|
+
return { title, thumb, duration, views, download };
|
|
162
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* © Created by AxelDev09 🔥
|
|
3
|
+
* GitHub: https://github.com/AxelDev09
|
|
4
|
+
* Instagram: @axeldev09
|
|
5
|
+
* Deja los créditos we 🗣️
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import axios from 'axios';
|
|
9
|
+
import * as cheerio from 'cheerio';
|
|
10
|
+
|
|
11
|
+
const UA = 'Mozilla/5.0 (Linux; Android 11; Redmi Note 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36';
|
|
12
|
+
|
|
13
|
+
export async function xhamsterSearch(query, limit = 10) {
|
|
14
|
+
const { data } = await axios.get(`https://xhamster.com/search/${encodeURIComponent(query)}`, {
|
|
15
|
+
headers: { 'User-Agent': UA },
|
|
16
|
+
timeout: 12000
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const $ = cheerio.load(data);
|
|
20
|
+
const results = [];
|
|
21
|
+
|
|
22
|
+
$('[class*="video-thumb"]').each((_, el) => {
|
|
23
|
+
if (results.length >= limit) return false;
|
|
24
|
+
|
|
25
|
+
const anchor = $(el).find('a.thumb-image-container').first();
|
|
26
|
+
const img = $(el).find('img').first();
|
|
27
|
+
|
|
28
|
+
const title = anchor.attr('aria-label') || $(el).find('[class*="name"]').first().text().trim();
|
|
29
|
+
const href = anchor.attr('href') || '';
|
|
30
|
+
const thumb = img.attr('src') || img.attr('srcset')?.split(' ')[0] || '';
|
|
31
|
+
const preview = anchor.attr('data-previewvideo') || '';
|
|
32
|
+
const duration = $(el).find('time').first().attr('datetime') || '';
|
|
33
|
+
const views = $(el).find('[class*="views"]').first().text().trim();
|
|
34
|
+
|
|
35
|
+
if (!title || !href) return;
|
|
36
|
+
|
|
37
|
+
results.push({ title, url: href, thumb, preview, duration, views });
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return results;
|
|
41
|
+
}
|
package/src/scrapers/apk.js
CHANGED
|
@@ -5,52 +5,51 @@
|
|
|
5
5
|
* Deja los créditos we 🗣️
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import axios from 'axios'
|
|
9
|
-
import * as cheerio from 'cheerio'
|
|
8
|
+
import axios from 'axios';
|
|
9
|
+
import * as cheerio from 'cheerio';
|
|
10
10
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
const UA = 'Mozilla/5.0 (Linux; Android 11; Redmi Note 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36';
|
|
12
|
+
const BASE = 'https://apkpure.net';
|
|
13
|
+
const DL_BASE = 'https://d.apkpure.net';
|
|
14
|
+
const HEADERS = { 'User-Agent': UA, 'Referer': BASE };
|
|
14
15
|
|
|
15
16
|
function extractPkg(href) {
|
|
16
|
-
const m = href?.match(/\/((?:com|org|net|io|co)\.[a-z0-9.]+)(?:\/|$)/i)
|
|
17
|
-
return m ? m[1] : ''
|
|
17
|
+
const m = href?.match(/\/((?:com|org|net|io|co)\.[a-z0-9.]+)(?:\/|$)/i);
|
|
18
|
+
return m ? m[1] : '';
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return
|
|
21
|
+
function parsePageData(data) {
|
|
22
|
+
try {
|
|
23
|
+
const m = data.match(/window\.apkpure\s*=\s*\{pageData:\s*(\{.*?\})\s*[,;]/s);
|
|
24
|
+
if (m) return JSON.parse(m[1]);
|
|
25
|
+
} catch {}
|
|
26
|
+
return null;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
export async function apkSearch(query, limit = 5) {
|
|
29
|
-
if (!query?.trim()) throw new Error('Query vacío')
|
|
30
|
-
|
|
31
|
-
const $ = await searchPage(query)
|
|
32
|
-
const results = []
|
|
33
|
-
const seen = new Set()
|
|
34
|
-
|
|
35
|
-
$('.brand-info-top').each((_, el) => {
|
|
36
|
-
if (results.length >= limit) return false
|
|
30
|
+
if (!query?.trim()) throw new Error('Query vacío');
|
|
37
31
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
32
|
+
const { data } = await axios.get(`${BASE}/search?q=${encodeURIComponent(query)}`, {
|
|
33
|
+
headers: HEADERS, timeout: 15000
|
|
34
|
+
});
|
|
35
|
+
const $ = cheerio.load(data);
|
|
36
|
+
const results = [];
|
|
37
|
+
const seen = new Set();
|
|
42
38
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
.map(a => $(a).attr('href'))
|
|
46
|
-
.find(h => extractPkg(h)) || ''
|
|
39
|
+
$('.search-brand-container').each((_, el) => {
|
|
40
|
+
if (results.length >= limit) return false;
|
|
47
41
|
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
42
|
+
const name = $(el).find('a.top').first().text().trim();
|
|
43
|
+
const dev = $(el).find('a.developer').first().text().trim();
|
|
44
|
+
const date = $(el).find('span.time').first().text().trim();
|
|
45
|
+
const icon = $(el).find('img.app-icon-img').first().attr('data-original') || '';
|
|
46
|
+
const appHref = $(el).find('a.top').first().attr('href') || '';
|
|
47
|
+
const pkg = extractPkg(appHref);
|
|
51
48
|
|
|
52
|
-
|
|
49
|
+
if (!name || !pkg || seen.has(pkg)) return;
|
|
50
|
+
seen.add(pkg);
|
|
53
51
|
|
|
52
|
+
const appUrl = appHref.startsWith('http') ? appHref : `${BASE}${appHref}`;
|
|
54
53
|
results.push({
|
|
55
54
|
name,
|
|
56
55
|
developer: dev,
|
|
@@ -58,55 +57,50 @@ export async function apkSearch(query, limit = 5) {
|
|
|
58
57
|
date,
|
|
59
58
|
icon,
|
|
60
59
|
appUrl,
|
|
61
|
-
dlUrl:
|
|
62
|
-
})
|
|
63
|
-
})
|
|
60
|
+
dlUrl: `${DL_BASE}/b/APK/${pkg}?version=latest`,
|
|
61
|
+
});
|
|
62
|
+
});
|
|
64
63
|
|
|
65
|
-
if (!results.length) throw new Error('No se encontraron resultados')
|
|
66
|
-
return results
|
|
64
|
+
if (!results.length) throw new Error('No se encontraron resultados');
|
|
65
|
+
return results;
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
export async function apkInfo(pkgOrUrl) {
|
|
70
|
-
let pkg = pkgOrUrl
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const icon = el.closest('.search-result').find('img').first().attr('src') || ''
|
|
95
|
-
|
|
96
|
-
const dlLinks = []
|
|
97
|
-
$(`a[href*="d.apkpure.com"]`).each((_, a) => {
|
|
98
|
-
const href = $(a).attr('href') || ''
|
|
99
|
-
if (href.includes(pkg)) dlLinks.push(href)
|
|
100
|
-
})
|
|
69
|
+
let pkg = pkgOrUrl.includes('apkpure') ? extractPkg(pkgOrUrl) : pkgOrUrl;
|
|
70
|
+
if (!pkg) pkg = pkgOrUrl;
|
|
71
|
+
|
|
72
|
+
const shortName = pkg.split('.').slice(-2).join('-').toLowerCase();
|
|
73
|
+
|
|
74
|
+
const { data } = await axios.get(`${BASE}/${shortName}/${pkg}/download`, {
|
|
75
|
+
headers: HEADERS, timeout: 15000, validateStatus: () => true
|
|
76
|
+
});
|
|
77
|
+
const $ = cheerio.load(data);
|
|
78
|
+
const pageData = parsePageData(data);
|
|
79
|
+
|
|
80
|
+
const name = pageData?.versionName
|
|
81
|
+
? $('title').text().split(' APK')[0].replace('Download ', '').trim()
|
|
82
|
+
: $('h1, .title-like').first().text().trim();
|
|
83
|
+
const version = pageData?.versionName || $('[class*="version"]').first().text().trim().match(/[\d.]+/)?.[0] || '';
|
|
84
|
+
const size = $('[class*="size"]').first().text().trim();
|
|
85
|
+
const icon = $('img.icon, img[itemprop="image"]').first().attr('src') || '';
|
|
86
|
+
const dev = $('[class*="developer"], .dev-info, [itemprop="author"]').first().text().trim();
|
|
87
|
+
|
|
88
|
+
const dlLinks = [];
|
|
89
|
+
$(`a[href*="d.apkpure.net"]`).each((_, a) => {
|
|
90
|
+
const href = $(a).attr('href') || '';
|
|
91
|
+
if (href.includes(pkg)) dlLinks.push(href);
|
|
92
|
+
});
|
|
101
93
|
|
|
102
94
|
return {
|
|
103
95
|
name,
|
|
104
96
|
developer: dev,
|
|
105
97
|
pkg,
|
|
106
|
-
|
|
98
|
+
version,
|
|
99
|
+
size,
|
|
107
100
|
icon,
|
|
108
|
-
download:
|
|
101
|
+
download: `${DL_BASE}/b/APK/${pkg}?version=latest`,
|
|
102
|
+
downloadXapk: `${DL_BASE}/b/XAPK/${pkg}?version=latest`,
|
|
109
103
|
dlLinks: [...new Set(dlLinks)],
|
|
110
|
-
url:
|
|
111
|
-
}
|
|
104
|
+
url: `${BASE}/${shortName}/${pkg}`,
|
|
105
|
+
};
|
|
112
106
|
}
|
package/src/scrapers/index.js
CHANGED
|
@@ -6,4 +6,6 @@ export { tweetInfo, tweetDownload } from './twitt
|
|
|
6
6
|
export { mediafireInfo } from './mediafire.js'
|
|
7
7
|
export { githubInfo, githubRelease, githubContents, githubSearch } from './github.js'
|
|
8
8
|
export { apkSearch, apkInfo } from './apk.js'
|
|
9
|
-
export { gdriveInfo, gdriveDownload } from './gdrive.js'
|
|
9
|
+
export { gdriveInfo, gdriveDownload } from './gdrive.js'
|
|
10
|
+
export { igDownload } from './instagram.js'
|
|
11
|
+
export { spotidownTrack } from './spotidown.js'
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* © Created by AxelDev09 🔥
|
|
3
|
+
* GitHub: https://github.com/AxelDev09
|
|
4
|
+
* Instagram: @axeldev09
|
|
5
|
+
* Deja los créditos we 🗣️
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import axios from 'axios';
|
|
9
|
+
import * as cheerio from 'cheerio';
|
|
10
|
+
|
|
11
|
+
const UA = 'Mozilla/5.0 (Linux; Android 11; Redmi Note 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36';
|
|
12
|
+
const AJAX_URL = 'https://igsnapinsta.com/wp-admin/admin-ajax.php';
|
|
13
|
+
const BASE_URL = 'https://igsnapinsta.com';
|
|
14
|
+
|
|
15
|
+
function decodeUrl(encodedUrl) {
|
|
16
|
+
try { return Buffer.from(encodedUrl, 'base64').toString('utf-8'); }
|
|
17
|
+
catch { return encodedUrl; }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function parseItems(html) {
|
|
21
|
+
const $ = cheerio.load(html);
|
|
22
|
+
const items = [];
|
|
23
|
+
const seen = new Set();
|
|
24
|
+
|
|
25
|
+
function add(type, url) {
|
|
26
|
+
const clean = url.replace(/&/g, '&').trim();
|
|
27
|
+
if (!clean || seen.has(clean)) return;
|
|
28
|
+
seen.add(clean);
|
|
29
|
+
items.push({ type, url: clean });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Primero agregar fuentes directas (CDN Instagram)
|
|
33
|
+
$('source[src]').each((_, el) => {
|
|
34
|
+
const src = $(el).attr('src');
|
|
35
|
+
if (src && !src.includes('kdnsd')) add('video', src);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
$('img[src]').each((_, el) => {
|
|
39
|
+
const src = $(el).attr('src') || '';
|
|
40
|
+
if ((src.includes('cdninstagram') || src.includes('fbcdn')) && !src.includes('kdnsd'))
|
|
41
|
+
add('image', src);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (!items.length) {
|
|
45
|
+
$('a[href]').each((_, el) => {
|
|
46
|
+
const href = $(el).attr('href') || '';
|
|
47
|
+
if (!href.includes('kdnsd/v1/download')) return;
|
|
48
|
+
const b64 = href.split('url=')[1] || '';
|
|
49
|
+
const decoded = decodeUrl(decodeURIComponent(b64));
|
|
50
|
+
const type = decoded.includes('.mp4') ? 'video' : 'image';
|
|
51
|
+
add(type, href.replace(/&/g, '&'));
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
$('img[src]').each((_, el) => {
|
|
55
|
+
const src = $(el).attr('src') || '';
|
|
56
|
+
if (src.includes('kdnsd/v1/download')) add('image', src);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return items;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function detectType(url) {
|
|
64
|
+
if (url.includes('/reel/')) return 'reel';
|
|
65
|
+
if (url.includes('/p/')) return 'post';
|
|
66
|
+
if (url.includes('/stories/')) return 'story';
|
|
67
|
+
if (url.includes('/tv/')) return 'video';
|
|
68
|
+
const path = new URL(url).pathname.replace(/\/$/, '');
|
|
69
|
+
if (path.split('/').length === 2) return 'profile';
|
|
70
|
+
return 'post';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function igDownload(url) {
|
|
74
|
+
if (!url.includes('instagram.com'))
|
|
75
|
+
throw new Error('URL inválida. Debe ser un link de Instagram.');
|
|
76
|
+
|
|
77
|
+
const { data } = await axios.post(
|
|
78
|
+
AJAX_URL,
|
|
79
|
+
new URLSearchParams({ action: 'kdnsd_get_instagram_video', url }),
|
|
80
|
+
{
|
|
81
|
+
headers: {
|
|
82
|
+
'User-Agent': UA,
|
|
83
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
84
|
+
'Referer': `${BASE_URL}/es/`,
|
|
85
|
+
'Origin': BASE_URL,
|
|
86
|
+
'X-Requested-With': 'XMLHttpRequest',
|
|
87
|
+
},
|
|
88
|
+
timeout: 20000
|
|
89
|
+
}
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
if (!data?.success || !data?.data?.html) {
|
|
93
|
+
const html = data?.data?.html || '';
|
|
94
|
+
if (html.includes('private') || html.includes('privado') || data?.data?.message?.includes('private'))
|
|
95
|
+
throw new Error('Perfil privado. Solo se puede descargar contenido de perfiles públicos.');
|
|
96
|
+
throw new Error('No se pudo obtener el contenido. Verificá que el perfil/post sea público.');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const type = detectType(url);
|
|
100
|
+
const items = parseItems(data.data.html);
|
|
101
|
+
|
|
102
|
+
if (!items.length)
|
|
103
|
+
throw new Error('No se encontró contenido descargable.');
|
|
104
|
+
|
|
105
|
+
return { type, items };
|
|
106
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* © Created by AxelDev09 🔥
|
|
3
|
+
* GitHub: https://github.com/AxelDev09
|
|
4
|
+
* Instagram: @axeldev09
|
|
5
|
+
* Deja los créditos we 🗣️
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import axios from 'axios';
|
|
9
|
+
import * as cheerio from 'cheerio';
|
|
10
|
+
|
|
11
|
+
const UA = 'Mozilla/5.0 (Linux; Android 11; Redmi Note 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36';
|
|
12
|
+
const BASE_URL = 'https://spotidown.app';
|
|
13
|
+
|
|
14
|
+
async function getSession() {
|
|
15
|
+
const { data, headers } = await axios.get(`${BASE_URL}/es5`, {
|
|
16
|
+
headers: { 'User-Agent': UA }, timeout: 12000
|
|
17
|
+
});
|
|
18
|
+
const $ = cheerio.load(data);
|
|
19
|
+
const tokenName = $('input[type="hidden"]').not('[name="g-recaptcha-response"]').first().attr('name');
|
|
20
|
+
const token = $('input[type="hidden"]').not('[name="g-recaptcha-response"]').first().val();
|
|
21
|
+
const cookies = headers['set-cookie']?.map(c => c.split(';')[0]).join('; ') || '';
|
|
22
|
+
return { tokenName, token, cookies };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function buildHeaders(cookies) {
|
|
26
|
+
return {
|
|
27
|
+
'User-Agent': UA,
|
|
28
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
29
|
+
'Referer': `${BASE_URL}/es5`,
|
|
30
|
+
'Origin': BASE_URL,
|
|
31
|
+
'Cookie': cookies,
|
|
32
|
+
'X-Requested-With': 'XMLHttpRequest',
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function spotidownTrack(url) {
|
|
37
|
+
if (!url.includes('spotify.com/track'))
|
|
38
|
+
throw new Error('URL inválida. Debe ser un link de track de Spotify.');
|
|
39
|
+
|
|
40
|
+
const { tokenName, token, cookies } = await getSession();
|
|
41
|
+
|
|
42
|
+
const { data: actionData } = await axios.post(
|
|
43
|
+
`${BASE_URL}/action`,
|
|
44
|
+
new URLSearchParams({ url, 'g-recaptcha-response': '', [tokenName]: token }),
|
|
45
|
+
{ headers: buildHeaders(cookies), timeout: 20000 }
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
if (actionData?.error) throw new Error(actionData.message || 'Error al obtener el track.');
|
|
49
|
+
|
|
50
|
+
const $r = cheerio.load(actionData.data || '');
|
|
51
|
+
const dataF = $r('input[name="data"]').first().val();
|
|
52
|
+
const baseF = $r('input[name="base"]').first().val();
|
|
53
|
+
const tkF = $r('input[name="token"]').first().val();
|
|
54
|
+
|
|
55
|
+
if (!dataF) throw new Error('No se pudo obtener la info del track.');
|
|
56
|
+
|
|
57
|
+
const trackInfo = JSON.parse(Buffer.from(dataF, 'base64').toString());
|
|
58
|
+
|
|
59
|
+
const { data: trackData } = await axios.post(
|
|
60
|
+
`${BASE_URL}/action/track`,
|
|
61
|
+
new URLSearchParams({ data: dataF, base: baseF, token: tkF }),
|
|
62
|
+
{ headers: buildHeaders(cookies), timeout: 30000 }
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
if (trackData?.error) throw new Error(trackData.message || 'Error al descargar el track.');
|
|
66
|
+
|
|
67
|
+
const $t = cheerio.load(trackData.data || '');
|
|
68
|
+
const links = [];
|
|
69
|
+
|
|
70
|
+
$t('a[id="popup"]').each((_, el) => {
|
|
71
|
+
const href = $t(el).attr('href') || '';
|
|
72
|
+
const label = $t(el).find('span span').text().trim();
|
|
73
|
+
if (href) links.push({ label, url: href });
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const mp3 = links.find(l => l.label.toLowerCase().includes('mp3'))?.url || null;
|
|
77
|
+
const cover = links.find(l => l.label.toLowerCase().includes('cover'))?.url || null;
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
name: trackInfo.name,
|
|
81
|
+
artist: trackInfo.artist,
|
|
82
|
+
album: trackInfo.album,
|
|
83
|
+
duration: trackInfo.duration,
|
|
84
|
+
year: trackInfo.date,
|
|
85
|
+
cover: trackInfo.cover,
|
|
86
|
+
mp3,
|
|
87
|
+
coverHd: cover,
|
|
88
|
+
};
|
|
89
|
+
}
|
package/src/tools/index.js
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* © Created by AxelDev09 🔥
|
|
3
|
+
* GitHub: https://github.com/AxelDev09
|
|
4
|
+
* Instagram: @axeldev09
|
|
5
|
+
* Deja los créditos we 🗣️
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import axios from 'axios';
|
|
9
|
+
|
|
10
|
+
const UA = 'Mozilla/5.0 (Linux; Android 11; Redmi Note 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36';
|
|
11
|
+
|
|
12
|
+
export async function screenshot(url, width = 1280, height = 720) {
|
|
13
|
+
if (!/^https?:\/\//i.test(url)) url = `https://${url}`;
|
|
14
|
+
|
|
15
|
+
const { data } = await axios.get(
|
|
16
|
+
`https://image.thum.io/get/width/${width}/crop/${height}/${url}`,
|
|
17
|
+
{
|
|
18
|
+
responseType: 'arraybuffer',
|
|
19
|
+
timeout: 20000,
|
|
20
|
+
headers: { 'User-Agent': UA }
|
|
21
|
+
}
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
if (!data || data.byteLength < 5000) throw new Error('No se pudo capturar la página.');
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
url,
|
|
28
|
+
width,
|
|
29
|
+
height,
|
|
30
|
+
size: `${(data.byteLength / 1024).toFixed(1)} KB`,
|
|
31
|
+
buffer: Buffer.from(data)
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
package/test.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { phDownloadBuffer } from './src/nsfw/phdl.js';
|
|
2
|
-
import { writeFile } from 'fs/promises';
|
|
3
|
-
|
|
4
|
-
const URL_TEST = 'https://www.pornhub.com/view_video.php?viewkey=69b03c0796d02';
|
|
5
|
-
const OUT_PATH = '/sdcard/test-phdl.mp4';
|
|
6
|
-
|
|
7
|
-
console.log('🔍 Testeando phDownloadBuffer...\n');
|
|
8
|
-
|
|
9
|
-
try {
|
|
10
|
-
console.log('⏳ Fetcheando página y convirtiendo HLS con ffmpeg...');
|
|
11
|
-
const result = await phDownloadBuffer(URL_TEST, '480');
|
|
12
|
-
console.log('✅ Conversión exitosa:\n');
|
|
13
|
-
console.log(` 📹 Título: ${result.title}`);
|
|
14
|
-
console.log(` ⏱️ Duración: ${result.duration || '—'}`);
|
|
15
|
-
console.log(` 🎯 Calidad: ${result.quality}`);
|
|
16
|
-
console.log(` 📦 Buffer: ${(result.buffer.length / 1024 / 1024).toFixed(2)} MB`);
|
|
17
|
-
await writeFile(OUT_PATH, result.buffer);
|
|
18
|
-
console.log(`\n💾 Video guardado en: ${OUT_PATH}`);
|
|
19
|
-
} catch (e) {
|
|
20
|
-
console.log(`❌ Error: ${e.message}`);
|
|
21
|
-
}
|