@soyaxell09/zenbot-scraper 1.1.7 → 1.1.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soyaxell09/zenbot-scraper",
3
- "version": "1.1.7",
3
+ "version": "1.1.9",
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",
package/src/index.js CHANGED
@@ -6,18 +6,23 @@
6
6
  */
7
7
 
8
8
  export { ytInfo, ytDownload, ytSearch, getFileSize } from './scrapers/youtube.js'
9
+ export { ddownr, ddownrFormats } from './scrapers/ddownr.js'
9
10
  export { youtubeDownload } from './scrapers/youtubev2.js'
10
- export { tiktokInfo, tiktokDownload } from './scrapers/tiktok.js'
11
+ export { tiktokInfo, tiktokDownload, tiktokSearch } from './scrapers/tiktok.js'
11
12
  export { snaptikDownload } from './scrapers/snaptik.js'
12
13
  export { deezerSearch, deezerTrack } from './scrapers/deezer.js'
13
14
  export { igDownload, igReelDownload, igStalk, igStories } from './scrapers/instagram.js'
14
15
  export { threadsDownload } from './scrapers/threads.js'
16
+ export { bookSearch, bookInfo }
17
+ from './scrapers/books.js'
18
+
15
19
  export { spotidownTrack } from './scrapers/spotidown.js'
16
20
  export { fbDownload } from './scrapers/facebook.js'
17
21
  export { tweetInfo, tweetDownload } from './scrapers/twitter.js'
18
22
  export { mediafireInfo } from './scrapers/mediafire.js'
19
23
  export { githubInfo, githubRelease, githubContents, githubSearch } from './scrapers/github.js'
20
24
  export { apkSearch, apkInfo } from './scrapers/apk.js'
25
+ export { aptoideSearch, aptoideInfo } from './scrapers/aptoide.js'
21
26
  export { gdriveInfo, gdriveDownload } from './scrapers/gdrive.js'
22
27
  export { googleSearch } from './search/google.js'
23
28
  export { giphy, gifNext, giphyBuffer } from './search/giphy.js'
