cruxx3 commited on
Commit
04e3611
·
verified ·
1 Parent(s): 71731fb

Upload folder using huggingface_hub

Browse files
Files changed (4) hide show
  1. README.md +5 -7
  2. favicon.ico +0 -0
  3. index.js +489 -0
  4. package.json +12 -0
README.md CHANGED
@@ -1,12 +1,10 @@
1
  ---
2
- title: Helper
3
- emoji: 🐨
4
  colorFrom: green
5
- colorTo: pink
6
- sdk: gradio
7
- sdk_version: 5.9.1
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: green
5
+ colorTo: blue
6
+ sdk: docker
 
 
7
  pinned: false
8
  ---
9
 
10
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
favicon.ico ADDED
index.js ADDED
@@ -0,0 +1,489 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 playwright from 'playwright'
10
+ import PDFDocument from 'pdfkit'
11
+ import sharp from 'sharp'
12
+ import util from 'util'
13
+ import yts from 'yt-search'
14
+
15
+ const utils = {
16
+ getBrowser: (...opts) =>
17
+ playwright.chromium.launch({
18
+ args: [
19
+ '--incognito',
20
+ '--single-process',
21
+ '--no-sandbox',
22
+ '--no-zygote',
23
+ '--no-cache'
24
+ ],
25
+ executablePath: process.env.CHROME_BIN,
26
+ headless: true,
27
+ ...opts
28
+ }),
29
+ // from https://github.com/petersolopov/carbonara
30
+ fetchCarbonaraAPI: async (code, opts = {}) => {
31
+ let resp = await utils.fetchPOST(
32
+ 'https://carbonara.solopov.dev/api/cook',
33
+ JSON.stringify({ code, ...opts }),
34
+ {
35
+ headers: { 'Content-Type': 'application/json' }
36
+ }
37
+ )
38
+ if (!resp.ok) {
39
+ let content = resp.headers.get('content-type')
40
+ if (/json$/.test(content)) {
41
+ let json = await resp.json()
42
+ throw json.error || 'An error occurred'
43
+ }
44
+ throw resp.statusText
45
+ }
46
+
47
+ let img = await resp.arrayBuffer()
48
+ const output = `${tmpDir}/${utils.randomName('.png')}`
49
+ await fs.promises.writeFile(output, Buffer.from(img))
50
+ return output
51
+ },
52
+ /*
53
+ fetchCobaltAPI: async (url, opts = {}) =>
54
+ (
55
+ await utils.fetchPOST(
56
+ 'https://capi.3kh0.net',
57
+ JSON.stringify({ url, ...opts }),
58
+ {
59
+ headers: {
60
+ Accept: 'application/json',
61
+ 'Content-Type': 'application/json'
62
+ }
63
+ }
64
+ )
65
+ ).json(),
66
+ */
67
+ fetchMediafireAPI: async (id) => {
68
+ // TODO: folder download
69
+ let resp = await fetch(
70
+ `https://mediafire.com/api/1.5/file/get_info.php?response_format=json&quick_key=${id}`
71
+ )
72
+ let json = await resp.json()
73
+ return json.response
74
+ },
75
+ fetchPOST: (url, body, opts = {}) =>
76
+ fetch(url, { method: 'POST', body, ...opts }),
77
+ fetchSaveTubeAPI: async (opts = {}) => {
78
+ const headers = {
79
+ Authority: `cdn${~~(Math.random() * 11) + 51}.savetube.su`,
80
+ 'Content-Type': 'application/json'
81
+ }
82
+
83
+ const makeRequest = async (endpoint) =>
84
+ (
85
+ await utils.fetchPOST(
86
+ `https://${headers.Authority}${endpoint}`,
87
+ JSON.stringify(opts),
88
+ { headers }
89
+ )
90
+ ).json()
91
+
92
+ let info = await makeRequest('/info')
93
+ opts.key = info.data.key
94
+ return makeRequest('/download')
95
+ },
96
+ formatSize: (n) => bytes(+n, { unitSeparator: ' ' }),
97
+ generateBrat: async (text) => {
98
+ const browser = await utils.getBrowser()
99
+ try {
100
+ const page = await browser.newPage()
101
+ await page.goto('https://www.bratgenerator.com/')
102
+ await page.click('#toggleButtonWhite')
103
+ await page.locator('#textInput').fill(text)
104
+ const output = `${tmpDir}/${utils.randomName('.jpg')}`
105
+ const ss = await page.locator('#textOverlay').screenshot({ path: output })
106
+ return output
107
+ } catch (e) {
108
+ throw e
109
+ } finally {
110
+ if (browser) await browser.close()
111
+ }
112
+ },
113
+ getMediafireDownloadLink: async (url) => {
114
+ let resp = await fetch(url)
115
+ let html = await resp.text()
116
+ let dl = html.match(/href="(.*?)".*id="downloadButton"/)?.[1]
117
+ return dl
118
+ ? {
119
+ cookie: resp.headers.get('set-cookie'),
120
+ download: dl
121
+ }
122
+ : false
123
+ },
124
+ getError: (e) =>
125
+ String(e).startsWith('[object ') ? 'Internal Server Error' : String(e),
126
+ isBase64: (str) => {
127
+ try {
128
+ return btoa(atob(str)) === str
129
+ } catch {
130
+ return false
131
+ }
132
+ },
133
+ isTrue: (str) => [true, 'true'].includes(str),
134
+ mediafireIdRegex: /https?:\/\/(www.)?mediafire.com\/(file|folder)\/(\w+)/,
135
+ randomIP: () =>
136
+ [...new Array(4)].map(() => ~~(Math.random() * 256)).join('.'),
137
+ randomName: (str = '') => Math.random().toString(36).slice(2) + str,
138
+ toPDF: (urls, opts = {}) =>
139
+ new Promise(async (resolve, reject) => {
140
+ try {
141
+ const doc = new PDFDocument({ margin: 0, size: 'A4' })
142
+ const buffs = []
143
+
144
+ for (let x = 0; x < urls.length; x++) {
145
+ if (!/https?:\/\//.test(urls[x])) continue
146
+ const url = new URL(urls[x])
147
+ let image = await fetch(url.toString(), {
148
+ headers: { referer: url.origin }
149
+ })
150
+ if (!image.ok) continue
151
+
152
+ const type = image.headers.get('content-type')
153
+ if (!/image/.test(type)) continue
154
+ image = Buffer.from(await image.arrayBuffer())
155
+ if (/(gif|webp)$/.test(type))
156
+ image = await sharp(image).png().toBuffer()
157
+
158
+ doc.image(image, 0, 0, {
159
+ fit: [595.28, 841.89],
160
+ align: 'center',
161
+ valign: 'center',
162
+ ...opts
163
+ })
164
+ if (urls.length != x + 1) doc.addPage()
165
+ }
166
+
167
+ doc.on('data', (chunk) => buffs.push(chunk))
168
+ doc.on('end', () => resolve(Buffer.concat(buffs)))
169
+ doc.on('error', (err) => reject(err))
170
+ doc.end()
171
+ } catch (e) {
172
+ console.log(e)
173
+ reject(e)
174
+ }
175
+ }),
176
+ // from https://github.com/Nurutomo/wabot-aq/blob/master/lib/y2mate.js#L15
177
+ ytIdRegex:
178
+ /(?:http(?:s|):\/\/|)(?:(?:www\.|)?youtube(?:\-nocookie|)\.com\/(?:shorts\/)?(?:watch\?.*(?:|\&)v=|embed\/|live\/|v\/)?|youtu\.be\/)([-_0-9A-Za-z]{11})/
179
+ }
180
+
181
+ const app = express()
182
+ const tmpDir = os.tmpdir()
183
+
184
+ app.set('json spaces', 4)
185
+ app.use(express.json({ limit: '200mb' }))
186
+ app.use(express.urlencoded({ extended: true, limit: '200mb' }))
187
+ app.use(favicon(path.join(import.meta.dirname, 'favicon.ico')))
188
+ app.use(morgan('combined'))
189
+
190
+ app.use((req, __, next) => {
191
+ // clear tmp
192
+ /*
193
+ for (let file of fs.readdirSync(tmpDir)) {
194
+ file = `${tmpDir}/${file}`
195
+ const stat = fs.statSync(file)
196
+ const exp = Date.now() - stat.mtimeMs >= 1000 * 60 * 30
197
+ if (stat.isFile() && exp) {
198
+ console.log('Deleting file', file)
199
+ fs.unlinkSync(file)
200
+ }
201
+ }
202
+ */
203
+ req.allParams = Object.assign(req.query, req.body)
204
+ next()
205
+ })
206
+
207
+ app.use('/file', express.static(tmpDir))
208
+
209
+ app.all('/', (_, res) => {
210
+ const status = {}
211
+ status['diskUsage'] = cp.execSync('du -sh').toString().split('M')[0] + ' MB'
212
+
213
+ const used = process.memoryUsage()
214
+ for (let x in used) status[x] = utils.formatSize(used[x])
215
+
216
+ const totalmem = os.totalmem()
217
+ const freemem = os.freemem()
218
+ status['memoryUsage'] =
219
+ `${utils.formatSize(totalmem - freemem)} / ${utils.formatSize(totalmem)}`
220
+
221
+ const id = process.env.SPACE_ID
222
+ res.json({
223
+ message: id
224
+ ? `Go to https://hf.co/spaces/${id}/discussions for discuss`
225
+ : 'Hello World!',
226
+ uptime: new Date(process.uptime() * 1000).toUTCString().split(' ')[4],
227
+ status
228
+ })
229
+ })
230
+
231
+ app.all(/^\/(brat|carbon)/, async (req, res) => {
232
+ if (!['GET', 'POST'].includes(req.method))
233
+ return res
234
+ .status(405)
235
+ .json({ success: false, message: 'Method Not Allowed' })
236
+
237
+ try {
238
+ const obj = req.allParams
239
+ const isBrat = req.params[0] === 'brat'
240
+ if (isBrat && !obj.text) {
241
+ return res
242
+ .status(400)
243
+ .json({ success: false, message: "Required parameter 'text'" })
244
+ } else if (!isBrat && !(obj.code || obj.text)) {
245
+ return res
246
+ .status(400)
247
+ .json({ success: false, message: "Required parameter 'code'" })
248
+ }
249
+
250
+ const image = isBrat
251
+ ? await utils.generateBrat(obj.text)
252
+ : await utils.fetchCarbonaraAPI(obj.code || obj.text, obj)
253
+ const resultUrl = `https://${req.hostname}/${image.replace(tmpDir, 'file')}`
254
+ utils.isTrue(obj.json)
255
+ ? res.json({ success: true, result: resultUrl })
256
+ : res[utils.isTrue(obj.raw) ? 'send' : 'redirect'](resultUrl)
257
+ } catch (e) {
258
+ console.log(e)
259
+ res.status(500).json({ error: true, message: utils.getError(e) })
260
+ }
261
+ })
262
+
263
+ app.all('/mediafire', async (req, res) => {
264
+ if (!['GET', 'POST'].includes(req.method))
265
+ return res
266
+ .status(405)
267
+ .json({ success: false, message: 'Method Not Allowed' })
268
+
269
+ try {
270
+ const obj = req.allParams
271
+ if (!obj.url)
272
+ return res
273
+ .status(400)
274
+ .json({ success: false, message: "Required parameter 'url'" })
275
+ if (!utils.mediafireIdRegex.test(obj.url))
276
+ return res.status(400).json({ success: false, message: 'Invalid url' })
277
+
278
+ const [, _, type, id] = utils.mediafireIdRegex.exec(obj.url)
279
+ if (type === 'folder')
280
+ return res
281
+ .status(400)
282
+ .json({ success: false, message: 'Folder download not supported yet' })
283
+ if (!id)
284
+ return res
285
+ .status(400)
286
+ .json({ success: false, message: 'Cannot find file id' })
287
+
288
+ const [data, result] = await Promise.all([
289
+ utils.fetchMediafireAPI(id),
290
+ utils.getMediafireDownloadLink(obj.url)
291
+ ])
292
+ if (data.error)
293
+ return res.status(400).json({ success: false, message: data.message })
294
+
295
+ for (let [key, val] of Object.entries(data.file_info)) {
296
+ if (key === 'links') continue
297
+ if (key === 'size') val = utils.formatSize(val)
298
+ key = key.split('_')[0]
299
+ result[key] = val
300
+ }
301
+
302
+ res.json({ success: true, result })
303
+ } catch (e) {
304
+ console.log(e)
305
+ res.status(500).json({ error: true, message: utils.getError(e) })
306
+ }
307
+ })
308
+
309
+ app.all('/topdf', async (req, res) => {
310
+ if (req.method !== 'POST')
311
+ return res
312
+ .status(405)
313
+ .json({ success: false, message: 'Method Not Allowed' })
314
+
315
+ try {
316
+ const { images: urls, json, raw } = req.body
317
+ if (!urls)
318
+ return res.status(400).json({
319
+ success: false,
320
+ message: "Payload 'images' requires an array of urls"
321
+ })
322
+ if (!Array.isArray(urls)) urls = [urls]
323
+
324
+ const bufferPDF = await utils.toPDF(urls)
325
+ if (!bufferPDF.length)
326
+ return res
327
+ .status(400)
328
+ .json({ success: false, message: "Can't convert to pdf" })
329
+
330
+ const fileName = utils.randomName('.pdf')
331
+ await fs.promises.writeFile(`${tmpDir}/${fileName}`, bufferPDF)
332
+
333
+ const resultUrl = `https://${req.hostname}/file/${fileName}`
334
+ utils.isTrue(json)
335
+ ? res.json({ success: true, result: resultUrl })
336
+ : res[utils.isTrue(raw) ? 'send' : 'redirect'](resultUrl)
337
+ } catch (e) {
338
+ console.log(e)
339
+ res.status(500).json({ error: true, message: utils.getError(e) })
340
+ }
341
+ })
342
+
343
+ app.all(/^\/webp2(gif|mp4|png)/, async (req, res) => {
344
+ if (req.method !== 'POST')
345
+ return res
346
+ .status(405)
347
+ .json({ success: false, message: 'Method Not Allowed' })
348
+
349
+ try {
350
+ const { file, json, raw } = req.body
351
+ if (!file)
352
+ return res.status(400).json({
353
+ success: false,
354
+ message: "Payload 'file' requires base64 string"
355
+ })
356
+ if (!utils.isBase64(file))
357
+ return res
358
+ .status(400)
359
+ .json({ success: false, message: 'Invalid base64 format' })
360
+
361
+ const type = req.params[0]
362
+ if (type === 'png') {
363
+ const fileName = utils.randomName('.png')
364
+ const fileBuffer = await sharp(Buffer.from(file, 'base64'))
365
+ .png()
366
+ .toBuffer()
367
+ await fs.promises.writeFile(`${tmpDir}/${fileName}`, fileBuffer)
368
+
369
+ const resultUrl = `https://${req.hostname}/file/${fileName}`
370
+ utils.isTrue(json)
371
+ ? res.json({ success: true, result: resultUrl })
372
+ : res[utils.isTrue(raw) ? 'send' : 'redirect'](resultUrl)
373
+ return
374
+ }
375
+
376
+ const fileName = utils.randomName('.webp')
377
+ const filePath = `${tmpDir}/${fileName}`
378
+ await fs.promises.writeFile(filePath, Buffer.from(file, 'base64'))
379
+
380
+ const exec = util.promisify(cp.exec).bind(cp)
381
+ await exec(`convert ${filePath} ${filePath.replace('webp', 'gif')}`)
382
+
383
+ let resultUrl
384
+ if (type === 'gif')
385
+ resultUrl = `https://${req.hostname}/file/${fileName.replace('webp', 'gif')}`
386
+ else {
387
+ await exec(
388
+ `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')}`
389
+ )
390
+ resultUrl = `https://${req.hostname}/file/${fileName.replace('webp', 'mp4')}`
391
+ }
392
+
393
+ utils.isTrue(json)
394
+ ? res.json({ success: true, result: resultUrl })
395
+ : res[utils.isTrue(raw) ? 'send' : 'redirect'](resultUrl)
396
+ } catch (e) {
397
+ console.log(e)
398
+ res.status(500).json({ error: true, message: utils.getError(e) })
399
+ }
400
+ })
401
+
402
+ // /yt /yt/dl /yt/download /yt/search /youtube/dl /youtube/download /youtube/search
403
+ app.all(/^\/y(outube|t)(\/(d(ownload|l)|search)?)?/, async (req, res) => {
404
+ if (!['GET', 'POST'].includes(req.method))
405
+ return res
406
+ .status(405)
407
+ .json({ success: false, message: 'Method Not Allowed' })
408
+
409
+ try {
410
+ const type = req.params[2]
411
+ const obj = req.allParams
412
+ if (type === 'search') {
413
+ if (!obj.query)
414
+ return res
415
+ .status(400)
416
+ .json({ success: false, message: "Required parameter 'query'" })
417
+
418
+ const result = await yts(obj)
419
+ if (!(result.all?.length || result?.url))
420
+ return res
421
+ .status(400)
422
+ .json({ success: false, message: 'Video unavailable' })
423
+
424
+ res.json({ success: true, result })
425
+ return
426
+ } else if (['dl', 'download'].includes(type)) {
427
+ if (!obj.url)
428
+ return res
429
+ .status(400)
430
+ .json({ success: false, message: "Required parameter 'url'" })
431
+ if (!utils.ytIdRegex.test(obj.url))
432
+ return res.status(400).json({ success: false, message: 'Invalid url' })
433
+
434
+ const isAudio = obj.type !== 'video'
435
+ const payload = {
436
+ downloadType: isAudio ? 'audio' : 'video',
437
+ quality: String(
438
+ obj.quality || isAudio ? 128 : 720
439
+ ),
440
+ url: obj.url
441
+ }
442
+
443
+ const result = await utils.fetchSaveTubeAPI(payload)
444
+ if (!result.data?.downloadUrl) {
445
+ console.log(result)
446
+ return res
447
+ .status(400)
448
+ .json({ success: false, message: 'An error occurred' })
449
+ }
450
+
451
+ res.redirect(result.data.downloadUrl)
452
+ return
453
+ }
454
+
455
+ if (!obj.query)
456
+ return res
457
+ .status(400)
458
+ .json({ success: false, message: "Required parameter 'query'" })
459
+
460
+ let result = await yts(
461
+ utils.ytIdRegex.test(obj.query)
462
+ ? { videoId: utils.ytIdRegex.exec(obj.query)[1] }
463
+ : obj.query
464
+ )
465
+ result = result.videos ? result.videos[0] : result
466
+ if (!result?.url)
467
+ return res
468
+ .status(400)
469
+ .json({ success: false, message: 'Video unavailable' })
470
+
471
+ const dlUrl = `https://${req.hostname}/yt/dl?url=${result.url}`
472
+ const download = {
473
+ audio: `${dlUrl}&type=audio`,
474
+ video: `${dlUrl}&type=video`
475
+ }
476
+ res.json({
477
+ success: true,
478
+ result: { ...result, download }
479
+ })
480
+ } catch (e) {
481
+ console.log(e)
482
+ res.status(500).json({ error: true, message: utils.getError(e) })
483
+ }
484
+ })
485
+
486
+ // app.use((req, res, next) => {})
487
+
488
+ const PORT = process.env.PORT || 7860
489
+ app.listen(PORT, () => console.log(`App running on port ${PORT}`))
package.json ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "type": "module",
3
+ "dependencies": {
4
+ "express": "*",
5
+ "morgan": "*",
6
+ "pdfkit": "*",
7
+ "playwright": "*",
8
+ "serve-favicon": "*",
9
+ "sharp": "*",
10
+ "yt-search": "*"
11
+ }
12
+ }