Spaces:
Running
Running
Create server.js
Browse files
server.js
ADDED
@@ -0,0 +1,249 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import axios from 'axios';
|
2 |
+
import cheerio from 'cheerio';
|
3 |
+
import { createRequire } from 'module';
|
4 |
+
import os from 'os';
|
5 |
+
import express from 'express';
|
6 |
+
import { promisify } from 'util';
|
7 |
+
import { fileTypeFromBuffer } from 'file-type';
|
8 |
+
import ffmpeg from 'fluent-ffmpeg';
|
9 |
+
import nodeID3 from 'node-id3';
|
10 |
+
import ytdl from 'ytdl-core';
|
11 |
+
import FormData from 'form-data';
|
12 |
+
import fetch from 'node-fetch';
|
13 |
+
|
14 |
+
const require = createRequire(import.meta.url);
|
15 |
+
const fs = require('fs');
|
16 |
+
const path = require('path');
|
17 |
+
const { google } = require('googleapis');
|
18 |
+
const puppeteer = require('puppeteer');
|
19 |
+
|
20 |
+
const PORT = process.env.PORT || 7860;
|
21 |
+
const app = express();
|
22 |
+
const readFileAsync = promisify(fs.readFile);
|
23 |
+
|
24 |
+
const tempDir = path.join(os.tmpdir(), "temp");
|
25 |
+
const fss = fs.promises;
|
26 |
+
|
27 |
+
// Membuat direktori sementara jika belum ada
|
28 |
+
(async () => {
|
29 |
+
if (!fs.existsSync(tempDir)) {
|
30 |
+
await fss.mkdir(tempDir, { recursive: true });
|
31 |
+
}
|
32 |
+
})();
|
33 |
+
|
34 |
+
const youtube = google.youtube({ version: 'v3', auth: 'AIzaSyBPkpdJEGtAHebbaP3_CcA1_urfMFfeLLg' });
|
35 |
+
|
36 |
+
app.use('/temp', express.static(tempDir));
|
37 |
+
app.use(express.json());
|
38 |
+
|
39 |
+
app.get("/", (req, res) => {
|
40 |
+
res.type("json");
|
41 |
+
const keluaran = {
|
42 |
+
success: true,
|
43 |
+
author: "Nex",
|
44 |
+
data: {
|
45 |
+
igdl: "/igdl",
|
46 |
+
twdl: "/twdl"
|
47 |
+
},
|
48 |
+
};
|
49 |
+
res.send(keluaran);
|
50 |
+
});
|
51 |
+
|
52 |
+
// Fungsi untuk menghasilkan IP acak
|
53 |
+
const generateRandomIP = () => {
|
54 |
+
const octet = () => Math.floor(Math.random() * 256);
|
55 |
+
return `${octet()}.${octet()}.${octet()}.${octet()}`;
|
56 |
+
};
|
57 |
+
|
58 |
+
// Fungsi untuk upload file
|
59 |
+
async function uploader(buffer) {
|
60 |
+
const { ext } = await fileTypeFromBuffer(buffer);
|
61 |
+
const bodyForm = new FormData();
|
62 |
+
bodyForm.append('file', buffer, `file.${ext}`);
|
63 |
+
|
64 |
+
const response = await fetch('https://aemt.me/api/upload.php', {
|
65 |
+
method: 'POST',
|
66 |
+
body: bodyForm,
|
67 |
+
});
|
68 |
+
|
69 |
+
return {
|
70 |
+
status: response.status,
|
71 |
+
creator: 'Nex',
|
72 |
+
result: await response.json(),
|
73 |
+
};
|
74 |
+
}
|
75 |
+
|
76 |
+
// Fungsi untuk mendapatkan URL thumbnail HD
|
77 |
+
async function getHDThumbnailUrl(videoId) {
|
78 |
+
try {
|
79 |
+
const response = await youtube.videos.list({ part: 'snippet', id: videoId });
|
80 |
+
return response.data.items[0].snippet.thumbnails.maxres.url;
|
81 |
+
} catch (error) {
|
82 |
+
console.error('Error fetching HD thumbnail URL:', error.message);
|
83 |
+
return null;
|
84 |
+
}
|
85 |
+
}
|
86 |
+
|
87 |
+
// Fungsi untuk mendapatkan ID video dari URL
|
88 |
+
async function GetId(data) {
|
89 |
+
const regex = /(?:https?:\/\/)?(?:www\.)?(?:youtu(?:be\.com\/(?:watch\?(?:v=|vi=)|v\/|vi\/)|\.be\/|be\.com\/embed\/|be\.com\/shorts\/)|youtube\.com\/\?(?:v=|vi=))([\w-]{11})/;
|
90 |
+
const res = regex.exec(data);
|
91 |
+
if (res && res[1]) return res[1];
|
92 |
+
throw new Error("Please check the URL you have entered");
|
93 |
+
}
|
94 |
+
|
95 |
+
// Fungsi untuk menambahkan tag ID3 ke file audio
|
96 |
+
async function addAudioTags(media, title, artist, year, imagecover) {
|
97 |
+
try {
|
98 |
+
let audioBuffer;
|
99 |
+
if (typeof media === 'string') {
|
100 |
+
const response = await axios.get(media, { responseType: 'arraybuffer', maxContentLength: -1 });
|
101 |
+
audioBuffer = Buffer.from(response.data);
|
102 |
+
} else if (media instanceof Buffer) {
|
103 |
+
audioBuffer = media;
|
104 |
+
} else {
|
105 |
+
throw new Error('Media harus berupa URL string atau Buffer.');
|
106 |
+
}
|
107 |
+
|
108 |
+
const randomFilename = generateRandomName(10) + '.mp3';
|
109 |
+
const tmpFilePath = path.join(tempDir, randomFilename);
|
110 |
+
fs.writeFileSync(tmpFilePath, audioBuffer);
|
111 |
+
|
112 |
+
const tags = { title, artist, year };
|
113 |
+
if (typeof imagecover === 'string') {
|
114 |
+
const response = await axios.get(imagecover, { responseType: 'arraybuffer' });
|
115 |
+
const coverBuffer = Buffer.from(response.data);
|
116 |
+
tags.image = {
|
117 |
+
mime: 'image/jpeg',
|
118 |
+
type: { id: 3, name: 'Front Cover' },
|
119 |
+
description: 'Cover',
|
120 |
+
imageBuffer: coverBuffer
|
121 |
+
};
|
122 |
+
} else if (imagecover instanceof Buffer) {
|
123 |
+
tags.image = {
|
124 |
+
mime: 'image/jpeg',
|
125 |
+
type: { id: 3, name: 'Front Cover' },
|
126 |
+
description: 'Cover',
|
127 |
+
imageBuffer: imagecover
|
128 |
+
};
|
129 |
+
}
|
130 |
+
|
131 |
+
const success = nodeID3.write(tags, tmpFilePath);
|
132 |
+
console[success ? 'log' : 'error'](success ? 'Tag ID3 berhasil diubah!' : 'Gagal mengubah tag ID3.');
|
133 |
+
|
134 |
+
return { msg: `Audio berhasil diubah.`, path: `${tmpFilePath}` };
|
135 |
+
} catch (error) {
|
136 |
+
console.error('Terjadi kesalahan:', error);
|
137 |
+
throw new Error('Terjadi kesalahan saat mengubah audio.');
|
138 |
+
}
|
139 |
+
}
|
140 |
+
|
141 |
+
// Fungsi untuk menghasilkan nama acak
|
142 |
+
function generateRandomName(length) {
|
143 |
+
const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
144 |
+
let randomName = '';
|
145 |
+
for (let i = 0; i < length; i++) {
|
146 |
+
randomName += characters.charAt(Math.floor(Math.random() * characters.length));
|
147 |
+
}
|
148 |
+
return randomName;
|
149 |
+
}
|
150 |
+
|
151 |
+
// Fungsi untuk mendapatkan URL audio MP3 dari video YouTube
|
152 |
+
async function getAudioMP3Url(videoUrl) {
|
153 |
+
try {
|
154 |
+
const info = await ytdl.getInfo(videoUrl);
|
155 |
+
const audioFormat = ytdl.chooseFormat(info.formats, { filter: 'audioonly', quality: 'highestaudio' });
|
156 |
+
const path_audio = path.join(tempDir, generateRandomName(10) + '.mp3');
|
157 |
+
let uploadResult;
|
158 |
+
|
159 |
+
await new Promise((resolve, reject) => {
|
160 |
+
ffmpeg()
|
161 |
+
.input(audioFormat.url)
|
162 |
+
.outputOptions('-f mp3')
|
163 |
+
.outputOptions('-acodec libmp3lame')
|
164 |
+
.outputOptions('-ab 128k')
|
165 |
+
.outputOptions('-ar 44100')
|
166 |
+
.on('end', async () => {
|
167 |
+
const buffer = fs.readFileSync(path_audio);
|
168 |
+
const id_video = await GetId(videoUrl);
|
169 |
+
const hd_thumbnail = await getHDThumbnailUrl(id_video);
|
170 |
+
const convert = await addAudioTags(buffer, info.videoDetails.title, info.videoDetails.ownerChannelName, 2024, hd_thumbnail);
|
171 |
+
const buffer2 = fs.readFileSync(convert.path);
|
172 |
+
|
173 |
+
fs.unlinkSync(path_audio);
|
174 |
+
|
175 |
+
resolve();
|
176 |
+
})
|
177 |
+
.on('error', (err) => {
|
178 |
+
console.error('FFmpeg conversion error:', err);
|
179 |
+
reject(err);
|
180 |
+
})
|
181 |
+
.save(path_audio);
|
182 |
+
});
|
183 |
+
|
184 |
+
return {
|
185 |
+
status: 200,
|
186 |
+
title: info.videoDetails.title,
|
187 |
+
result: {
|
188 |
+
url_path: `${process.env.SPACE_HOST}/temp/${path.basename(convert.path)}`,
|
189 |
+
path: convert.path
|
190 |
+
}
|
191 |
+
};
|
192 |
+
} catch (error) {
|
193 |
+
console.error('Error:', error);
|
194 |
+
throw new Error('Failed to process audio URL');
|
195 |
+
}
|
196 |
+
}
|
197 |
+
|
198 |
+
// Endpoint untuk mengunduh audio MP3 dari YouTube
|
199 |
+
app.get('/ytmp3', async (req, res) => {
|
200 |
+
try {
|
201 |
+
const { url } = req.query;
|
202 |
+
if (!url) return res.status(400).json({ error: 'Parameter url is required' });
|
203 |
+
|
204 |
+
let result = await getAudioMP3Url(url);
|
205 |
+
res.json(result);
|
206 |
+
|
207 |
+
// Menghapus file setelah 10 menit
|
208 |
+
try {
|
209 |
+
await new Promise(resolve => setTimeout(resolve, 10 * 60 * 1000)); // 10 minutes
|
210 |
+
await fss.unlink(result.result.path);
|
211 |
+
console.log(`File ${result.result.path} deleted.`);
|
212 |
+
} catch (error) {
|
213 |
+
console.error(`Error deleting file ${result.result.path}:`, error);
|
214 |
+
}
|
215 |
+
} catch (error) {
|
216 |
+
console.error('Error processing request:', error);
|
217 |
+
res.status(500).json({
|
218 |
+
error: 'Failed to process request\n' + error
|
219 |
+
});
|
220 |
+
}
|
221 |
+
});
|
222 |
+
|
223 |
+
// Fungsi untuk ping website
|
224 |
+
async function pingWebsite() {
|
225 |
+
const browser = await puppeteer.launch({
|
226 |
+
headless: true,
|
227 |
+
args: ['--no-sandbox', '--disable-setuid-sandbox']
|
228 |
+
});
|
229 |
+
const page = await browser.newPage();
|
230 |
+
await page.setUserAgent("Mozilla/5.0 (Linux; Android 10; SM-G965U Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.141 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/420.0.0.32.61;]");
|
231 |
+
await page.goto('https://huggingface.co/spaces/ArashiCode/api');
|
232 |
+
console.log("Ping");
|
233 |
+
await browser.close();
|
234 |
+
}
|
235 |
+
|
236 |
+
// Ping website setiap 5 jam
|
237 |
+
async function pingEvery5Hours() {
|
238 |
+
await pingWebsite();
|
239 |
+
setInterval(async () => {
|
240 |
+
await pingWebsite();
|
241 |
+
}, 5 * 60 * 60 * 1000); // 5 hours in milliseconds
|
242 |
+
}
|
243 |
+
|
244 |
+
// Mulai ping
|
245 |
+
pingEvery5Hours();
|
246 |
+
|
247 |
+
app.listen(PORT, () => {
|
248 |
+
console.log(`Server is running on port ${PORT}`);
|
249 |
+
});
|