@zenaveline/scraper 1.3.3 → 1.4.3

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
@@ -91,6 +91,8 @@ Panggil nama fungsinya sesuai daftar di bawah ini untuk menikmati semua fiturnya
91
91
  | `douyindl(url)` | Download video Douyin tanpa watermark. |
92
92
  | `allinonedownloader(url, tool)` | Downloader all-in-one yang mendukung banyak platform (Tiktok, YouTube, IG, dll). |
93
93
  | `tiktokdownload(url)` | Download video/foto Tiktok tanpa watermark. |
94
+ | `savetube` | Class API untuk download video/audio dari YouTube. Gunakan dengan `new scraper.savetube()`. |
95
+ | `mediafiredl(url)` | Unduh file atau folder secara langsung dari Mediafire. |
94
96
 
95
97
  ### 🎵 Musik & Audio
96
98
  | Fungsi | Deskripsi Singkat |
@@ -108,6 +110,7 @@ Panggil nama fungsinya sesuai daftar di bawah ini untuk menikmati semua fiturnya
108
110
  | `novaai(text)` | Chatbot interaktif menggunakan Nova AI untuk menjawab pertanyaan apa saja. |
109
111
  | `sharpify(img, model)` | Tools manipulasi gambar keren: `enhance`, `upscale` (Bikin HD), atau `removebg`. |
110
112
  | `pixaremovebg(img)` | Algoritma canggih dari Pixacut API untuk menghapus background dengan hasil super rapi. |
113
+ | `nanobanana(buffer, prompt)` | Edit gambar dengan instruksi teks (prompt) menggunakan AI Live3D. |
111
114
 
112
115
  ### ℹ️ Utility
113
116
  | Fungsi | Deskripsi Singkat |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenaveline/scraper",
3
- "version": "1.3.3",
3
+ "version": "1.4.3",
4
4
  "description": "Kumpulan scraper yang mudah digunakan!",
5
5
  "keywords": [
6
6
  "@zenaveline/scraper",
@@ -25,6 +25,7 @@
25
25
  "axios": "^1.6.0",
26
26
  "cheerio": "^1.0.0",
27
27
  "crypto": "^1.0.1",
28
+ "form-data": "^4.0.0",
28
29
  "sharp": "^0.33.2"
29
30
  }
30
31
  }
