@soyaxell09/zenbot-scraper 1.0.13 → 1.1.0

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
@@ -27,29 +27,19 @@ src/
27
27
  ### YouTube
28
28
 
29
29
  ```js
30
- import { ytSearch, ytDownload, ytInfo } from '@soyaxell09/zenbot-scraper'
30
+ import { ytSearch, ytDownload, ytInfo, getFileSize } from '@soyaxell09/zenbot-scraper'
31
31
 
32
32
  const results = await ytSearch('bad bunny', 5)
33
33
  // → [{ id, title, url, thumbnail, duration, views, channel, published }]
34
34
 
35
- const video = await ytDownload('https://youtu.be/dQw4w9WgXcQ', 'video', '360p')
36
- const audio = await ytDownload('nicki nicole wapo traketero', 'mp3')
37
- // → { title, author, thumbnail, type, url, duration, ... }
38
- ```
39
-
40
- ### YouTube v2 (via ytdown.to — más calidades)
41
-
42
- ```js
43
- import { ytDownloadV2, ytInfoV2, getFileSizeV2 } from '@soyaxell09/zenbot-scraper'
44
-
45
- const info = await ytInfoV2('https://youtu.be/dQw4w9WgXcQ')
46
- // → { title, uploader, views, thumb, qualities: [{ id, type, quality, size, sizeB, duration }] }
35
+ const info = await ytInfo('https://youtu.be/dQw4w9WgXcQ')
36
+ // { id, title, uploader, views, thumb, duration, qualities: [{ id, type, quality, size, sizeB, duration }] }
47
37
 
48
- const video = await ytDownloadV2('https://youtu.be/dQw4w9WgXcQ', 'video', '1080p')
49
- const audio = await ytDownloadV2('https://youtu.be/dQw4w9WgXcQ', 'mp3', '128k')
38
+ const video = await ytDownload('https://youtu.be/dQw4w9WgXcQ', 'video', '360p')
39
+ const audio = await ytDownload('https://youtu.be/dQw4w9WgXcQ', 'mp3', '128k')
50
40
  // → { title, uploader, views, thumb, type, quality, size, sizeB, duration, url }
51
41
 
52
- const size = await getFileSizeV2('https://example.com/file.mp4')
42
+ const size = await getFileSize('https://example.com/file.mp4')
53
43
  // → '14.5 MB'
54
44
  ```
55
45
 
@@ -59,29 +49,28 @@ const size = await getFileSizeV2('https://example.com/file.mp4')
59
49
  import { tiktokDownload, tiktokInfo } from '@soyaxell09/zenbot-scraper'
60
50
 
61
51
  const result = await tiktokDownload('https://www.tiktok.com/@user/video/123')
62
- // → { nowatermark, watermark, audio, music: { title, author, url }, stats: { plays, likes, comments, shares } }
52
+ // → { id, title, author, thumbnail, duration, nowatermark, watermark, audio, images, music, plays, likes, comments, shares }
63
53
  ```
64
54
 
65
55
  ### Instagram
66
56
 
67
57
  ```js
68
- import { igDownload } from '@soyaxell09/zenbot-scraper'
58
+ import { igDownload, igReelDownload, igStalk, igStories } from '@soyaxell09/zenbot-scraper'
69
59
 
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
- ```
60
+ const post = await igDownload('https://www.instagram.com/p/ABC123/')
61
+ // → { type, items: [{ type, url }] }
75
62
 
76
- ### Instagram
63
+ const reel = await igReelDownload('https://www.instagram.com/reel/ABC123/')
64
+ // → { type, items: [{ type, url }] }
77
65
 
78
- ```js
79
- import { igDownload } from '@soyaxell09/zenbot-scraper'
66
+ const story = await igDownload('https://www.instagram.com/stories/user/123456789/')
67
+ // { type: 'story', items: [{ type, url }] }
80
68
 
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)
69
+ const perfil = await igStalk('siamusic')
70
+ // → { username, fullName, bio, followers, following, posts, isPrivate, isVerified, avatar, url }
71
+
72
+ const stories = await igStories('siamusic')
73
+ // → { username, items: [{ type, url }] }
85
74
  ```
86
75
 
87
76
  ### Facebook
@@ -90,7 +79,7 @@ const result = await igDownload('https://www.instagram.com/reel/xxx/')
90
79
  import { fbDownload } from '@soyaxell09/zenbot-scraper'
91
80
 
92
81
  const result = await fbDownload('https://www.facebook.com/watch?v=123')
93
- // → { hd, sd, thumb, title }
82
+ // → { videoId, title, hd, sd, thumb, source }
94
83
  ```