@@ -0,0 +1,80 @@
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 API_BASE = 'https://ws75.aptoide.com/api/7'
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
+ function extractSize(app) {
14
+ const bytes = app.file?.filesize || app.file?.size || app.size || null
15
+ if (!bytes || bytes <= 0) return 'N/A'
16
+ if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'
17
+ return (bytes / (1024 * 1024)).toFixed(2) + ' MB'
18
+ }
19
+
20
+ export async function aptoideSearch(query, limit = 10) {
21
+ try {
22
+ const { data } = await axios.get(`${API_BASE}/apps/search`, {
23
+ params: { query, limit },
24
+ headers: { 'User-Agent': UA },
25
+ timeout: 10000
26
+ })
27
+
28
+ const list = data.datalist?.list || []
29
+ return list.slice(0, limit).map(app => ({
30
+ title: app.name,
31
+ id: app.package,
32
+ url: `https://es.aptoide.com/app/${app.package}`,
33
+ thumb: app.icon,
34
+ version: app.file?.vername || 'N/A',
35
+ size: extractSize(app),
36
+ rating: app.stats?.rating?.avg?.toFixed(1) || '0.0'
37
+ }))
38
+ } catch {
39
+ return []
40
+ }
41
+ }
42
+
43
+ export async function aptoideInfo(packageName) {
44
+ try {
45
+ const { data } = await axios.get(`${API_BASE}/app/get`, {
46
+ params: { package_name: packageName },
47
+ headers: { 'User-Agent': UA },
48
+ timeout: 10000
49
+ })
50
+
51
+ const app = data.nodes?.meta?.data || data.datalist?.list?.[0]
52
+ if (!app) return null
53
+
54
+ const rawDesc = app.media?.description || app.description || ''
55
+
56
+ return {
57
+ title: app.name,
58
+ id: app.package,
59
+ version: app.file?.vername || 'N/A',
60
+ size: extractSize(app),
61
+ thumb: app.icon,
62
+ updated: app.updated,
63
+ downloads: app.stats?.downloads?.toLocaleString() || '0',
64
+ rating: app.stats?.rating?.avg?.toFixed(1) || '0.0',
65
+ total_reviews: app.stats?.rating?.total || 0,
66
+ is_safe: app.file?.malware?.rank === 'TRUSTED',
67
+ security_reason: app.file?.malware?.reason?.sig || 'Verificado',
68
+ min_android: app.file?.hardware?.sdk ? `Android ${app.file.hardware.sdk}+` : 'Varía',
69
+ arch: app.file?.hardware?.cpus?.join(', ') || 'Universal',
70
+ md5: app.file?.md5sum,
71
+ screenshots: app.media?.screenshots?.map(s => s.url) || [],
72
+ video: app.media?.videos?.[0]?.url || null,
73
+ description: rawDesc.replace(/<[^>]*>/g, '').trim().slice(0, 1000),
74
+ download: app.file?.path || null,
75
+ url: `https://es.aptoide.com/app/${app.package}`
76
+ }
77
+ } catch {
78
+ return null
79
+ }
80
+ }
@@ -0,0 +1,136 @@
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 BASE = 'https://ww3.lectulandia.co'
12
+ 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'
13
+ const HDRS = {
14
+ 'User-Agent': UA,
15
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
16
+ 'Accept-Language': 'es-ES,es;q=0.9',
17
+ }
18
+
19
+ async function resolveAntUpload(url) {
20
+ if (!url || !url.includes('antupload.com/file/')) return null
21
+
22
+ try {
23
+ const res = await axios.get(url, { headers: HDRS, timeout: 12000 })
24
+ const cookies = res.headers['set-cookie']?.map(c => c.split(';')[0]).join('; ') || ''
25
+ const $ = cheerio.load(res.data)
26
+
27
+ const finalHref = $('#downloadB').attr('href') || $('a[href*="/filed/"]').first().attr('href')
28
+
29
+ if (finalHref) {
30
+ const absoluteLink = finalHref.startsWith('http') ? finalHref : 'https://www.antupload.com' + finalHref
31
+ return {
32
+ url: absoluteLink,
33
+ headers: {
34
+ 'User-Agent': UA,
35
+ 'Referer': url,
36
+ 'Cookie': cookies
37
+ }
38
+ }
39
+ }
40
+ } catch (error) {}
41
+ return null
42
+ }
43
+
44
+ export async function bookSearch(query, limit = 10) {
45
+ const { data } = await axios.get(`${BASE}/?s=${encodeURIComponent(query)}`, {
46
+ headers: HDRS, timeout: 12000
47
+ })
48
+ const $ = cheerio.load(data)
49
+ const results = []
50
+
51
+ $('article.card').each((i, el) => {
52
+ if (i >= limit) return false
53
+ const href = $(el).find('a.card-click-target').attr('href') || ''
54
+ const url = href.startsWith('http') ? href : BASE + href
55
+ const title = $(el).find('img.cover').attr('alt')?.trim()
56
+ || $(el).find('h2 a').text().trim()
57
+ const thumb = $(el).find('img.cover').attr('src') || ''
58
+ const author = $(el).find('a[href*="/autor/"]').first().text().trim()
59
+ const genre = $(el).find('a[href*="/genero/"]').first().text().trim() || null
60
+
61
+ if (!title || !url) return
62
+ results.push({ title, author, thumb, url, genre })
63
+ })
64
+
65
+ if (!results.length) throw new Error('Sin resultados')
66
+ return results
67
+ }
68
+
69
+ export async function bookInfo(url) {
70
+ const { data } = await axios.get(url, { headers: HDRS, timeout: 12000 })
71
+ const $ = cheerio.load(data)
72
+
73
+ const title = $('#title').text().trim()
74
+ const author = $('#autor').text().replace(/^Autor:\s*/i, '').trim()
75
+ const genre = $('a[href*="/genero/"]').first().text().replace(/^Genero:\s*/i, '').trim() || null
76
+ const year = $('#publicado').text().replace(/^Publicado:\s*/i, '').trim() || null
77
+
78
+ let description = ''
79
+ $('#book .description, #content .description, .book .description').each((_, el) => {
80
+ const txt = $(el).text().trim()
81
+ if (txt.length > description.length) description = txt
82
+ })
83
+
84
+ if (!description) {
85
+ description = $('meta[property="og:description"]').attr('content')?.trim() || ''
86
+ }
87
+
88
+ const thumb = $('meta[property="og:image"]').attr('content')
89
+ || $('img.cover').attr('src')
90
+ || ''
91
+
92
+ const getAntUploadLink = (href) => {
93
+ if (!href) return null
94
+ const dMatch = href.match(/[?&]d=([^&]+)/)
95
+ if (dMatch && dMatch[1]) {
96
+ try {
97
+ const decoded = Buffer.from(dMatch[1], 'base64').toString('utf-8')
98
+ return 'https://www.antupload.com/file/' + decoded
99
+ } catch (e) {}
100
+ }
101
+ return href.startsWith('http') ? href : BASE + href
102
+ }
103
+
104
+ let epubLink = null
105
+ let pdfLink = null
106
+
107
+ $('a[href*="download.php"]').each((_, el) => {
108
+ const href = $(el).attr('href') || ''
109
+ const tVal = href.match(/[?&]t=(\d+)/)?.[1]
110
+
111
+ if (tVal === '1') epubLink = getAntUploadLink(href)
112
+ if (tVal === '2') pdfLink = getAntUploadLink(href)
113
+ })
114
+
115
+ if (!epubLink) epubLink = getAntUploadLink($('a#download1').attr('href') || '')
116
+ if (!pdfLink) pdfLink = getAntUploadLink($('a#download2').attr('href') || '')
117
+
118
+ const [finalEpub, finalPdf] = await Promise.all([
119
+ resolveAntUpload(epubLink),
120
+ resolveAntUpload(pdfLink)
121
+ ])
122
+
123
+ return {
124
+ title,
125
+ author,
126
+ description: description.replace(/\s+/g, ' ').slice(0, 1000),
127
+ genre,
128
+ year,
129
+ thumb,
130
+ url,
131
+ download: {
132
+ epub: finalEpub || null,
133
+ pdf: finalPdf || null,
134
+ }
135
+ }
136
+ }
@@ -0,0 +1,81 @@
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 API_KEY = 'dfcb6d76f2f6a9894gjkege8a4ab23';
12
+
13
+ export const ddownrFormats = {
14
+ video: ['360', '480', '720', '1080', '1440', '4k', '8k'],
15
+ audio: ['mp3', 'm4a', 'webm', 'aac', 'flac', 'opus', 'ogg', 'wav']
16
+ };
17
+
18
+ export async function ddownr(url, format = '720') {
19
+ try {
20
+ const requestedFormat = format.toString().toLowerCase();
21
+ const isValidFormat = ddownrFormats.video.includes(requestedFormat) || ddownrFormats.audio.includes(requestedFormat);
22
+
23
+ if (!isValidFormat) {
24
+ throw new Error(`Formato no soportado. Video: ${ddownrFormats.video.join(', ')} | Audio: ${ddownrFormats.audio.join(', ')}`);
25
+ }
26
+
27
+ const initUrl = `https://p.savenow.to/ajax/download.php?copyright=0&format=${requestedFormat}&url=${encodeURIComponent(url)}&api=${API_KEY}`;
28
+
29
+ const initRes = await axios.get(initUrl, {
30
+ headers: { 'User-Agent': UA },
31
+ timeout: 15000
32
+ });
33
+
34
+ if (!initRes.data || !initRes.data.success || !initRes.data.id) {
35
+ throw new Error('No se pudo iniciar la tarea de descarga');
36
+ }
37
+
38
+ const { id, title, info } = initRes.data;
39
+ const thumb = info?.image || '';
40
+
41
+ let downloadUrl = null;
42
+ let attempts = 0;
43
+ const maxAttempts = 40;
44
+
45
+ while (attempts < maxAttempts) {
46
+ await new Promise(resolve => setTimeout(resolve, 4000));
47
+
48
+ const progRes = await axios.get(`https://p.savenow.to/ajax/progress.php?id=${id}`, {
49
+ headers: { 'User-Agent': UA },
50
+ timeout: 10000
51
+ });
52
+
53
+ const { success, download_url, text } = progRes.data;
54
+
55
+ if (text === 'Error' || text === 'Failed') {
56
+ throw new Error('El servidor de conversión falló al procesar este video');
57
+ }
58
+
59
+ if (success === 1 && download_url) {
60
+ downloadUrl = download_url;
61
+ break;
62
+ }
63
+
64
+ attempts++;
65
+ }
66
+
67
+ if (!downloadUrl) {
68
+ throw new Error('El servidor tardó demasiado en procesar el video (Timeout)');
69
+ }
70
+
71
+ return {
72
+ title: title || info?.title || 'YouTube Video',
73
+ thumb: thumb,
74
+ format: requestedFormat,
75
+ download: downloadUrl
76
+ };
77
+
78
+ } catch (error) {
79
+ throw new Error(`Ddownr Error: ${error.message}`);
80
+ }
81
+ }
@@ -1,8 +1,6 @@
1
1
  /*
2
2
  * © Created by AxelDev09 🔥
3
3
  * GitHub: https://github.com/AxelDev09
4
- * Instagram: @axeldev09
5
- * Deja los créditos we 🗣️
6
4
  */
