rippanteq7 commited on
Commit
196f9ca
·
verified ·
1 Parent(s): 4576573

Upload folder using huggingface_hub

Browse files
Files changed (4) hide show
  1. Dockerfile +17 -0
  2. favicon.ico +0 -0
  3. index.js +302 -0
  4. 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", "."]
favicon.ico ADDED
index.js ADDED
@@ -0,0 +1,302 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ fetchAIODown: async (url) => {
16
+ const fetchToken = async () =>
17
+ (await (await fetch('https://aiodown.com/')).text()).match(/value="(\w+)/)?.[1]
18
+
19
+ let resp = await utils.fetchPOST('https://aiodown.com/wp-json/aio-dl/video-data/', new URLSearchParams({
20
+ url, token: await fetchToken(), hash: btoa(url) + (url.length + 1000) + btoa('aio-dl')
21
+ }), {
22
+ headers: { 'X-Forwarded-For': utils.randomIP() }
23
+ })
24
+ if (!resp.ok) {
25
+ let content = resp.headers.get('content-type')
26
+ if (/json$/.test(content)) {
27
+ let json = await resp.json()
28
+ throw json.message || 'An error occurred'
29
+ } else {
30
+ throw resp.statusText
31
+ }
32
+ }
33
+
34
+ return resp.json()
35
+ },
36
+ // from https://github.com/petersolopov/carbonara
37
+ fetchCarbonaraAPI: async (code, output, opts = {}) => {
38
+ let resp = await utils.fetchPOST('https://carbonara.solopov.dev/api/cook', JSON.stringify({ code, ...opts }), {
39
+ headers: { 'Content-Type': 'application/json' }
40
+ })
41
+ if (!resp.ok) {
42
+ let content = resp.headers.get('content-type')
43
+ if (/json$/.test(content)) {
44
+ let json = await resp.json()
45
+ throw json.error || 'An error occurred'
46
+ } else {
47
+ throw resp.statusText
48
+ }
49
+ }
50
+
51
+ let img = await resp.arrayBuffer()
52
+ await fs.promises.writeFile(output, Buffer.from(img))
53
+ return output
54
+ },
55
+ fetchCobaltAPI: async (url, opts = {}) => (
56
+ await utils.fetchPOST('https://kityune.imput.net/api/json', JSON.stringify({ url, ...opts }), {
57
+ headers: { Accept: 'application/json', 'Content-Type': 'application/json' }
58
+ })
59
+ ).json(),
60
+ fetchYTizAPI: async (url, opts = {}) => {
61
+ const baseUrl = 'https://m1.fly.dev/api'
62
+ const headers = { 'Content-Type': 'application/json' }
63
+ const fetchInfo = async (args) =>
64
+ (await utils.fetchPOST(`${baseUrl}/info`, JSON.stringify(args), { headers })).json()
65
+ const fetchDownload = async (args) =>
66
+ (await utils.fetchPOST(`${baseUrl}/download`, JSON.stringify(args), { headers })).json()
67
+
68
+ const body = { url, format: 'mp3', startTime: 0, endTime: 0, ...opts }
69
+ let info = await fetchInfo({ ...body, ...opts })
70
+ let dl = await fetchDownload({
71
+ quality: '320',
72
+ metadata: true,
73
+ trim: false,
74
+ ...body,
75
+ ...info,
76
+ ...opts
77
+ })
78
+
79
+ return utils.fetchPOST(`${baseUrl}/file_send`, JSON.stringify(dl), { headers })
80
+ },
81
+ fetchPOST: (url, body, opts = {}) => fetch(url, { method: 'POST', body, ...opts }),
82
+ formatSize: (n) => bytes(+n, { unitSeparator: ' ' }),
83
+ getError: (e) => String(e).startsWith('[object ') ? 'Internal Server Error' : String(e),
84
+ isBase64: (str) => {
85
+ try {
86
+ return btoa(atob(str)) === str
87
+ } catch {
88
+ return false
89
+ }
90
+ },
91
+ isTrue: (str) => [true, 'true'].includes(str),
92
+ randomIP: () => [...new Array(4)].map(() => ~~(Math.random() * 256)).join('.'),
93
+ randomName: (str = '') => (Math.random().toString(36).slice(2) + str),
94
+ toPDF: (urls, opts = {}) => new Promise(async (resolve, reject) => {
95
+ try {
96
+ const doc = new PDFDocument({ margin: 0, size: 'A4' })
97
+ const buffs = []
98
+
99
+ for (let x = 0; x < urls.length; x++) {
100
+ if (!/https?:\/\//.test(urls[x])) continue
101
+ const url = new URL(urls[x])
102
+ let image = await fetch(url.toString(), { headers: { referer: url.origin } })
103
+ if (!image.ok) continue
104
+
105
+ const type = image.headers.get('content-type')
106
+ if (!/image/.test(type)) continue
107
+ image = Buffer.from(await image.arrayBuffer())
108
+ if (/(gif|webp)$/.test(type)) image = await sharp(image).png().toBuffer()
109
+
110
+ doc.image(image, 0, 0, { fit: [595.28, 841.89], align: 'center', valign: 'center', ...opts })
111
+ if (urls.length != x + 1) doc.addPage()
112
+ }
113
+
114
+ doc.on('data', (chunk) => buffs.push(chunk))
115
+ doc.on('end', () => resolve(Buffer.concat(buffs)))
116
+ doc.on('error', (err) => reject(err))
117
+ doc.end()
118
+ } catch (e) {
119
+ console.log(e)
120
+ reject(e)
121
+ }
122
+ }),
123
+ // from https://github.com/Nurutomo/wabot-aq/blob/master/lib/y2mate.js#L15
124
+ ytIdRegex: /(?:http(?:s|):\/\/|)(?:(?:www\.|)?youtube(?:\-nocookie|)\.com\/(?:shorts\/)?(?:watch\?.*(?:|\&)v=|embed\/|live\/|v\/)?|youtu\.be\/)([-_0-9A-Za-z]{11})/,
125
+ }
126
+
127
+ const app = express()
128
+ const tmpDir = os.tmpdir()
129
+
130
+ app.set('json spaces', 4)
131
+ app.use(express.json({ limit: '200mb' }))
132
+ app.use(express.urlencoded({ extended: true, limit: '200mb' }))
133
+ app.use(favicon(path.join(import.meta.dirname, 'favicon.ico')))
134
+ app.use(morgan('combined'))
135
+
136
+ app.use((req, __, next) => {
137
+ // clear tmp
138
+ for (let file of fs.readdirSync(tmpDir)) {
139
+ file = `${tmpDir}/${file}`
140
+ const stat = fs.statSync(file)
141
+ const exp = Date.now() - stat.mtimeMs >= 1000 * 60 * 30
142
+ if (stat.isFile() && exp) {
143
+ console.log('Deleted file', file)
144
+ fs.unlinkSync(file)
145
+ }
146
+ }
147
+ req.allParams = Object.assign(req.query, req.body)
148
+ next()
149
+ })
150
+
151
+ app.use('/file', express.static(tmpDir))
152
+
153
+ app.all('/', (_, res) => {
154
+ const status = {}
155
+ status['diskUsage'] = cp.execSync('du -sh').toString().split('M')[0] + ' MB'
156
+
157
+ const used = process.memoryUsage()
158
+ for (let x in used) status[x] = utils.formatSize(used[x])
159
+
160
+ const totalmem = os.totalmem()
161
+ const freemem = os.freemem()
162
+ status['memoryUsage'] = `${utils.formatSize(totalmem - freemem)} / ${utils.formatSize(totalmem)}`
163
+
164
+ res.json({
165
+ creator: `@${process.env['SPACE_AUTHOR_NAME'] || 'rippanteq7'}`,
166
+ message: 'Hello World!',
167
+ uptime: new Date(process.uptime() * 1000).toUTCString().split(' ')[4],
168
+ status
169
+ })
170
+ })
171
+
172
+ app.all('/carbon', async (req, res) => {
173
+ if (!['GET', 'POST'].includes(req.method)) return res.status(405).json({ success: false, message: 'Method Not Allowed' })
174
+
175
+ try {
176
+ const obj = req.allParams
177
+ if (!obj.code) return res.status(400).json({ success: false, message: 'Required parameter \'code\'' })
178
+
179
+ const filePath = `${tmpDir}/${utils.randomName('.png')}`
180
+ // nembak API carbonara njir
181
+ const image = await utils.fetchCarbonaraAPI(obj.code, filePath, obj)
182
+ const resultUrl = `https://${req.hostname}/${image.replace(tmpDir, 'file')}`
183
+ utils.isTrue(obj.json) ? res.json({ success: true, result: resultUrl }) : res[utils.isTrue(obj.raw) ? 'send' : 'redirect'](resultUrl)
184
+ } catch (e) {
185
+ console.log(e)
186
+ res.status(500).json({ error: true, message: utils.getError(e) })
187
+ }
188
+ })
189
+
190
+ app.all('/topdf', async (req, res) => {
191
+ if (req.method !== 'POST') return res.status(405).json({ success: false, message: 'Method Not Allowed' })
192
+
193
+ try {
194
+ const { images: urls, json, raw } = req.body
195
+ if (!urls) return res.status(400).json({ success: false, message: 'Payload \'images\' requires an array of urls' })
196
+ if (!Array.isArray(urls)) urls = [urls]
197
+
198
+ const bufferPDF = await utils.toPDF(urls)
199
+ if (!bufferPDF.length) return res.status(400).json({ success: false, message: 'Can\'t convert to pdf' })
200
+
201
+ const fileName = utils.randomName('.pdf')
202
+ await fs.promises.writeFile(`${tmpDir}/${fileName}`, bufferPDF)
203
+
204
+ const resultUrl = `https://${req.hostname}/file/${fileName}`
205
+ utils.isTrue(json) ? res.json({ success: true, result: resultUrl }) : res[utils.isTrue(raw) ? 'send' : 'redirect'](resultUrl)
206
+ } catch (e) {
207
+ console.log(e)
208
+ res.status(500).json({ error: true, message: utils.getError(e) })
209
+ }
210
+ })
211
+
212
+ app.all(/^\/webp2(gif|mp4|png)/, async (req, res) => {
213
+ if (req.method !== 'POST') return res.status(405).json({ success: false, message: 'Method Not Allowed' })
214
+
215
+ try {
216
+ const { file, json, raw } = req.body
217
+ if (!file) return res.status(400).json({ success: false, message: 'Payload \'file\' requires base64 string' })
218
+ if (!utils.isBase64(file)) return res.status(400).json({ success: false, message: 'Invalid base64 format' })
219
+
220
+ const type = req.params[0]
221
+ if (type === 'png') {
222
+ const fileName = utils.randomName('.png')
223
+ const fileBuffer = await sharp(Buffer.from(file, 'base64')).png().toBuffer()
224
+ await fs.promises.writeFile(`${tmpDir}/${fileName}`, fileBuffer)
225
+
226
+ const resultUrl = `https://${req.hostname}/file/${fileName}`
227
+ utils.isTrue(json) ? res.json({ success: true, result: resultUrl }) : res[utils.isTrue(raw) ? 'send' : 'redirect'](resultUrl)
228
+ } else {
229
+ const fileName = utils.randomName('.webp')
230
+ const filePath = `${tmpDir}/${fileName}`
231
+ await fs.promises.writeFile(filePath, Buffer.from(file, 'base64'))
232
+
233
+ const exec = util.promisify(cp.exec).bind(cp)
234
+ await exec(`convert ${filePath} ${filePath.replace('webp', 'gif')}`)
235
+
236
+ let resultUrl
237
+ if (type === 'gif') resultUrl = `https://${req.hostname}/file/${fileName.replace('webp', 'gif')}`
238
+ else {
239
+ 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')}`)
240
+ resultUrl = `https://${req.hostname}/file/${fileName.replace('webp', 'mp4')}`
241
+ }
242
+
243
+ utils.isTrue(json) ? res.json({ success: true, result: resultUrl }) : res[utils.isTrue(raw) ? 'send' : 'redirect'](resultUrl)
244
+ }
245
+ } catch (e) {
246
+ console.log(e)
247
+ res.status(500).json({ error: true, message: utils.getError(e) })
248
+ }
249
+ })
250
+
251
+ // /yt /yt/dl /yt/download /yt/search /youtube/dl /youtube/download /youtube/search
252
+ app.all(/^\/y(outube|t)(\/(d(ownload|l)|search)?)?/, async (req, res) => {
253
+ if (!['GET', 'POST'].includes(req.method)) return res.status(405).json({ success: false, message: 'Method Not Allowed' })
254
+
255
+ try {
256
+ const type = req.params[2]
257
+ const obj = req.allParams
258
+ if (type === 'search') {
259
+ if (!obj.query) return res.status(400).json({ success: false, message: 'Required parameter \'query\'' })
260
+
261
+ const result = await yts(obj)
262
+ if (!(result.all?.length || result?.url)) return res.status(400).json({ success: false, message: 'Video unavailable' })
263
+
264
+ res.json({ success: true, result })
265
+ return
266
+ } else if (['dl', 'download'].includes(type)) {
267
+ if (!obj.url) return res.status(400).json({ success: false, message: 'Required parameter \'url\'' })
268
+ if (!utils.ytIdRegex.test(obj.url)) return res.status(400).json({ success: false, message: 'Invalid url' })
269
+ // nembak lagi woila
270
+ const result = await utils.fetchCobaltAPI(obj.url, { isAudioOnly: obj.type === 'audio', vQuality: 'max', ...obj })
271
+ if (result.status === 'error') return res.status(400).json({ success: false, message: result.text || 'An error occurred' })
272
+ res.redirect(result.url)
273
+ /*
274
+ if (obj.type === 'audio') {
275
+ const result = await utils.fetchYTizAPI(obj.url)
276
+
277
+ }
278
+ */
279
+ return
280
+ } else {
281
+ if (!obj.query) return res.status(400).json({ success: false, message: 'Required parameter \'query\'' })
282
+
283
+ let result = await yts(utils.ytIdRegex.test(obj.query) ? { videoId: utils.ytIdRegex.exec(obj.query)[1] } : obj.query)
284
+ result = result.videos ? result.videos[0] : result
285
+ if (!result?.url) return res.status(400).json({ success: false, message: 'Video unavailable' })
286
+
287
+ const dlUrl = `https://${req.hostname}/yt/dl?url=${result.url}`
288
+ const download = { audio: `${dlUrl}&type=audio`, video: `${dlUrl}&type=video` }
289
+ res.json({
290
+ success: true,
291
+ result: { ...result, download }
292
+ })
293
+ }
294
+ } catch (e) {
295
+ console.log(e)
296
+ res.status(500).json({ error: true, message: utils.getError(e) })
297
+ }
298
+ })
299
+
300
+ // app.use((req, res, next) => {})
301
+
302
+ 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
+ }