rippanteq7 commited on
Commit
d6a14cd
1 Parent(s): f9f70a2

Upload folder using huggingface_hub

Browse files
Files changed (5) hide show
  1. Dockerfile +17 -0
  2. README.md +5 -6
  3. favicon.ico +0 -0
  4. index.js +263 -0
  5. package.json +11 -0
Dockerfile ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM node:latest
2
+
3
+ ENV CHROME_BIN=/usr/bin/chromium \
4
+ TZ=Asia/Jakarta
5
+ RUN apt-get update && apt-get install -y \
6
+ chromium \
7
+ ffmpeg \
8
+ imagemagick \
9
+ libnss3-dev \
10
+ webp && \
11
+ apt-get clean
12
+ WORKDIR /app
13
+ COPY package*.json $WORKDIR
14
+ RUN npm i
15
+ COPY . $WORKDIR
16
+ EXPOSE 7860
17
+ CMD ["node", "."]
README.md CHANGED
@@ -1,12 +1,11 @@
1
  ---
2
  title: Helper
3
- emoji: 💻
4
- colorFrom: indigo
5
- colorTo: green
6
- sdk: gradio
7
- sdk_version: 5.4.0
8
- app_file: app.py
9
  pinned: false
 
10
  ---
11
 
12
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
  title: Helper
3
+ emoji: 🐢
4
+ colorFrom: blue
5
+ colorTo: gray
6
+ sdk: docker
 
 
7
  pinned: false
8
+ license: mit
9
  ---
10
 