95
84
 
96
85
  ### Twitter / X
@@ -98,13 +87,31 @@ const result = await fbDownload('https://www.facebook.com/watch?v=123')
98
87
  ```js
99
88
  import { tweetInfo, tweetDownload } from '@soyaxell09/zenbot-scraper'
100
89
 
101
- const info = await tweetInfo('https://x.com/user/status/123')
90
+ const info = await tweetInfo('https://x.com/user/status/123')
102
91
  // → { id, text, lang, createdAt, likes, replies, author: { name, username, avatar }, hashtags, mentions, medias }
103
92
 
104
93
  const media = await tweetDownload('https://x.com/user/status/123')
105
94
  // → { id, text, author, videos: [{ type, url, thumbnail, variants }], photos: [{ type, url, width, height }] }
106
95
  ```
107
96
 
97
+ ### Threads
98
+
99
+ ```js
100
+ import { threadsDownload } from '@soyaxell09/zenbot-scraper'
101
+
102
+ const result = await threadsDownload('https://www.threads.net/t/xxx')
103
+ // → { title, medias: [{ type, url }] }
104
+ ```
105
+
106
+ ### Spotify (SpotiDown)
107
+
108
+ ```js
109
+ import { spotidownTrack } from '@soyaxell09/zenbot-scraper'
110
+
111
+ const result = await spotidownTrack('https://open.spotify.com/track/xxx')
112
+ // → { name, artist, album, year, duration, cover, mp3, coverHd }
113
+ ```
114
+
108
115
  ### MediaFire
109
116
 
110
117
  ```js
@@ -149,16 +156,6 @@ const file = await gdriveDownload('https://drive.google.com/file/d/1ABC.../view'
149
156
  // → { fileId, buffer, contentType, size, url }
150
157
  ```
151
158
 
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
-
162
159
  ---
163
160
 
164
161
  ## 🔍 Search
@@ -184,10 +181,17 @@ const tracks = await spotify('bad bunny', 'tracks', 5)
184
181
  ### Tenor (GIFs)
185
182
 
186
183
  ```js
187
- import { giphy } from '@soyaxell09/zenbot-scraper'
184
+ import { giphy, gifNext, giphyBuffer } from '@soyaxell09/zenbot-scraper'
188
185
 
189
186
  const gifs = await giphy('funny cat', 5)
190
187
  // → [{ id, title, url, gif, preview, mp4, width, height }]
188
+
189
+ const next = await gifNext('funny cat')
190
+ // → { id, title, url, gif, preview, mp4, width, height }
191
+ // Mantiene una cola paginada — cada llamada devuelve el siguiente GIF sin repetir
192
+
193
+ const buf = await giphyBuffer('funny cat')
194
+ // → { buffer, mimetype, title, url }
191
195
  ```
192
196
 
193
197
  ### Pinterest
@@ -195,8 +199,13 @@ const gifs = await giphy('funny cat', 5)
195
199
  ```js
196
200
  import { pinsearch, pinimg, pinvid } from '@soyaxell09/zenbot-scraper'
197
201
 
198
- const imgs = await pinsearch('anime wallpaper', 5)
202
+ const imgs = await pinsearch('anime wallpaper', 50)
203
+ // → [{ index, image, url, pinId }]
204
+ // Paginación infinita — carga más resultados automáticamente si limit > resultados en caché
205
+
199
206
  const pin = await pinimg('https://www.pinterest.com/pin/123/')
207
+ // → { id, title, description, altText, image, images, width, height, dominantColor, saves, repins, createdAt, tags, domain, link, board, creator, pinner, url }
208
+
200
209
  const vids = await pinvid('anime', 5)
201
210
  ```
202
211
 
@@ -277,7 +286,10 @@ const w = await weather('Buenos Aires')
277
286
  import { qrGenerate, qrRead } from '@soyaxell09/zenbot-scraper'
278
287
 
