Spaces:
Sleeping
Sleeping
Update index.js
Browse files
index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
| 1 |
import express from 'express';
|
| 2 |
import multer from 'multer';
|
| 3 |
import { spawn } from 'child_process';
|
| 4 |
-
import { writeFile, unlink } from 'fs/promises';
|
| 5 |
-
import { createReadStream
|
| 6 |
import path from 'path';
|
| 7 |
import { v4 as uuidv4 } from 'uuid';
|
| 8 |
import fetch from 'node-fetch';
|
|
@@ -29,7 +29,7 @@ const downloadFile = async (url) => {
|
|
| 29 |
return Buffer.from(arrayBuffer);
|
| 30 |
};
|
| 31 |
|
| 32 |
-
// --- "УМНЫЙ" ЭНДПОИНТ
|
| 33 |
app.post('/api/run/stream', upload.single('file'), async (req, res) => {
|
| 34 |
try {
|
| 35 |
const { command, args: argsJson, file_url } = req.body;
|
|
@@ -53,61 +53,45 @@ app.post('/api/run/stream', upload.single('file'), async (req, res) => {
|
|
| 53 |
return res.status(400).json({ error: 'A file must be provided via "file" or "file_url".' });
|
| 54 |
}
|
| 55 |
|
| 56 |
-
// --- СПЕЦИАЛЬНАЯ ЛОГИКА ДЛЯ FFMPEG
|
| 57 |
if (command === 'ffmpeg') {
|
| 58 |
const inputFilePath = path.join(TEMP_DIR, `${uuidv4()}-input`);
|
| 59 |
const outputFilePath = path.join(TEMP_DIR, `${uuidv4()}-output`);
|
| 60 |
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
// Если ffmpeg завершился с ошибкой до того, как мы начали стримить
|
| 96 |
-
if (!res.headersSent) {
|
| 97 |
-
res.status(500).json({ error: 'ffmpeg failed before output was generated.', code: code });
|
| 98 |
-
}
|
| 99 |
-
// Файлы будут удалены после завершения стрима (если он начался) или сразу
|
| 100 |
-
setTimeout(cleanup, 100); // Небольшая задержка для завершения потока
|
| 101 |
-
});
|
| 102 |
-
|
| 103 |
-
ffmpegProcess.on('error', (err) => {
|
| 104 |
-
watcher.close();
|
| 105 |
-
if (!res.headersSent) {
|
| 106 |
-
res.status(500).json({ error: 'Failed to start ffmpeg.', message: err.message });
|
| 107 |
-
}
|
| 108 |
-
cleanup();
|
| 109 |
-
});
|
| 110 |
-
|
| 111 |
// --- ОБЫЧНАЯ ЛОГИКА ДЛЯ ДРУГИХ КОМАНД ---
|
| 112 |
} else {
|
| 113 |
const process = spawn(command, args);
|
|
@@ -137,6 +121,7 @@ app.post('/api/run/stream', upload.single('file'), async (req, res) => {
|
|
| 137 |
});
|
| 138 |
|
| 139 |
|
|
|
|
| 140 |
// ... (остальной код для /api/task/* и /api/download/* без изменений) ...
|
| 141 |
|
| 142 |
const executeTask = async (taskId) => {
|
|
@@ -252,4 +237,3 @@ app.get('/api/download/:fileId', (req, res) => {
|
|
| 252 |
|
| 253 |
app.listen(PORT, () => { startCleanupJob(); });
|
| 254 |
|
| 255 |
-
|
|
|
|
| 1 |
import express from 'express';
|
| 2 |
import multer from 'multer';
|
| 3 |
import { spawn } from 'child_process';
|
| 4 |
+
import { writeFile, unlink, readFile } from 'fs/promises';
|
| 5 |
+
import { createReadStream } from 'fs';
|
| 6 |
import path from 'path';
|
| 7 |
import { v4 as uuidv4 } from 'uuid';
|
| 8 |
import fetch from 'node-fetch';
|
|
|
|
| 29 |
return Buffer.from(arrayBuffer);
|
| 30 |
};
|
| 31 |
|
| 32 |
+
// --- "УМНЫЙ" ЭНДПОИНТ ДЛЯ ПОТОКОВОЙ ОБРАБОТКИ ---
|
| 33 |
app.post('/api/run/stream', upload.single('file'), async (req, res) => {
|
| 34 |
try {
|
| 35 |
const { command, args: argsJson, file_url } = req.body;
|
|
|
|
| 53 |
return res.status(400).json({ error: 'A file must be provided via "file" or "file_url".' });
|
| 54 |
}
|
| 55 |
|
| 56 |
+
// --- СПЕЦИАЛЬНАЯ ЛОГИКА ДЛЯ FFMPEG ---
|
| 57 |
if (command === 'ffmpeg') {
|
| 58 |
const inputFilePath = path.join(TEMP_DIR, `${uuidv4()}-input`);
|
| 59 |
const outputFilePath = path.join(TEMP_DIR, `${uuidv4()}-output`);
|
| 60 |
|
| 61 |
+
try {
|
| 62 |
+
await writeFile(inputFilePath, inputBuffer);
|
| 63 |
+
|
| 64 |
+
const processedArgs = args.map(arg =>
|
| 65 |
+
arg.replace('{INPUT_FILE}', inputFilePath)
|
| 66 |
+
.replace('{OUTPUT_FILE}', outputFilePath)
|
| 67 |
+
);
|
| 68 |
+
|
| 69 |
+
const process = spawn(command, processedArgs);
|
| 70 |
+
let stderrChunks = [];
|
| 71 |
+
process.stderr.on('data', (data) => stderrChunks.push(data));
|
| 72 |
+
|
| 73 |
+
process.on('close', async (code) => {
|
| 74 |
+
if (code === 0) {
|
| 75 |
+
try {
|
| 76 |
+
const outputBuffer = await readFile(outputFilePath);
|
| 77 |
+
res.setHeader('Content-Type', 'application/octet-stream');
|
| 78 |
+
res.send(outputBuffer);
|
| 79 |
+
} catch (readError) {
|
| 80 |
+
res.status(500).json({ error: "Command succeeded, but failed to read output file.", message: readError.message });
|
| 81 |
+
}
|
| 82 |
+
} else {
|
| 83 |
+
const stderr = Buffer.concat(stderrChunks).toString('utf8');
|
| 84 |
+
res.status(500).json({ error: 'Command execution failed.', code: code, stderr: stderr });
|
| 85 |
+
}
|
| 86 |
+
// Очистка
|
| 87 |
+
await unlink(inputFilePath).catch(()=>{});
|
| 88 |
+
await unlink(outputFilePath).catch(()=>{});
|
| 89 |
+
});
|
| 90 |
+
} catch (execError) {
|
| 91 |
+
res.status(500).json({ error: "Failed during ffmpeg file operations.", message: execError.message });
|
| 92 |
+
await unlink(inputFilePath).catch(()=>{});
|
| 93 |
+
await unlink(outputFilePath).catch(()=>{});
|
| 94 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
// --- ОБЫЧНАЯ ЛОГИКА ДЛЯ ДРУГИХ КОМАНД ---
|
| 96 |
} else {
|
| 97 |
const process = spawn(command, args);
|
|
|
|
| 121 |
});
|
| 122 |
|
| 123 |
|
| 124 |
+
// --- СИСТЕМА АСИНХРОННЫХ ЗАДАЧ (ОСТАЕТСЯ КАК АЛЬТЕРНАТИВА) ---
|
| 125 |
// ... (остальной код для /api/task/* и /api/download/* без изменений) ...
|
| 126 |
|
| 127 |
const executeTask = async (taskId) => {
|
|
|
|
| 237 |
|
| 238 |
app.listen(PORT, () => { startCleanupJob(); });
|
| 239 |
|
|
|