11
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
favicon.ico ADDED
index.js ADDED
@@ -0,0 +1,263 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import bytes from 'bytes'
2
+ import cp from 'child_process'
3
+ import express from 'express'
4
+ import favicon from 'serve-favicon'
5
+ import fs from 'fs'
6
+ import morgan from 'morgan'
7
+ import os from 'os'
8
+ import path from 'path'
9
+ import PDFDocument from 'pdfkit'
10
+ import sharp from 'sharp'
11
+ import util from 'util'
12
+ import yts from 'yt-search'
13
+
14
+ const utils = {
15
+ // from https://github.com/petersolopov/carbonara
16
+ fetchCarbonaraAPI: async (code, output, opts = {}) => {
17
+ let resp = await utils.fetchPOST('https://carbonara.solopov.dev/api/cook', JSON.stringify({ code, ...opts }), {
18
+ headers: { 'Content-Type': 'application/json' }
19
+ })
20
+ if (!resp.ok) {
21
+ let content = resp.headers.get('content-type')
22
+ if (/json$/.test(content)) {
23
+ let json = await resp.json()
24
+ throw json.error || 'An error occurred'
25
+ } else {
26
+ throw resp.statusText
27
+ }
28
+ }
29
+
30
+ let img = await resp.arrayBuffer()
31
+ await fs.promises.writeFile(output, Buffer.from(img))
32
+ return output
33
+ },
34
+ fetchCobaltAPI: async (url, opts = {}) => (
35
+ await utils.fetchPOST('https://cobalt-7.kwiatekmiki.com/api/json', JSON.stringify({ url, ...opts }), {
36
+ headers: { Accept: 'application/json', 'Content-Type': 'application/json' }
37
+ })
38
+ ).json(),
39
+ fetchPOST: (url, body, opts = {}) => fetch(url, { method: 'POST', body, ...opts }),
40
+ formatSize: (n) => bytes(+n, { unitSeparator: ' ' }),
41
+ getError: (e) => String(e).startsWith('[object ') ? 'Internal Server Error' : String(e),
42
+ isBase64: (str) => {
43
+ try {
44
+ return btoa(atob(str)) === str
45
+ } catch {
46
+ return false
47
+ }
48
+ },
49
+ isTrue: (str) => [true, 'true'].includes(str),
50
+ randomIP: () => [...new Array(4)].map(() => ~~(Math.random() * 256)).join('.'),
51
+ randomName: (str = '') => (Math.random().toString(36).slice(2) + str),
52
+ toPDF: (urls, opts = {}) => new Promise(async (resolve, reject) => {
53
+ try {
54
+ const doc = new PDFDocument({ margin: 0, size: 'A4' })
55
+ const buffs = []
56
+
57
+ for (let x = 0; x < urls.length; x++) {
58
+ if (!/https?:\/\//.test(urls[x])) continue
59
+ const url = new URL(urls[x])
60
+ let image = await fetch(url.toString(), { headers: { referer: url.origin } })
61
+ if (!image.ok) continue
62
+
63
+ const type = image.headers.get('content-type')
64
+ if (!/image/.test(type)) continue
65
+ image = Buffer.from(await image.arrayBuffer())
66
+ if (/(gif|webp)$/.test(type)) image = await sharp(image).png().toBuffer()
67
+
68
+ doc.image(image, 0, 0, { fit: [595.28, 841.89], align: 'center', valign: 'center', ...opts })
69
+ if (urls.length != x + 1) doc.addPage()
70
+ }
71
+
72
+ doc.on('data', (chunk) => buffs.push(chunk))
73
+ doc.on('end', () => resolve(Buffer.concat(buffs)))
74
+ doc.on('error', (err) => reject(err))
75
+ doc.end()
76
+ } catch (e) {
77
+ console.log(e)
78
+ reject(e)
79
+ }
80
+ }),
81
+ // from https://github.com/Nurutomo/wabot-aq/blob/master/lib/y2mate.js#L15
82
+ ytIdRegex: /(?:http(?:s|):\/\/|)(?:(?:www\.|)?youtube(?:\-nocookie|)\.com\/(?:shorts\/)?(?:watch\?.*(?:|\&)v=|embed\/|live\/|v\/)?|youtu\.be\/)([-_0-9A-Za-z]{11})/,
83
+ }
84
+
85
+ const app = express()
86
+ const tmpDir = os.tmpdir()
87
+
88
+ app.set('json spaces', 4)
89
+ app.use(express.json({ limit: '200mb' }))
90
+ app.use(express.urlencoded({ extended: true, limit: '200mb' }))
91
+ app.use(favicon(path.join(import.meta.dirname, 'favicon.ico')))
92
+ app.use(morgan('combined'))
93
+
94
+ app.use((req, __, next) => {
95
+ // clear tmp
96
+ for (let file of fs.readdirSync(tmpDir)) {
97
+ file = `${tmpDir}/${file}`
98
+ const stat = fs.statSync(file)
99
+ const exp = Date.now() - stat.mtimeMs >= 1000 * 60 * 30
100
+ if (stat.isFile() && exp) {
101
+ console.log('Deleted file', file)
102
+ fs.unlinkSync(file)
103
+ }
104
+ }
105
+ req.allParams = Object.assign(req.query, req.body)
106
+ req.headers['User-Agent'] = 'WhatsApp/1.2.3'
107
+ next()
108
+ })
109
+
110
+ app.use('/file', express.static(tmpDir))
111
+
112
+ app.all('/', (_, res) => {
113
+ const status = {}
114
+ status['diskUsage'] = cp.execSync('du -sh').toString().split('M')[0] + ' MB'
115
+
116
+ const used = process.memoryUsage()
117
+ for (let x in used) status[x] = utils.formatSize(used[x])
118
+
119
+ const totalmem = os.totalmem()
120
+ const freemem = os.freemem()
121
+ status['memoryUsage'] = `${utils.formatSize(totalmem - freemem)} / ${utils.formatSize(totalmem)}`
122
+
123
+ res.json({
124
+ creator: `@${process.env['SPACE_AUTHOR_NAME'] || 'rippanteq7'}`,
125
+ message: 'Hello World!',
126
+ uptime: new Date(process.uptime() * 1000).toUTCString().split(' ')[4],
127
+ status
128
+ })
129
+ })
130
+
131
+ app.all('/carbon', async (req, res) => {
132
+ if (!['GET', 'POST'].includes(req.method)) return res.status(405).json({ success: false, message: 'Method Not Allowed' })
133
+
134
+ try {
135
+ const obj = req.allParams
136
+ if (!obj.code) return res.status(400).json({ success: false, message: 'Required parameter \'code\'' })
137
+
138
+ const filePath = `${tmpDir}/${utils.randomName('.png')}`
139
+ // nembak API carbonara njir
140
+ const image = await utils.fetchCarbonaraAPI(obj.code, filePath, obj)
141
+ const resultUrl = `https://${req.hostname}/${image.replace(tmpDir, 'file')}`
142
+ utils.isTrue(obj.json) ? res.json({ success: true, result: resultUrl }) : res[utils.isTrue(obj.raw) ? 'send' : 'redirect'](resultUrl)
143
+ } catch (e) {
144
+ console.log(e)
145
+ res.status(500).json({ error: true, message: utils.getError(e) })
146
+ }
147
+ })
148
+
149
+ app.all('/topdf', async (req, res) => {
150
+ if (req.method !== 'POST') return res.status(405).json({ success: false, message: 'Method Not Allowed' })
151
+
152
+ try {
153
+ const { images: urls, json, raw } = req.body
154
+ if (!urls) return res.status(400).json({ success: false, message: 'Payload \'images\' requires an array of urls' })
155
+ if (!Array.isArray(urls)) urls = [urls]
156
+
157
+ const bufferPDF = await utils.toPDF(urls)
158
+ if (!bufferPDF.length) return res.status(400).json({ success: false, message: 'Can\'t convert to pdf' })
159
+
160
+ const fileName = utils.randomName('.pdf')
161
+ await fs.promises.writeFile(`${tmpDir}/${fileName}`, bufferPDF)
162
+
163
+ const resultUrl = `https://${req.hostname}/file/${fileName}`
164
+ utils.isTrue(json) ? res.json({ success: true, result: resultUrl }) : res[utils.isTrue(raw) ? 'send' : 'redirect'](resultUrl)
165
+ } catch (e) {
166
+ console.log(e)
167
+ res.status(500).json({ error: true, message: utils.getError(e) })
168
+ }
169
+ })
170
+
171
+ app.all(/^\/webp2(gif|mp4|png)/, async (req, res) => {
172
+ if (req.method !== 'POST') return res.status(405).json({ success: false, message: 'Method Not Allowed' })
173
+
174
+ try {
175
+ const { file, json, raw } = req.body
176
+ if (!file) return res.status(400).json({ success: false, message: 'Payload \'file\' requires base64 string' })
177
+ if (!utils.isBase64(file)) return res.status(400).json({ success: false, message: 'Invalid base64 format' })
178
+
179
+ const type = req.params[0]
180
+ if (type === 'png') {
181
+ const fileName = utils.randomName('.png')
182
+ const fileBuffer = await sharp(Buffer.from(file, 'base64')).png().toBuffer()
183
+ await fs.promises.writeFile(`${tmpDir}/${fileName}`, fileBuffer)
184
+
185
+ const resultUrl = `https://${req.hostname}/file/${fileName}`
186
+ utils.isTrue(json) ? res.json({ success: true, result: resultUrl }) : res[utils.isTrue(raw) ? 'send' : 'redirect'](resultUrl)
187
+ } else {
188
+ const fileName = utils.randomName('.webp')
189
+ const filePath = `${tmpDir}/${fileName}`
190
+ await fs.promises.writeFile(filePath, Buffer.from(file, 'base64'))
191
+
192
+ const exec = util.promisify(cp.exec).bind(cp)
193
+ await exec(`convert ${filePath} ${filePath.replace('webp', 'gif')}`)
194
+
195
+ let resultUrl
196
+ if (type === 'gif') resultUrl = `https://${req.hostname}/file/${fileName.replace('webp', 'gif')}`
197
+ else {
198
+ await exec(`ffmpeg -i ${filePath.replace('webp', 'gif')} -movflags faststart -pix_fmt yuv420p -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" ${filePath.replace('webp', 'mp4')}`)
199
+ resultUrl = `https://${req.hostname}/file/${fileName.replace('webp', 'mp4')}`
200
+ }
201
+
202
+ utils.isTrue(json) ? res.json({ success: true, result: resultUrl }) : res[utils.isTrue(raw) ? 'send' : 'redirect'](resultUrl)
203
+ }
204
+ } catch (e) {
205
+ console.log(e)
206
+ res.status(500).json({ error: true, message: utils.getError(e) })
207
+ }
208
+ })
209
+
210
+ // /yt /yt/dl /yt/download /yt/search /youtube/dl /youtube/download /youtube/search
211
+ app.all(/^\/y(outube|t)(\/(d(ownload|l)|search)?)?/, async (req, res) => {
212
+ if (!['GET', 'POST'].includes(req.method)) return res.status(405).json({ success: false, message: 'Method Not Allowed' })
213
+
214
+ try {
215
+ const type = req.params[2]
216
+ const obj = req.allParams
217
+ if (type === 'search') {
218
+ if (!obj.query) return res.status(400).json({ success: false, message: 'Required parameter \'query\'' })
219
+
220
+ const result = await yts(obj)
221
+ if (!(result.all?.length || result?.url)) return res.status(400).json({ success: false, message: 'Video unavailable' })
222
+
223
+ res.json({ success: true, result })
224
+ return
225
+ } else if (['dl', 'download'].includes(type)) {
226
+ if (!obj.url) return res.status(400).json({ success: false, message: 'Required parameter \'url\'' })
227
+ if (!utils.ytIdRegex.test(obj.url)) return res.status(400).json({ success: false, message: 'Invalid url' })
228
+
229
+ const payload = obj
230
+ if (obj.type === 'audio') {
231
+ payload.isAudioOnly = true
232
+ // payload.audioBitrate = obj.quality || '128'
233
+ } else {
234
+ payload.vQuality = obj.quality || '720'
235
+ }
236
+ // nembak lagi woila
237
+ const result = await utils.fetchCobaltAPI(obj.url, payload)
238
+ if (result.status === 'error') return res.status(400).json({ success: false, message: 'An error occurred' })
239
+ res.redirect(result.url)
240
+ return
241
+ } else {
242
+ if (!obj.query) return res.status(400).json({ success: false, message: 'Required parameter \'query\'' })
243
+
244
+ let result = await yts(utils.ytIdRegex.test(obj.query) ? { videoId: utils.ytIdRegex.exec(obj.query)[1] } : obj.query)
245
+ result = result.videos ? result.videos[0] : result
246
+ if (!result?.url) return res.status(400).json({ success: false, message: 'Video unavailable' })
247
+
248
+ const dlUrl = `https://${req.hostname}/yt/dl?url=${result.url}`
249
+ const download = { audio: `${dlUrl}&type=audio`, video: `${dlUrl}&type=video` }
250
+ res.json({
251
+ success: true,
252
+ result: { ...result, download }
253
+ })
254
+ }
255
+ } catch (e) {
256
+ console.log(e)
257
+ res.status(500).json({ error: true, message: utils.getError(e) })
258
+ }
259
+ })
260
+
261
+ // app.use((req, res, next) => {})
262
+
263
+ app.listen(7860, () => console.log('App running on port 7860'))
package.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "type": "module",
3
+ "dependencies": {
4
+ "express": "*",
5
+ "morgan": "*",
6
+ "pdfkit": "*",
7
+ "serve-favicon": "*",
8
+ "sharp": "*",
9
+ "yt-search": "*"
10
+ }
11
+ }