279
288
  const qr = await qrGenerate('https://github.com/axeldev09', 300)
289
+ // → { url, buffer, size, text }
290
+
280
291
  const result = await qrRead('https://example.com/qr.png')
292
+ // → { text, type }
281
293
  ```
282
294
 
283
295
  ### Acortador de URLs
@@ -286,7 +298,10 @@ const result = await qrRead('https://example.com/qr.png')
286
298
  import { shortenUrl, expandUrl } from '@soyaxell09/zenbot-scraper'
287
299
 
288
300
  const short = await shortenUrl('https://github.com/axeldev09/zenbot-scraper')
301
+ // → { short, source }
302
+
289
303
  const expanded = await expandUrl('https://is.gd/xxxxx')
304
+ // → { original, expanded }
290
305
  ```
291
306
 
292
307
  ### Noticias
@@ -295,6 +310,8 @@ const expanded = await expandUrl('https://is.gd/xxxxx')
295
310
  import { news, newsCategories } from '@soyaxell09/zenbot-scraper'
296
311
 
297
312
  const n = await news('es', 5)
313
+ // → { category, source, items: [{ title, description, url, image, published, source }] }
314
+
298
315
  const cats = newsCategories()
299
316
  // → ['es', 'en', 'pt', 'tech', 'sports', 'science', 'world']
300
317
  ```
@@ -325,98 +342,65 @@ const r3 = await upload(buffer, 'video.mp4')
325
342
 
326
343
  > ⚠️ Solo para bots con verificación de edad. Usá responsablemente.
327
344
 
328
- ### XNXX Search
345
+ ### XNXX
329
346
 
330
347
  ```js
331
- import { xnxxSearch } from '@soyaxell09/zenbot-scraper'
348
+ import { xnxxSearch, xnxxDownload } from '@soyaxell09/zenbot-scraper'
332
349
 
333
350
  const results = await xnxxSearch('query', 10)
334
351
  // → [{ title, url, thumb, preview, duration, views, quality, uploader }]
335
- ```
336
-
337
- ### XNXX Download
338
-
339
- ```js
340
- import { xnxxDownload } from '@soyaxell09/zenbot-scraper'
341
352
 
342
353
  const result = await xnxxDownload('https://www.xnxx.com/video-xxx/...')
343
354
  // → { title, thumb, uploader, duration, views, uploadDate, download: { low, high, hls } }
344
355
  ```
345
356
 
346
- ### PornHub Search
357
+ ### PornHub
347
358
 
348
359
  ```js
349
- import { phSearch } from '@soyaxell09/zenbot-scraper'
360
+ import { phSearch, phDownload, phDownloadBuffer } from '@soyaxell09/zenbot-scraper'
350
361
 
351
362
  const results = await phSearch('query', 10)
352
363
  // → [{ title, url, thumb, preview, duration, vkey }]
353
- ```
354
364
 
355
- ### PornHub Download
356
-
357
- ```js
358
- import { phDownload, phDownloadBuffer } from '@soyaxell09/zenbot-scraper'
359
-
360
- // Solo info + HLS streams
361
365
  const result = await phDownload('https://www.pornhub.com/view_video.php?viewkey=xxx')
362
366
  // → { title, thumb, duration, uploadDate, hls: { '1080p', '720p', '480p', '240p' } }
363
367
 
364
- // Convertir a mp4 con ffmpeg (requiere ffmpeg instalado)
365
368
  const video = await phDownloadBuffer('https://www.pornhub.com/view_video.php?viewkey=xxx', '720')
366
369
  // → { title, thumb, duration, uploadDate, buffer, quality }
367
370
  ```
368
371
 
369
- ### XVideos Search
372
+ ### XVideos
370
373
 
371
374
  ```js
372
- import { xvideosSearch } from '@soyaxell09/zenbot-scraper'
375
+ import { xvideosSearch, xvideosDownload } from '@soyaxell09/zenbot-scraper'
373
376
 
374
377
  const results = await xvideosSearch('query', 10)
375
378
  // → [{ title, url, thumb, preview, duration, views, quality }]
376
- ```
377
-
378
- ### XVideos Download
379
-
380
- ```js
381
- import { xvideosDownload } from '@soyaxell09/zenbot-scraper'
382
379
 
