添加停止转码按钮

This commit is contained in:
CN-JS-HuiBai
2026-04-02 20:55:22 +08:00
parent c8151a7779
commit 4e4db42934
4 changed files with 114 additions and 1 deletions

View File

@@ -59,6 +59,7 @@ const createS3Client = (credentials) => {
};
const progressMap = {};
const transcodeProcesses = new Map();
const wsSubscriptions = new Map();
const getProgressKey = (key) => key.split('/').map(segment => segment.replace(/[^a-zA-Z0-9_\-]/g, '_')).join('/');
@@ -187,6 +188,14 @@ const clearMp4Cache = () => {
};
const clearTranscodeCache = () => {
for (const command of transcodeProcesses.values()) {
try {
if (typeof command.kill === 'function') {
command.kill('SIGKILL');
}
} catch (_) {}
}
transcodeProcesses.clear();
clearMp4Cache();
Object.keys(progressMap).forEach((key) => delete progressMap[key]);
};
@@ -287,6 +296,42 @@ app.post('/api/clear-transcode-cache', (req, res) => {
}
});
app.post('/api/stop-transcode', (req, res) => {
try {
const { key } = req.body;
if (!key) {
return res.status(400).json({ error: 'Video key is required' });
}
const progressKey = getProgressKey(key);
const command = transcodeProcesses.get(progressKey);
if (!command) {
return res.status(404).json({ error: 'No active transcode found for this key' });
}
try {
if (typeof command.kill === 'function') {
command.kill('SIGKILL');
}
} catch (killError) {
console.warn('Failed to kill transcode process:', killError);
}
transcodeProcesses.delete(progressKey);
progressMap[progressKey] = {
status: 'cancelled',
percent: 0,
details: 'Transcode stopped by user',
mp4Url: `/mp4/${progressKey}/video.mp4`
};
broadcastWs(progressKey, { type: 'progress', key, progress: progressMap[progressKey] });
res.json({ message: 'Transcode stopped' });
} catch (error) {
console.error('Error stopping transcode:', error);
res.status(500).json({ error: 'Failed to stop transcode', detail: error.message });
}
});
// Endpoint to transcode S3 video streaming to MP4
app.post('/api/transcode', async (req, res) => {
const { bucket, key, codec, encoder } = req.body;
@@ -320,6 +365,7 @@ app.post('/api/transcode', async (req, res) => {
const mp4Url = `/mp4/${progressKey}/video.mp4`;
progressMap[progressKey] = { status: 'pending', percent: 0, details: 'Waiting for ffmpeg to start', mp4Url };
let currentFfmpegCommand = null;
// If it already exists, just return the URL
if (fs.existsSync(mp4Path)) {
@@ -396,6 +442,8 @@ app.post('/api/transcode', async (req, res) => {
.videoCodec(encoderName)
.audioCodec('aac')
.outputOptions(createFfmpegOptions(encoderName));
transcodeProcesses.set(progressKey, command);
currentFfmpegCommand = command;
if (/_vaapi$/.test(encoderName)) {
command
.inputOptions(['-vaapi_device', '/dev/dri/renderD128'])
@@ -435,6 +483,7 @@ app.post('/api/transcode', async (req, res) => {
console.error(`Output verification failed for ${mp4Path}:`, verifyError);
progressState = { status: 'failed', percent: progressMap[progressKey]?.percent || 0, details: `Output verification failed: ${verifyError.message}`, mp4Url };
}
transcodeProcesses.delete(progressKey);
progressMap[progressKey] = progressState;
broadcastWs(progressKey, { type: 'progress', key, progress: progressState });
if (progressState.status === 'finished') {
@@ -445,6 +494,7 @@ app.post('/api/transcode', async (req, res) => {
const errMessage = err?.message || '';
const isHardwareFailure = !attemptedSoftwareFallback && encoderName !== codecMap.software[safeCodec] && shouldRetryWithSoftware(errMessage);
if (isHardwareFailure) {
transcodeProcesses.delete(progressKey);
attemptedSoftwareFallback = true;
console.warn(`Hardware encoder failed for ${key}; retrying with software encoder`, errMessage);
try {
@@ -465,6 +515,7 @@ app.post('/api/transcode', async (req, res) => {
}
cleanupTmpInput();
transcodeProcesses.delete(progressKey);
console.error(`Error transcoding ${key}:`, err);
const failedState = { status: 'failed', percent: progressMap[progressKey]?.percent || 0, details: err.message || 'Transcoding failed', mp4Url };
progressMap[progressKey] = failedState;