7
5
 
8
6
  import axios from 'axios'
@@ -1,15 +1,17 @@
1
1
  export { ytInfo, ytDownload, ytSearch, getFileSize } from './youtube.js'
2
2
  export { youtubeDownload } from './youtubev2.js'
3
- export { tiktokInfo, tiktokDownload } from './tiktok.js'
4
- export { tiktokSearch } from './tiktok.js'
5
- export { snaptikDownload } from './snaptik.js'
6
- export { deezerSearch, deezerTrack } from './deezer.js'
3
+ export { tiktokInfo, tiktokDownload, tiktokSearch } from './tiktok.js'
4
+ export { snaptikDownload } from './snaptik.js'
5
+ export { deezerSearch, deezerTrack } from './deezer.js'
7
6
  export { fbDownload } from './facebook.js'
8
7
  export { tweetInfo, tweetDownload } from './twitter.js'
9
8
  export { mediafireInfo } from './mediafire.js'
10
9
  export { githubInfo, githubRelease, githubContents, githubSearch } from './github.js'
11
10
  export { apkSearch, apkInfo } from './apk.js'
11
+ export { aptoideSearch, aptoideInfo } from './aptoide.js'
12
+ export { bookSearch, bookInfo } from './books.js'
12
13
  export { gdriveInfo, gdriveDownload } from './gdrive.js'