383
380
  const result = await xvideosDownload('https://www.xvideos.com/video.xxx/...')
384
381
  // → { title, thumb, duration, views, uploadDate, download: { low, high, hls } }
385
382
  ```
386
383
 
387
- ### XHamster Search
384
+ ### XHamster
388
385
 
389
386
  ```js
390
- import { xhamsterSearch } from '@soyaxell09/zenbot-scraper'
387
+ import { xhamsterSearch, xhamsterDownload, xhamsterDownloadBuffer } from '@soyaxell09/zenbot-scraper'
391
388
 
392
389
  const results = await xhamsterSearch('query', 10)
393
390
  // → [{ title, url, thumb, preview, duration, views }]
394
- ```
395
-
396
- ### XHamster Download
397
391
 
398
- ```js
399
- import { xhamsterDownload, xhamsterDownloadBuffer } from '@soyaxell09/zenbot-scraper'
400
-
401
- // Info + calidades HLS
402
392
  const result = await xhamsterDownload('https://xhamster.com/videos/...')
403
393
  // → { title, thumb, duration, views, download: { '144p', '240p', '480p', '720p', '1080p' } }
404
394
 
405
- // Convertir a mp4 con ffmpeg (requiere ffmpeg instalado)
406
395
  const video = await xhamsterDownloadBuffer('https://xhamster.com/videos/...', '480p')
407
396
  // → { title, thumb, duration, views, buffer, quality }
408
397
  ```
409
398
 
410
- ### Rule34 (yande.re + paheal)
399
+ ### Rule34 (paheal)
411
400
 
412
401
  ```js
413
- import { rule34Search, rule34Random } from '@soyaxell09/zenbot-scraper'
414
-
415
- // Buscar por tags (usa yande.re, cae a paheal si no hay resultados)
416
- const results = await rule34Search('cat_girl', 10)
417
- // → [{ id, url, full, preview, tags, score, width, height }]
402
+ import { rule34Random } from '@soyaxell09/zenbot-scraper'
418
403
 
419
- // Imagen random (usa paheal, con o sin tags)
420
404
  const random = await rule34Random('cat_girl')
421
405
  const any = await rule34Random()
422
406
  // → { id, url, full, preview, tags, score, width, height }
@@ -429,7 +413,7 @@ const any = await rule34Random()
429
413
  ```js
430
414
  import { ytDownload, tiktokDownload, igDownload, translate, weather } from '@soyaxell09/zenbot-scraper'
431
415
  import { ytDownload, fbDownload, igDownload } from '@soyaxell09/zenbot-scraper/scrapers'
432
- import { googleSearch, spotify } from '@soyaxell09/zenbot-scraper/search'
416
+ import { googleSearch, spotify, gifNext, pinsearch } from '@soyaxell09/zenbot-scraper/search'
433
417
  import { translate, weather, news, screenshot } from '@soyaxell09/zenbot-scraper/tools'
434
418
  import { xnxxSearch, phSearch, rule34Random, xhamsterSearch } from '@soyaxell09/zenbot-scraper/nsfw'
435
419
  ```
@@ -439,7 +423,7 @@ import { xnxxSearch, phSearch, rule34Random, xhamsterSearch } from '@soy
439
423
  ## ⚙️ Requisitos
440
424
 
441
425
  - Node.js >= 18.0.0
442
- - Dependencias: `axios`, `cheerio`, `@distube/ytdl-core`, `form-data`
426
+ - Dependencias: `axios`, `cheerio`, `yt-search`, `form-data`
443
427
  - Para `phDownloadBuffer` y `xhamsterDownloadBuffer`: requiere `ffmpeg` instalado en el sistema
444
428
 
445
429
  ---
@@ -452,7 +436,7 @@ import { xnxxSearch, phSearch, rule34Random, xhamsterSearch } from '@soy
452
436
 
453
437
  ## ⭐ Apoyá el proyecto
454
438
 
455
- Si este módulo te fue útil, dejá una ⭐ en el repositorio. Le pusimos mucho esfuerzo y tiempo para que funcione bien.
439
+ Si este módulo te fue útil, dejá una ⭐ en el repositorio.
456
440
 
457
441
  **Dejá los créditos** si usás este módulo en tu bot — es lo único que se pide 🙏
