@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 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 } from '@soyaxell09/zenbot-scraper/search'
370
- import { translate, weather, news } from '@soyaxell09/zenbot-scraper/tools'
371
- import { xnxxSearch, phSearch, rule34Random } from '@soyaxell09/zenbot-scraper/nsfw'
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.11",
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 } 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'
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'
@@ -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(p => ({
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
+ }
@@ -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 HEADERS = {
12
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36',
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
- async function searchPage(query) {
21
- const res = await axios.get(
22
- `https://apkpure.com/search?q=${encodeURIComponent(query)}`,
23
- { headers: HEADERS, timeout: 15000 }
24
- )
25
- return cheerio.load(res.data)
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
- const name = $(el).find('.p1').text().trim()
39
- const dev = $(el).find('.p2').text().trim()
40
- const date = $(el).find('.date').text().trim()
41
- const icon = $(el).closest('.search-result').find('img').first().attr('src') || ''
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
- const appHref = $(el).closest('.search-result')
44
- .find('a[href]').toArray()
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 pkg = extractPkg(appHref)
49
- if (!name || !pkg || seen.has(pkg)) return
50
- seen.add(pkg)
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
- const appUrl = appHref.startsWith('http') ? appHref : `https://apkpure.com${appHref}`
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: `https://d.apkpure.com/b/XAPK/${pkg}?version=latest`,
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 (pkgOrUrl.includes('apkpure.com')) {
72
- pkg = extractPkg(pkgOrUrl) || pkgOrUrl
73
- }
74
-
75
- // Buscar por última parte del pkg (ej: "whatsapp" de "com.whatsapp")
76
- const shortName = pkg.split('.').pop()
77
- const $ = await searchPage(shortName)
78
-
79
- // Buscar el item que tenga el pkg correcto
80
- let found = null
81
- $('.brand-info-top').each((_, el) => {
82
- if (found) return false
83
- const appHref = $(el).closest('.search-result')
84
- .find('a[href]').toArray()
85
- .map(a => $(a).attr('href'))
86
- .find(h => h?.includes(pkg))
87
- if (appHref) found = el
88
- })
89
-
90
- const el = found ? $(found) : $('.brand-info-top').first()
91
- const name = el.find('.p1').text().trim()
92
- const dev = el.find('.p2').text().trim()
93
- const date = el.find('.date').text().trim()
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
- date,
98
+ version,
99
+ size,
107
100
  icon,
108
- download: `https://d.apkpure.com/b/XAPK/${pkg}?version=latest`,
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: `https://apkpure.com/search?q=${shortName}`,
111
- }
104
+ url: `${BASE}/${shortName}/${pkg}`,
105
+ };
112
106
  }
@@ -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(/&amp;/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(/&amp;/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
+ }
@@ -13,3 +13,4 @@ export { qrGenerate, qrRead } from './qr.js'
13
13
  export { shortenUrl, expandUrl } from './urlshortener.js'
14
14
  export { news, newsCategories } from './news.js'
15
15
  export { upload } from './upload.js'
16
+ export { screenshot } from './screenshot.js'
@@ -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
- }