Fix muxing

This commit is contained in:
2026-05-15 04:10:06 +02:00
parent 05faf1ce3b
commit 6deb883472
26 changed files with 662 additions and 169 deletions

View File

@@ -2,10 +2,43 @@
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const jobStore_1 = require("../db/jobStore");
const config_1 = require("../../config/config");
const router = (0, express_1.Router)();
// Optional .env overrides for the (long) prompt strings — keep getDefaultConfig()'s
// hardcoded prompts as the final fallback. Users who want to tweak prompts without
// editing source can set these in .env, or set them per-job in the Settings UI.
const ENV_OVERRIDES = {
defaultPrompt: process.env.AIDIO_DEFAULT_PROMPT,
changePrompt: process.env.AIDIO_CHANGE_PROMPT,
batchPrompt: process.env.AIDIO_BATCH_PROMPT,
};
// Fields in Config that are nested objects (provider configs, etc.) and shouldn't
// be flattened into the form-facing config map. API keys live inside these — keep
// them off the wire.
const NESTED_FIELDS = new Set(['visionProviders', 'ttsProviders']);
function buildLayeredConfig() {
const defaults = (0, config_1.getDefaultConfig)();
const db = (0, jobStore_1.getAllConfig)();
const merged = {};
for (const [key, value] of Object.entries(defaults)) {
if (NESTED_FIELDS.has(key))
continue;
if (value === undefined || value === null)
continue;
merged[key] = String(value);
}
for (const [key, value] of Object.entries(ENV_OVERRIDES)) {
if (value !== undefined && value !== '')
merged[key] = value;
}
for (const [key, value] of Object.entries(db)) {
if (value !== undefined && value !== '')
merged[key] = value;
}
return merged;
}
router.get('/', (_req, res) => {
const config = (0, jobStore_1.getAllConfig)();
res.json({ config });
res.json({ config: buildLayeredConfig() });
});
router.put('/', (req, res) => {
const updates = req.body;
@@ -16,8 +49,7 @@ router.put('/', (req, res) => {
for (const [key, value] of Object.entries(updates)) {
(0, jobStore_1.setConfigValue)(key, String(value));
}
const config = (0, jobStore_1.getAllConfig)();
res.json({ config });
res.json({ config: buildLayeredConfig() });
});
exports.default = router;
//# sourceMappingURL=config.js.map

View File

@@ -1 +1 @@
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/server/routes/config.ts"],"names":[],"mappings":";;AAAA,qCAAoD;AACpD,6CAA8D;AAE9D,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AAExB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAa,EAAE,GAAa,EAAE,EAAE;IAC/C,MAAM,MAAM,GAAG,IAAA,uBAAY,GAAE,CAAC;IAC9B,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;IAC9C,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC;IACzB,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACpD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,+CAA+C,EAAE,CAAC,CAAC;QACjF,OAAO;IACT,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,IAAA,yBAAc,EAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACrC,CAAC;IACD,MAAM,MAAM,GAAG,IAAA,uBAAY,GAAE,CAAC;IAC9B,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC;AAEH,kBAAe,MAAM,CAAC"}
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/server/routes/config.ts"],"names":[],"mappings":";;AAAA,qCAAoD;AACpD,6CAA8D;AAC9D,gDAAuD;AAEvD,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AAExB,oFAAoF;AACpF,mFAAmF;AACnF,gFAAgF;AAChF,MAAM,aAAa,GAAuC;IACxD,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,oBAAoB;IAC/C,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB;IAC7C,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB;CAC5C,CAAC;AAEF,kFAAkF;AAClF,kFAAkF;AAClF,qBAAqB;AACrB,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAC,CAAC;AAEnE,SAAS,kBAAkB;IACzB,MAAM,QAAQ,GAAG,IAAA,yBAAgB,GAAwC,CAAC;IAC1E,MAAM,EAAE,GAAG,IAAA,uBAAY,GAAE,CAAC;IAE1B,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpD,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QACrC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;YAAE,SAAS;QACpD,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;QACzD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IAC/D,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;QAC9C,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IAC/D,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAa,EAAE,GAAa,EAAE,EAAE;IAC/C,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,kBAAkB,EAAE,EAAE,CAAC,CAAC;AAC7C,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;IAC9C,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC;IACzB,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACpD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,+CAA+C,EAAE,CAAC,CAAC;QACjF,OAAO;IACT,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,IAAA,yBAAc,EAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACrC,CAAC;IACD,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,kBAAkB,EAAE,EAAE,CAAC,CAAC;AAC7C,CAAC,CAAC,CAAC;AAEH,kBAAe,MAAM,CAAC"}

View File

@@ -65,23 +65,69 @@ router.get('/', (_req, res) => {
.sort((a, b) => b.filePath.localeCompare(a.filePath));
res.json({ files });
});
router.post('/youtube', (req, res) => {
router.delete('/:filename', (req, res) => {
const raw = req.params.filename;
const requested = Array.isArray(raw) ? raw[0] : raw;
if (!requested) {
res.status(400).json({ error: 'filename is required' });
return;
}
const resolved = path_1.default.resolve(UPLOADS_DIR, requested);
const uploadsWithSep = UPLOADS_DIR.endsWith(path_1.default.sep) ? UPLOADS_DIR : UPLOADS_DIR + path_1.default.sep;
if (!resolved.startsWith(uploadsWithSep)) {
res.status(400).json({ error: 'Invalid filename' });
return;
}
if (!fs_1.default.existsSync(resolved)) {
res.status(404).json({ error: 'File not found' });
return;
}
try {
fs_1.default.unlinkSync(resolved);
res.json({ ok: true });
}
catch (err) {
res.status(500).json({ error: `Failed to delete: ${err.message}` });
}
});
// Stream yt-dlp download progress over SSE.
// Returns events: {type:'progress', percent} ... {type:'done', filePath, filename, title}
// or {type:'error', message}
router.get('/youtube/stream', (req, res) => {
const url = req.query.url || '';
if (!url) {
res.status(400).json({ error: 'url query param is required' });
return;
}
if (!(0, ytDlp_1.isYtDlpAvailable)()) {
res.status(400).json({ error: 'yt-dlp is not installed or not in PATH' });
return;
}
const { url } = req.body;
if (!url) {
res.status(400).json({ error: 'URL is required' });
return;
}
try {
const result = (0, ytDlp_1.downloadVideo)(url, UPLOADS_DIR);
res.json(result);
}
catch (err) {
res.status(500).json({ error: `Failed to download: ${err.message}` });
}
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.setHeader('X-Accel-Buffering', 'no');
res.flushHeaders?.();
const send = (data) => {
res.write(`data: ${JSON.stringify(data)}\n\n`);
};
let clientGone = false;
req.on('close', () => { clientGone = true; });
(0, ytDlp_1.downloadVideo)(url, UPLOADS_DIR, (percent) => {
if (clientGone)
return;
send({ type: 'progress', percent });
}).then((result) => {
if (clientGone)
return;
send({ type: 'done', ...result });
res.end();
}).catch((err) => {
if (clientGone)
return;
send({ type: 'error', message: err.message });
res.end();
});
});
exports.default = router;
//# sourceMappingURL=files.js.map

File diff suppressed because one or more lines are too long

View File

@@ -145,10 +145,13 @@ function createJobsRouter(jobManager) {
filePath = format === 'vtt' ? job.output_subtitles_vtt : job.output_subtitles_srt;
filename = `${path_1.default.basename(job.video_filename, path_1.default.extname(job.video_filename))}_description.${format}`;
break;
case 'muxed':
case 'muxed': {
const opts = JSON.parse(job.output_options || '{}');
const suffix = opts.muxMode === 'mixed' ? '_described_mixed' : '_described';
filePath = job.output_muxed;
filename = `${path_1.default.basename(job.video_filename, path_1.default.extname(job.video_filename))}_described.mkv`;
filename = `${path_1.default.basename(job.video_filename, path_1.default.extname(job.video_filename))}${suffix}.mkv`;
break;
}
default:
res.status(400).json({ error: 'Invalid download type' });
return;

File diff suppressed because one or more lines are too long