458
442
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soyaxell09/zenbot-scraper",
3
- "version": "1.0.13",
3
+ "version": "1.1.0",
4
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",
@@ -51,7 +51,6 @@
51
51
  "author": "AxelDev09",
52
52
  "license": "MIT",
53
53
  "dependencies": {
54
- "@distube/ytdl-core": "^4.16.0",
55
54
  "axios": "^1.6.0",
56
55
  "cheerio": "^1.0.0",
57
56
  "node-fetch": "^3.3.2",
package/src/index.js CHANGED
@@ -5,20 +5,20 @@
5
5
  * Deja los créditos we 🗣️
6
6
  */
7
7
 
8
- export { ytInfo, ytDownload, ytSearch } from './scrapers/youtube.js'
9
- export { ytInfoV2, ytDownloadV2, getFileSizeV2 } from './scrapers/youtubev2.js'
8
+ export { ytInfo, ytDownload, ytSearch, getFileSize } from './scrapers/youtube.js'
10
9
  export { tiktokInfo, tiktokDownload } from './scrapers/tiktok.js'
11
- export { igDownload } from './scrapers/instagram.js'
10
+ export { igDownload, igReelDownload, igStalk, igStories } from './scrapers/instagram.js'
11
+ export { threadsDownload } from './scrapers/threads.js'
12
12
  export { spotidownTrack } from './scrapers/spotidown.js'
13
13
  export { fbDownload } from './scrapers/facebook.js'
14
14
  export { tweetInfo, tweetDownload } from './scrapers/twitter.js'
15
15
  export { mediafireInfo } from './scrapers/mediafire.js'
16
16
  export { githubInfo, githubRelease, githubContents, githubSearch } from './scrapers/github.js'
17
17
  export { apkSearch, apkInfo } from './scrapers/apk.js'
18
- export { gdriveInfo, gdriveDownload } from './scrapers/gdrive.js'
18
+ export { gdriveInfo, gdriveDownload } from './scrapers/gdrive.js'
19
19
  export { googleSearch } from './search/google.js'
20
20
  export { spotify } from './search/spotify.js'
21
- export { giphy } from './search/giphy.js'
21
+ export { giphy, gifNext, giphyBuffer } from './search/giphy.js'
22
22
  export { pinsearch, pinimg, pinvid } from './search/pinterest.js'
23
23
  export { stickerSearch } from './search/stickersearch.js'
24
24
  export { animeImage } from './search/anime.js'
@@ -38,6 +38,6 @@ export { phSearch } from './nsfw/
38
38
  export { phDownload, phDownloadBuffer } from './nsfw/phdl.js'
39
39
  export { xvideosSearch } from './nsfw/xvideossearch.js'
40
40
  export { xvideosDownload } from './nsfw/xvideosdl.js'
41
- export { rule34Search, rule34Random } from './nsfw/rule34.js'
41
+ export { rule34Random } from './nsfw/rule34.js'
42
42
  export { xhamsterSearch } from './nsfw/xhamstersearch.js'
43
43
  export { xhamsterDownload, xhamsterDownloadBuffer } from './nsfw/xhammerdl.js'
package/src/nsfw/index.js CHANGED
@@ -11,7 +11,6 @@ export { phSearch } from './phsearch.js'
11
11
  export { phDownload, phDownloadBuffer } from './phdl.js'
12
12
  export { xvideosSearch } from './xvideossearch.js'
13
13
  export { xvideosDownload } from './xvideosdl.js'
14
- export { rule34Search, rule34Random } from './rule34.js'
14
+ export { rule34Random } from './rule34.js'
15
15
  export { xhamsterSearch } from './xhamstersearch.js'
16
- export { xhamsterDownload, xhamsterDownloadBuffer } from './xhammerdl.js'
17
- export { hentaiImgSearch, hentaiImgRandom } from './hentaiimg.js'
16
+ export { xhamsterDownload, xhamsterDownloadBuffer } from './xhammerdl.js'
@@ -10,29 +10,6 @@ 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
-
26
- async function yandereSearch(tags, limit = 10) {
27
- const { data } = await axios.get('https://yande.re/post.json', {
28
- params: { tags, limit },
29
- headers: { 'User-Agent': UA },
30
- timeout: 12000
31
- });
32
- if (!Array.isArray(data) || !data.length) return [];
33
- return data.map(mapYande);
34
- }
35
-
36
13
  function parsePaheal(xml) {
37
14
  const $ = cheerio.load(xml, { xmlMode: true });
38
15
  const items = [];
@@ -53,22 +30,6 @@ function parsePaheal(xml) {
53
30
  return items;
54
31
  }
55
32
 
56
- async function pahealSearch(tags, limit = 10) {
57
- const { data } = await axios.get('https://rule34.paheal.net/api/danbooru/find_posts', {
58
- params: { tags, limit },
59
- headers: { 'User-Agent': UA },
60
- timeout: 12000
61
- });
62
- return parsePaheal(data);
63
- }
64
-
65
- export async function rule34Search(tags, limit = 10) {
66
- let items = await yandereSearch(tags, limit);
67
- if (!items.length) items = await pahealSearch(tags, limit);
68
- if (!items.length) throw new Error('Sin resultados para esos tags.');
69
- return items;
70
- }
71
-
72
33
  export async function rule34Random(tags = null) {
73
34
  for (let intento = 0; intento < 3; intento++) {
74
35
  const pid = Math.floor(Math.random() * 5);
@@ -1,5 +1,4 @@
1
- export { ytInfo, ytDownload, ytSearch } from './youtube.js'
2
- export { ytInfoV2, ytDownloadV2, getFileSizeV2 } from './youtubev2.js'
1
+ export { ytInfo, ytDownload, ytSearch, getFileSize } from './youtube.js'
3
2
  export { tiktokInfo, tiktokDownload } from './tiktok.js'
4
3
  export { fbDownload } from './facebook.js'
5
4
  export { tweetInfo, tweetDownload } from './twitter.js'
@@ -7,5 +6,6 @@ export { mediafireInfo } from './media
7
6
  export { githubInfo, githubRelease, githubContents, githubSearch } from './github.js'
8
7
  export { apkSearch, apkInfo } from './apk.js'
9
8
  export { gdriveInfo, gdriveDownload } from './gdrive.js'
10
- export { igDownload } from './instagram.js'
9
+ export { igDownload, igReelDownload, igStalk, igStories } from './instagram.js'
10
+ export { threadsDownload } from './threads.js'
11
11
  export { spotidownTrack } from './spotidown.js'
@@ -0,0 +1,55 @@
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
+ const BASE = 'https://sssthreads.app';
12
+
13
+ function decodeToken(token) {
14
+ const parts = token.split('.');
15
+ const pad = p => p + '='.repeat((4 - p.length % 4) % 4);
16
+ try { return JSON.parse(Buffer.from(pad(parts[0]), 'base64').toString()); }
17
+ catch { return null; }
18
+ }
19
+
20
+ export async function threadsDownload(url) {
21
+ if (!url.includes('threads.net') && !url.includes('threads.com'))
22
+ throw new Error('URL inválida. Debe ser un link de Threads.');
23
+
24
+ const form = new FormData();
25
+ form.append('action', 'fetch_source');
26
+ form.append('url', url);
27
+
28
+ const { data } = await axios.post(BASE + '/', form, {
29
+ headers: {
30
+ 'User-Agent': UA,
31
+ 'Referer': BASE + '/',
32
+ 'Origin': BASE,
33
+ },
34
+ timeout: 20000,
35
+ validateStatus: () => true
36
+ });
37
+
38
+ if (!data?.success || !data?.token)
39
+ throw new Error(data?.error || 'No se pudo obtener el contenido. Verificá que el post sea público.');
40
+
41
+ const payload = decodeToken(data.token);
42
+ if (!payload?.v)
43
+ throw new Error('No se encontró contenido descargable en ese post.');
44
+
45
+ const mediaUrl = payload.v.replace(/&amp;/g, '&');
46
+ const thumb = payload.t?.replace(/&amp;/g, '&') || null;
47
+ const type = mediaUrl.includes('.mp4') || mediaUrl.includes('/v/') ? 'video' : 'image';
48
+
49
+ return {
50
+ type,
51
+ url: mediaUrl,
52
+ thumb,
53
+ downloadUrl: `${BASE}/download.php?tk=${encodeURIComponent(data.token)}`,
54
+ }
55
+ }