13
- export { igDownload, igReelDownload, igStalk, igStories } from './instagram.js'
14
+ export { igDownload, igReelDownload, igStalk, igStories } from './instagram.js'
14
15
  export { threadsDownload } from './threads.js'
15
- export { spotidownTrack } from './spotidown.js'
16
+ export { spotidownTrack } from './spotidown.js'
17
+ export { ddownr, ddownrFormats } from './ddownr.js'
@@ -48,14 +48,53 @@ export async function tiktokDownload(url) {
48
48
  url: d.music_info?.play || null,
49
49
  cover: d.music_info?.cover || '',
50
50
  },
51
- plays: d.play_count || 0,
52
- likes: d.digg_count || 0,
53
- comments: d.comment_count || 0,
54
- shares: d.share_count || 0,
55
- source: 'tikwm',
51
+ plays: d.play_count || 0,
52
+ likes: d.digg_count || 0,
53
+ comments: d.comment_count || 0,
54
+ shares: d.share_count || 0,
55
+ source: 'tikwm',
56
56
  }
57
57
  }
58
58
 
59
59
  export async function tiktokInfo(url) {
60
60
  return tiktokDownload(url)
61
61
  }
62
+
63
+ export async function tiktokSearch(query, limit = 5) {
64
+ if (!query?.trim()) throw new Error('Se requiere un término de búsqueda')
65
+ const count = Math.min(Math.max(1, limit), 20)
66
+ const res = await axios.get('https://tikwm.com/api/feed/search', {
67
+ params: { keywords: query, count, cursor: 0, HD: 1 },
68
+ headers: HEADERS,
69
+ timeout: 20000,
70
+ })
71
+
72
+ const data = res.data
73
+ if (data?.code !== 0 || !data?.data?.videos?.length)
74
+ throw new Error('tikwm search: ' + (data?.msg || 'sin resultados'))
75
+
76
+ return data.data.videos.map(d => ({
77
+ id: d.id || '',
78
+ title: d.title || '',
79
+ author: d.author?.nickname || d.author?.unique_id || '',
80
+ thumbnail: d.cover || d.origin_cover || '',
81
+ duration: d.duration || 0,
82
+ nowatermark: d.play || null,
83
+ watermark: d.wmplay || null,
84
+ audio: d.music || null,
85
+ images: Array.isArray(d.images) && d.images.length > 0
86
+ ? d.images.map(img => typeof img === 'string' ? img : img?.url || img?.cover || '')
87
+ : null,
88
+ music: {
89
+ title: d.music_info?.title || '',
90
+ author: d.music_info?.author || '',
91
+ url: d.music_info?.play || null,
92
+ cover: d.music_info?.cover || '',
93
+ },
94
+ plays: d.play_count || 0,
95
+ likes: d.digg_count || 0,
96
+ comments: d.comment_count || 0,
97
+ shares: d.share_count || 0,
98
+ source: 'tikwm',
99
+ }))
100
+ }
@@ -1,123 +0,0 @@
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://app.ytdown.to';
12
-
13
- async function getSession() {
14
- const res = await axios.get(`${BASE}/es23/`, {
15
- headers: { 'User-Agent': UA },
16
- timeout: 12000
17
- });
18
- const cookies = res.headers['set-cookie']?.map(c => c.split(';')[0]).join('; ') || '';
19
- return cookies;
20
- }
21
-
22
- async function fetchData(url) {
23
- const cookies = await getSession();
24
- const params = new URLSearchParams();
25
- params.append('url', url);
26
-
27
- const { data } = await axios.post(`${BASE}/proxy.php`, params, {
28
- headers: {
29
- 'User-Agent': UA,
30
- 'Content-Type': 'application/x-www-form-urlencoded',
31
- 'Referer': `${BASE}/es23/`,
32
- 'Origin': BASE,
33
- 'Cookie': cookies,
34
- 'X-Requested-With': 'XMLHttpRequest',
35
- 'Accept': 'application/json, text/javascript, */*; q=0.01'
36
- },
37
- timeout: 25000
38
- });
39
-
40
- if (data?.api?.status !== 'ok') {
41
- const msg = data?.api?.message || 'Error al procesar'
42
- throw new Error(msg)
43
- }
44
- return data.api;
45
- }
46
-
47
- const YT_RE = /^https?:\/\/(www\.)?(youtube\.com|youtu\.be)\//i
48
-
49
- function normalizeUrl(url) {
50
- try {
51
- const u = new URL(url)
52
- if (u.hostname.includes('youtu.be')) {
53
- return `https://www.youtube.com/watch?v=${u.pathname.slice(1)}`
54
- }
55
- if (u.pathname.startsWith('/shorts/')) {
56
- return `https://www.youtube.com/watch?v=${u.pathname.split('/shorts/')[1].split('/')[0]}`
57
- }
58
- if (u.pathname.startsWith('/embed/')) {
59
- return `https://www.youtube.com/watch?v=${u.pathname.split('/embed/')[1].split('/')[0]}`
60
- }
61
- return url
62
- } catch {
63
- return url
64
- }
65
- }
66
-
67
- function isShort(url) {
68
- try { return new URL(url).pathname.startsWith('/shorts/') } catch { return false }
69
- }
70
-
71
- export async function youtubeDownload(url) {
72
- if (!url || !YT_RE.test(url)) throw new Error('Se requiere una URL válida de YouTube.')
73
- const normalized = normalizeUrl(url)
74
- let api
75
- try {
76
- api = await fetchData(normalized)
77
- } catch (e) {
78
- if (isShort(url) && (e.message.includes('unavailable') || e.message.includes('procesar')))
79
- throw new Error('Los YouTube Shorts no están disponibles en este momento. Intentá con un video normal.')
80
- throw e
81
- }
82
-
83
- const videos = api.mediaItems
84
- .filter(i => i.type === 'Video')
85
- .map(i => ({
86
- quality: i.mediaQuality,
87
- resolution: i.mediaRes,
88
- url: i.mediaUrl,
89
- size: i.mediaFileSize,
90
- ext: i.mediaExtension.toLowerCase()
91
- }));
92
-
93
- const audios = api.mediaItems
94
- .filter(i => i.type === 'Audio')
95
- .map(i => ({
96
- quality: i.mediaQuality,
97
- url: i.mediaUrl,
98
- size: i.mediaFileSize,
99
- ext: i.mediaExtension.toLowerCase()
100
- }));
101
-
102
- return {
103
- title: api.title,
104
- description: api.description,
105
- thumb: api.imagePreviewUrl,
106
- duration: api.mediaItems[0]?.mediaDuration || null,
107
- channel: {
108
- name: api.userInfo?.name || null,
109
- username: api.userInfo?.username || null,
110
- url: api.userInfo?.internalUrl || null,
111
- avatar: api.userInfo?.userAvatar || null,
112
- country: api.userInfo?.accountCountry || null
113
- },
114
- stats: {
115
- views: api.mediaStats?.viewsCount || null,
116
- followers: api.mediaStats?.followersCount || null
117
- },
118
- download: {
119
- video: videos,
120
- audio: audios
121
- }
122
- };
123
- }