@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 +1 -1
- package/src/index.js +6 -1
- package/src/scrapers/aptoide.js +80 -0
- package/src/scrapers/books.js +136 -0
- package/src/scrapers/ddownr.js +81 -0
- package/src/scrapers/deezer.js +0 -2
- package/src/scrapers/index.js +8 -6
- package/src/scrapers/tiktok.js +44 -5
- package/src/scrapers/youtubev2.js +0 -123
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soyaxell09/zenbot-scraper",
|
|
3
|
-
"version": "1.1.
|
|
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
|
+
}
|
package/src/scrapers/deezer.js
CHANGED
package/src/scrapers/index.js
CHANGED
|
@@ -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 }
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
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 }
|
|
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'
|
package/src/scrapers/tiktok.js
CHANGED
|
@@ -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:
|
|
52
|
-
likes:
|
|
53
|
-
comments:
|
|
54
|
-
shares:
|
|
55
|
-
source:
|
|
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
|
-
}
|