@@ -0,0 +1,144 @@
1
+ const cheerio = require('cheerio')
2
+ const axios = require("axios")
3
+
4
+ const regex_ = {
5
+ folder: /\.com\/folder\/([^\/]+)\/?/,
6
+ file: /\.com\/(view|file)\/([a-zA-Z0-9]+)/
7
+ }
8
+
9
+ async function extractFromWeb(is, link) {
10
+ try {
11
+ const lnk = link.replace('.com/view', '.com/file');
12
+ const f = await is.get(lnk)
13
+ const $ = cheerio.load(f.data)
14
+ const url = $('.input.popsok').attr('href')
15
+
16
+ if (!url || !/\/\/download\d+\.mediafire\.com\//.test(url)) throw Error ('Failed to find download url on the web')
17
+
18
+ const [name, date, size, type] = [
19
+ $('.intro .filename').text(),
20
+ $('.details li:nth-child(2) span').text(),
21
+ $('.details li:nth-child(1) span').text(),
22
+ $('.intro .filetype').text()
23
+ ]
24
+
25
+ const cont = {
26
+ 'af': 'Africa',
27
+ 'an': 'Antarctica',
28
+ 'as': 'Asia',
29
+ 'eu': 'Europe',
30
+ 'na': 'North America',
31
+ 'oc': 'Oceania',
32
+ 'sa': 'South America'
33
+ }
34
+
35
+ const $lo = $('.DLExtraInfo-uploadLocation')
36
+
37
+ const [continent, location, flag] = [
38
+ $lo.find('.DLExtraInfo-uploadLocationRegion').attr('data-lazyclass')?.replace('continent-', ''),
39
+ $lo.find('.DLExtraInfo-sectionDetails p').text().match(/from (.*?) on/)?.[1],
40
+ $lo.find('div.lazyload.flag').attr('data-lazyclass')?.replace('flag-', '')
41
+ ];
42
+
43
+ return {
44
+ size_format: size,
45
+ continent: cont[continent] || 'Unkown',
46
+ flag,
47
+ location,
48
+ url,
49
+ }
50
+ } catch (e) {
51
+ throw e
52
+ }
53
+ }
54
+
55
+ async function mediafire(link) {
56
+ try {
57
+ const is = axios.create({
58
+ baseURL: "https://www.mediafire.com",
59
+ headers: {
60
+ 'accept-encoding' : 'gzip, deflate, br, zstd',
61
+ 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36',
62
+ }
63
+ });
64
+
65
+ if (regex_.folder.test(link)) {
66
+ const ab = link.match(regex_.folder)
67
+ const getId = ab ? ab[1] : null;
68
+
69
+ const { data: info } = await is.get('/api/1.4/folder/get_info.php', {
70
+ params: {
71
+ recursive: 'yes',
72
+ folder_key: getId,
73
+ response_format: 'json'
74
+ }
75
+ })
76
+
77
+ let files_ = [], chunk = 1;
78
+
79
+ while (true) {
80
+ const { data: files } = await is.get('/api/1.4/folder/get_content.php', {
81
+ params: {
82
+ r: 'hrdd',
83
+ content_type: 'files',
84
+ filter: 'all',
85
+ order_by: 'name',
86
+ order_direction: 'asc',
87
+ chunk: chunk,
88
+ version: '1.5',
89
+ folder_key: getId,
90
+ response_format: 'json'
91
+ },
92
+ })
93
+ files.response.folder_content.files.map(p => files_.push({
94
+ ...p,
95
+ links: p.links.normal_download,
96
+ }));
97
+ if (files.response.folder_content.more_chunks === 'no') break;
98
+ chunk++;
99
+ await new Promise(r => setTimeout(r, 2e3));
100
+ }
101
+
102
+ return {
103
+ status: true,
104
+ type: 'folder',
105
+ ...info.response.folder_info,
106
+ files: files_
107
+ }
108
+ };
109
+
110
+ const match = link.match(regex_.file);
111
+ const ab = match ? match[2] : null;
112
+
113
+ let { data: info } = await is.get('/api/1.5/file/get_info.php', {
114
+ params: {
115
+ quick_key: ab,
116
+ response_format: 'json'
117
+ }
118
+ })
119
+
120
+ let inf = info.response.file_info, wb = await extractFromWeb(is, link);
121
+ if (!wb.type) wb = await extractFromWeb(is, link);
122
+
123
+ return {
124
+ status: true,
125
+ type: 'file',
126
+ ...inf,
127
+ links: inf.links.normal_download,
128
+ ...wb,
129
+ ...(!!inf.links.view ? {
130
+ preview: is.defaults.baseURL + '/convkey/' + inf.hash.substr(0, 4) + '/' + inf.quickkey + '9g.' + inf.filename.split('.').pop()
131
+ } : {}),
132
+ }
133
+ } catch(e) {
134
+ if (e.status === 404) {
135
+ return {
136
+ status: false,
137
+ msg: "Page not found or invalid link"
138
+ }
139
+ }
140
+ throw e.response ? e.response.data : e.message
141
+ }
142
+ }
143
+
144
+ module.exports = mediafire;
@@ -0,0 +1,115 @@
1
+ const axios = require('axios');
2
+ const crypto = require('crypto');
3
+ const FormData = require('form-data');
4
+
5
+ async function live3d(buffer, prompt) {
6
+ const config = {
7
+ pkey: "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlHZk1BMEdDU3FHU0liM0RRRUJBUVVBQTRHTkFEQ0JpUUtCZ1FDd2xPK2JvQzZjd1JvM1VmWFZCYWRhWXdjWDB6S1MyZnVWTlkycVowZGd3YjFOSisvUTlGZUFvc0w0T05pb3NENzFvbjNQVllxUlVsTDUwNDVtdkgySzlpOGJBRlZNRWlwN0U2Uk1LNnRLQUFpZjd4elpyWG5QMUdaNVJpanRxZGd3aCtZbXpUbzM5Y3VCQ3NacUs5b0VvZVEzci9teUc5Uys5Y1I1aHVUdUZRSURBUUFCCi0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQ==",
8
+ aid: "aifaceswap",
9
+ uid: "1H5tRtzsBkqXcaJ",
10
+ origin: "8f3f0c7387123ae0",
11
+ theme_version: '83EmcUoQTUv50LhNx0VrdcK8rcGexcP35FcZDcpgWsAXEyO4xqL5shCY6sFIWB2Q',
12
+ model: 'nano_banana_2',
13
+ }
14
+
15
+ let currentFp = crypto.randomBytes(16).toString('hex');
16
+
17
+ const crypt = {
18
+ aes: (data, key) => {
19
+ const cipher = crypto.createCipheriv('aes-128-cbc', key, key);
20
+ return Buffer.concat([cipher.update(data, 'utf8'), cipher.final()]).toString('base64')
21
+ },
22
+ rsa: (data) => {
23
+ return crypto.publicEncrypt({
24
+ key: Buffer.from(config.pkey, "base64").toString(),
25
+ padding: crypto.constants.RSA_PKCS1_PADDING,
26
+ }, Buffer.from(data, 'utf8')).toString('base64');
27
+ }
28
+ };
29
+
30
+ const api = axios.create({
31
+ baseURL: 'https://app-v1.live3d.io',
32
+ headers: {
33
+ 'User-Agent': 'Mozilla/5.0 (Linux; Android 16; NX729J) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.7499.34 Mobile Safari/537.36',
34
+ 'origin': 'https://live3d.io',
35
+ 'referer': 'https://live3d.io/',
36
+ 'theme-version': config.theme_version
37
+ }
38
+ });
39
+
40
+ api.interceptors.request.use((cfg) => {
41
+ const [i, d, n] = [
42
+ crypto.randomBytes(8).toString('hex'),
43
+ crypto.randomUUID(),
44
+ Math.floor(Date.now() / 1000)
45
+ ]
46
+
47
+ const s = crypt.rsa(i);
48
+ const signStr = cfg.url.includes('upload-img') ? `${config.aid}:${d}:${s}` : `${config.aid}:${config.uid}:${n}:${d}:${s}`;
49
+
50
+ Object.assign(cfg.headers, {
51
+ 'fp': currentFp,
52
+ 'fp1': crypt.aes(`${config.aid}:${currentFp}`, i),
53
+ 'x-guide': s,
54
+ 'x-sign': crypt.aes(signStr, i),
55
+ 'x-code': Date.now().toString()
56
+ });
57
+
58
+ return cfg;
59
+ });
60
+
61
+ try {
62
+ const form = new FormData();
63
+ form.append('file', buffer, { filename: 'input.jpg', contentType: 'image/jpeg' });
64
+ form.append('fn_name', 'demo-image-editor');
65
+ form.append('request_from', '9');
66
+ form.append('origin_from', config.origin);
67
+
68
+ const { data: upRes } = await api.post('/aitools/upload-img', form, {
69
+ headers: form.getHeaders()
70
+ });
71
+
72
+ const { data: job } = await api.post('/aitools/of/create', {
73
+ fn_name: 'demo-image-editor',
74
+ call_type: 3,
75
+ input: {
76
+ model: config.model,
77
+ source_images: [upRes.data.path],
78
+ prompt: prompt,
79
+ aspect_radio: 'auto',
80
+ request_from: 9
81
+ },
82
+ data: '',
83
+ request_from: 9,
84
+ origin_from: config.origin
85
+ });
86
+
87
+
88
+ const taskId = job.data.task_id;
89
+ if (!taskId) throw new Error("TaskId cannot be found")
90
+
91
+ while (true) {
92
+ const { data: status } = await api.post('/aitools/of/check-status', {
93
+ task_id: taskId,
94
+ fn_name: 'demo-image-editor',
95
+ call_type: 3,
96
+ request_from: 9,
97
+ origin_from: config.origin
98
+ });
99
+
100
+ if (status.data.status === 2) {
101
+ return {
102
+ status: 'success',
103
+ image_url: 'https://temp.live3d.io/' + status.data.result_image
104
+ };
105
+ } else if (status.data.status === 3) {
106
+ return status.data
107
+ }
108
+ await new Promise(r => setTimeout(r, 3000));
109
+ }
110
+ } catch (error) {
111
+ throw new Error(`Process failed: ${error.message}`);
112
+ }
113
+ }
114
+
115
+ module.exports = live3d;
@@ -0,0 +1,85 @@
1
+ const crypto = require("crypto")
2
+ const axios = require("axios")
3
+
4
+ class savetube {
5
+ constructor() {
6
+ this.ky = 'C5D58EF67A7584E4A29F6C35BBC4EB12';
7
+ this.fmt = ['144', '240', '360', '480', '720', '1080', 'mp3'];
8
+ this.m = /^((?:https?:)?\/\/)?((?:www|m|music)\.)?(?:youtube\.com|youtu\.be)\/(?:watch\?v=)?(?:embed\/)?(?:v\/)?(?:shorts\/)?([a-zA-Z0-9_-]{11})/;
9
+ this.is = axios.create({
10
+ headers: {
11
+ 'content-type': 'application/json',
12
+ 'origin': 'https://yt.savetube.me',
13
+ 'user-agent': 'Mozilla/5.0 (Android 15; Mobile; SM-F958; rv:130.0) Gecko/130.0 Firefox/130.0'
14
+ }
15
+ })
16
+ }
17
+
18
+ async decrypt(enc) {
19
+ try {
20
+ const [sr, ky] = [Buffer.from(enc,'base64'), Buffer.from(this.ky,'hex')]
21
+ const [iv, dt] = [sr.slice(0,16), sr.slice(16)]
22
+ const dc = crypto.createDecipheriv('aes-128-cbc', ky, iv);
23
+ return JSON.parse(Buffer.concat([dc.update(dt), dc.final()]).toString());
24
+ } catch (e) {
25
+ throw new Error(`Error while decrypting data: ${e.message}`);
26
+ }
27
+ }
28
+
29
+ async getCdn() {
30
+ const response = await this.is.get("https://media.savetube.vip/api/random-cdn");
31
+ if (!response.status) return response;
32
+ return {
33
+ status: true,
34
+ data: response.data.cdn
35
+ };
36
+ }
37
+
38
+ async download(url, format = 'mp3') {
39
+ const id = url.match(this.m)?.[3];
40
+ if (!id) {
41
+ return {
42
+ status: false,
43
+ msg: "ID cannot be extracted from url"
44
+ };
45
+ }
46
+ if (!format || !this.fmt.includes(format)) {
47
+ return {
48
+ status: false,
49
+ msg: "Formats not found",
50
+ list: this.fmt
51
+ };
52
+ }
53
+ try {
54
+ const u = await this.getCdn();
55
+ if (!u.status) return u;
56
+ const res = await this.is.post(`https://${u.data}/v2/info`, {
57
+ url: `https://www.youtube.com/watch?v=${id}`
58
+ });
59
+ const dec = await this.decrypt(res.data.data);
60
+ const dl = await this.is.post(`https://${u.data}/download`, {
61
+ id: id,
62
+ downloadType: format === 'mp3' ? 'audio' : 'video',
63
+ quality: format === 'mp3' ? '128' : format,
64
+ key: dec.key
65
+ })
66
+
67
+ return {
68
+ status: true,
69
+ title: dec.title,
70
+ format: format,
71
+ thumb: dec.thumbnail || `https://i.ytimg.com/vi/${id}/hqdefault.jpg`,
72
+ duration: dec.duration,
73
+ cached: dec.fromCache,
74
+ dl: dl.data.data.downloadUrl
75
+ };
76
+ } catch (error) {
77
+ return {
78
+ status: false,
79
+ error: error.message
80
+ };
81
+ }
82
+ }
83
+ }
84
+
85
+ module.exports = savetube;