fix(console): map Beep files to Audio/beep
Store and present Beep assets under Audio/beep while keeping legacy Audio/general reads working so existing files remain accessible during the directory transition.
This commit is contained in:
@@ -1585,6 +1585,8 @@
|
|||||||
source = `/media/audio/music/${encodeURIComponent(path.slice('Audio/music/'.length))}`;
|
source = `/media/audio/music/${encodeURIComponent(path.slice('Audio/music/'.length))}`;
|
||||||
} else if (path.startsWith('Audio/voice/')) {
|
} else if (path.startsWith('Audio/voice/')) {
|
||||||
source = `/media/audio/voice/${encodeURIComponent(path.slice('Audio/voice/'.length))}`;
|
source = `/media/audio/voice/${encodeURIComponent(path.slice('Audio/voice/'.length))}`;
|
||||||
|
} else if (path.startsWith('Audio/beep/')) {
|
||||||
|
source = `/media/audio/beep/${encodeURIComponent(path.slice('Audio/beep/'.length))}`;
|
||||||
} else if (path.startsWith('Audio/general/')) {
|
} else if (path.startsWith('Audio/general/')) {
|
||||||
source = `/media/audio/general/${encodeURIComponent(path.slice('Audio/general/'.length))}`;
|
source = `/media/audio/general/${encodeURIComponent(path.slice('Audio/general/'.length))}`;
|
||||||
} else if (path.startsWith('Audio/station/')) {
|
} else if (path.startsWith('Audio/station/')) {
|
||||||
|
|||||||
@@ -206,7 +206,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="hint" id="hint_area">
|
<div class="hint" id="hint_area">
|
||||||
需要服务端已存在 lsts.wav(建议放在 /opt/CC-StationBroadcast/Audio/general/lsts.wav)。点击“合成”会自动中文 TTS 并拼接生成临时广播,随后可发布到所选站台。
|
需要服务端已存在 lsts.wav(建议放在 /opt/CC-StationBroadcast/Audio/beep/lsts.wav)。点击“合成”会自动中文 TTS 并拼接生成临时广播,随后可发布到所选站台。
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="preview_area" style="margin-top:12px; display:none;">
|
<div id="preview_area" style="margin-top:12px; display:none;">
|
||||||
|
|||||||
+57
-20
@@ -43,7 +43,8 @@ const uploadDir = path.join(__dirname, 'public', 'uploads');
|
|||||||
const audioRootDir = path.join(appRootDir, 'Audio');
|
const audioRootDir = path.join(appRootDir, 'Audio');
|
||||||
const audioMusicDir = path.join(audioRootDir, 'music');
|
const audioMusicDir = path.join(audioRootDir, 'music');
|
||||||
const audioVoiceDir = path.join(audioRootDir, 'voice');
|
const audioVoiceDir = path.join(audioRootDir, 'voice');
|
||||||
const audioGeneralDir = path.join(audioRootDir, 'general');
|
const audioBeepDir = path.join(audioRootDir, 'beep');
|
||||||
|
const audioGeneralLegacyDir = path.join(audioRootDir, 'general');
|
||||||
const audioStationDir = path.join(audioRootDir, 'station');
|
const audioStationDir = path.join(audioRootDir, 'station');
|
||||||
const listFilePath = path.join(appRootDir, 'list.txt');
|
const listFilePath = path.join(appRootDir, 'list.txt');
|
||||||
const tempBroadcastDir = path.join(appRootDir, 'temp-broadcast');
|
const tempBroadcastDir = path.join(appRootDir, 'temp-broadcast');
|
||||||
@@ -54,10 +55,27 @@ if (!fs.existsSync(uploadDir)) fs.mkdirSync(uploadDir, { recursive: true });
|
|||||||
if (!fs.existsSync(audioRootDir)) fs.mkdirSync(audioRootDir, { recursive: true });
|
if (!fs.existsSync(audioRootDir)) fs.mkdirSync(audioRootDir, { recursive: true });
|
||||||
if (!fs.existsSync(audioMusicDir)) fs.mkdirSync(audioMusicDir, { recursive: true });
|
if (!fs.existsSync(audioMusicDir)) fs.mkdirSync(audioMusicDir, { recursive: true });
|
||||||
if (!fs.existsSync(audioVoiceDir)) fs.mkdirSync(audioVoiceDir, { recursive: true });
|
if (!fs.existsSync(audioVoiceDir)) fs.mkdirSync(audioVoiceDir, { recursive: true });
|
||||||
if (!fs.existsSync(audioGeneralDir)) fs.mkdirSync(audioGeneralDir, { recursive: true });
|
if (!fs.existsSync(audioBeepDir)) fs.mkdirSync(audioBeepDir, { recursive: true });
|
||||||
if (!fs.existsSync(audioStationDir)) fs.mkdirSync(audioStationDir, { recursive: true });
|
if (!fs.existsSync(audioStationDir)) fs.mkdirSync(audioStationDir, { recursive: true });
|
||||||
if (!fs.existsSync(tempBroadcastDir)) fs.mkdirSync(tempBroadcastDir, { recursive: true });
|
if (!fs.existsSync(tempBroadcastDir)) fs.mkdirSync(tempBroadcastDir, { recursive: true });
|
||||||
|
|
||||||
|
function getBeepAudioDirs() {
|
||||||
|
const dirs = [audioBeepDir];
|
||||||
|
if (audioGeneralLegacyDir !== audioBeepDir && fs.existsSync(audioGeneralLegacyDir)) {
|
||||||
|
dirs.push(audioGeneralLegacyDir);
|
||||||
|
}
|
||||||
|
return dirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendBinaryFileFromCandidates(res, candidates) {
|
||||||
|
for (const filePath of candidates) {
|
||||||
|
if (fs.existsSync(filePath)) {
|
||||||
|
return res.sendFile(filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res.status(404).send('Not found');
|
||||||
|
}
|
||||||
|
|
||||||
// Multer storage configuration
|
// Multer storage configuration
|
||||||
const storage = multer.diskStorage({
|
const storage = multer.diskStorage({
|
||||||
destination: function (req, file, cb) {
|
destination: function (req, file, cb) {
|
||||||
@@ -68,7 +86,7 @@ const storage = multer.diskStorage({
|
|||||||
} else if (file.fieldname === 'voiceAudioFiles') {
|
} else if (file.fieldname === 'voiceAudioFiles') {
|
||||||
cb(null, audioVoiceDir);
|
cb(null, audioVoiceDir);
|
||||||
} else if (file.fieldname === 'generalAudioFiles') {
|
} else if (file.fieldname === 'generalAudioFiles') {
|
||||||
cb(null, audioGeneralDir);
|
cb(null, audioBeepDir);
|
||||||
} else if (file.fieldname === 'stationAudioFiles') {
|
} else if (file.fieldname === 'stationAudioFiles') {
|
||||||
cb(null, audioStationDir);
|
cb(null, audioStationDir);
|
||||||
} else {
|
} else {
|
||||||
@@ -114,7 +132,11 @@ function sendBinaryFile(res, filePath) {
|
|||||||
|
|
||||||
app.get('/media/audio/general/:file', (req, res) => {
|
app.get('/media/audio/general/:file', (req, res) => {
|
||||||
const name = path.basename(String(req.params.file || ''));
|
const name = path.basename(String(req.params.file || ''));
|
||||||
sendBinaryFile(res, path.join(audioGeneralDir, name));
|
sendBinaryFileFromCandidates(res, getBeepAudioDirs().map(dir => path.join(dir, name)));
|
||||||
|
});
|
||||||
|
app.get('/media/audio/beep/:file', (req, res) => {
|
||||||
|
const name = path.basename(String(req.params.file || ''));
|
||||||
|
sendBinaryFileFromCandidates(res, getBeepAudioDirs().map(dir => path.join(dir, name)));
|
||||||
});
|
});
|
||||||
app.get('/media/audio/music/:file', (req, res) => {
|
app.get('/media/audio/music/:file', (req, res) => {
|
||||||
const name = path.basename(String(req.params.file || ''));
|
const name = path.basename(String(req.params.file || ''));
|
||||||
@@ -142,6 +164,7 @@ app.get('/media', (req, res) => {
|
|||||||
routes: [
|
routes: [
|
||||||
'/media/audio/music/:file',
|
'/media/audio/music/:file',
|
||||||
'/media/audio/voice/:file',
|
'/media/audio/voice/:file',
|
||||||
|
'/media/audio/beep/:file',
|
||||||
'/media/audio/general/:file',
|
'/media/audio/general/:file',
|
||||||
'/media/audio/station/:file',
|
'/media/audio/station/:file',
|
||||||
'/media/temp/:file'
|
'/media/temp/:file'
|
||||||
@@ -585,7 +608,7 @@ function listMusicTracks() {
|
|||||||
const out = new Set();
|
const out = new Set();
|
||||||
const scanDirs = [audioMusicDir];
|
const scanDirs = [audioMusicDir];
|
||||||
if (fs.existsSync(audioMusicDir) && fs.readdirSync(audioMusicDir).length === 0) {
|
if (fs.existsSync(audioMusicDir) && fs.readdirSync(audioMusicDir).length === 0) {
|
||||||
scanDirs.push(audioGeneralDir);
|
scanDirs.push(...getBeepAudioDirs());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const dir of scanDirs) {
|
for (const dir of scanDirs) {
|
||||||
@@ -642,7 +665,7 @@ function getPlaybackAudioByBaseName(baseName) {
|
|||||||
for (const ext of STREAMABLE_AUDIO_EXTENSIONS) {
|
for (const ext of STREAMABLE_AUDIO_EXTENSIONS) {
|
||||||
const candidates = [
|
const candidates = [
|
||||||
path.join(audioMusicDir, `${base}${ext}`),
|
path.join(audioMusicDir, `${base}${ext}`),
|
||||||
path.join(audioGeneralDir, `${base}${ext}`),
|
...getBeepAudioDirs().map(dir => path.join(dir, `${base}${ext}`)),
|
||||||
path.join(audioStationDir, `${base}${ext}`)
|
path.join(audioStationDir, `${base}${ext}`)
|
||||||
];
|
];
|
||||||
for (const p of candidates) {
|
for (const p of candidates) {
|
||||||
@@ -657,7 +680,7 @@ function getPlaybackAudioByBaseName(baseName) {
|
|||||||
audioFormat: 'stream',
|
audioFormat: 'stream',
|
||||||
streamUrl: getManagedMediaUrl(
|
streamUrl: getManagedMediaUrl(
|
||||||
p.startsWith(audioStationDir) ? 'audio/station' :
|
p.startsWith(audioStationDir) ? 'audio/station' :
|
||||||
(p.startsWith(audioMusicDir) ? 'audio/music' : 'audio/general'),
|
(p.startsWith(audioMusicDir) ? 'audio/music' : 'audio/beep'),
|
||||||
path.basename(p)
|
path.basename(p)
|
||||||
),
|
),
|
||||||
durationSec
|
durationSec
|
||||||
@@ -667,7 +690,7 @@ function getPlaybackAudioByBaseName(baseName) {
|
|||||||
|
|
||||||
for (const p of [
|
for (const p of [
|
||||||
path.join(audioMusicDir, `${base}.dfpwm`),
|
path.join(audioMusicDir, `${base}.dfpwm`),
|
||||||
path.join(audioGeneralDir, `${base}.dfpwm`),
|
...getBeepAudioDirs().map(dir => path.join(dir, `${base}.dfpwm`)),
|
||||||
path.join(audioStationDir, `${base}.dfpwm`)
|
path.join(audioStationDir, `${base}.dfpwm`)
|
||||||
]) {
|
]) {
|
||||||
if (!fs.existsSync(p)) continue;
|
if (!fs.existsSync(p)) continue;
|
||||||
@@ -838,17 +861,27 @@ function resolveManagedPath(inputPath) {
|
|||||||
return { logicalPath: 'list.txt', fullPath: listFilePath };
|
return { logicalPath: 'list.txt', fullPath: listFilePath };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (normalized === 'Audio/general/line.txt' || normalized === 'uploads/audio/general/line.txt') {
|
if (
|
||||||
return { logicalPath: 'Audio/general/line.txt', fullPath: path.join(audioGeneralDir, 'line.txt') };
|
normalized === 'Audio/beep/line.txt' ||
|
||||||
|
normalized === 'uploads/audio/beep/line.txt' ||
|
||||||
|
normalized === 'Audio/general/line.txt' ||
|
||||||
|
normalized === 'uploads/audio/general/line.txt'
|
||||||
|
) {
|
||||||
|
const fullPath = fs.existsSync(path.join(audioBeepDir, 'line.txt'))
|
||||||
|
? path.join(audioBeepDir, 'line.txt')
|
||||||
|
: path.join(audioGeneralLegacyDir, 'line.txt');
|
||||||
|
return { logicalPath: 'Audio/beep/line.txt', fullPath };
|
||||||
}
|
}
|
||||||
|
|
||||||
const prefixes = [
|
const prefixes = [
|
||||||
{ prefix: 'Audio/music/', baseDir: audioMusicDir, logicalPrefix: 'Audio/music/' },
|
{ prefix: 'Audio/music/', baseDir: audioMusicDir, logicalPrefix: 'Audio/music/' },
|
||||||
{ prefix: 'Audio/voice/', baseDir: audioVoiceDir, logicalPrefix: 'Audio/voice/' },
|
{ prefix: 'Audio/voice/', baseDir: audioVoiceDir, logicalPrefix: 'Audio/voice/' },
|
||||||
{ prefix: 'Audio/general/', baseDir: audioGeneralDir, logicalPrefix: 'Audio/general/' },
|
{ prefix: 'Audio/beep/', baseDir: audioBeepDir, logicalPrefix: 'Audio/beep/' },
|
||||||
|
{ prefix: 'Audio/general/', baseDir: audioGeneralLegacyDir, logicalPrefix: 'Audio/beep/' },
|
||||||
{ prefix: 'uploads/audio/music/', baseDir: audioMusicDir, logicalPrefix: 'Audio/music/' },
|
{ prefix: 'uploads/audio/music/', baseDir: audioMusicDir, logicalPrefix: 'Audio/music/' },
|
||||||
{ prefix: 'uploads/audio/voice/', baseDir: audioVoiceDir, logicalPrefix: 'Audio/voice/' },
|
{ prefix: 'uploads/audio/voice/', baseDir: audioVoiceDir, logicalPrefix: 'Audio/voice/' },
|
||||||
{ prefix: 'uploads/audio/general/', baseDir: audioGeneralDir, logicalPrefix: 'Audio/general/' },
|
{ prefix: 'uploads/audio/beep/', baseDir: audioBeepDir, logicalPrefix: 'Audio/beep/' },
|
||||||
|
{ prefix: 'uploads/audio/general/', baseDir: audioGeneralLegacyDir, logicalPrefix: 'Audio/beep/' },
|
||||||
{ prefix: 'Audio/station/', baseDir: audioStationDir, logicalPrefix: 'Audio/station/' },
|
{ prefix: 'Audio/station/', baseDir: audioStationDir, logicalPrefix: 'Audio/station/' },
|
||||||
{ prefix: 'uploads/audio/station/', baseDir: audioStationDir, logicalPrefix: 'Audio/station/' }
|
{ prefix: 'uploads/audio/station/', baseDir: audioStationDir, logicalPrefix: 'Audio/station/' }
|
||||||
];
|
];
|
||||||
@@ -916,17 +949,21 @@ function getManagedFiles() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fs.existsSync(audioGeneralDir)) {
|
const beepSeen = new Set();
|
||||||
fs.readdirSync(audioGeneralDir).forEach(file => {
|
for (const dir of getBeepAudioDirs()) {
|
||||||
|
if (!fs.existsSync(dir)) continue;
|
||||||
|
fs.readdirSync(dir).forEach(file => {
|
||||||
|
if (beepSeen.has(file)) return;
|
||||||
|
beepSeen.add(file);
|
||||||
if (file.endsWith('.dfpwm')) {
|
if (file.endsWith('.dfpwm')) {
|
||||||
const wavName = file.replace(/\.dfpwm$/, '.wav');
|
const wavName = file.replace(/\.dfpwm$/, '.wav');
|
||||||
if (fs.existsSync(path.join(audioGeneralDir, wavName))) return;
|
if (beepSeen.has(wavName) || fs.existsSync(path.join(dir, wavName))) return;
|
||||||
}
|
}
|
||||||
const filePath = path.join(audioGeneralDir, file);
|
const filePath = path.join(dir, file);
|
||||||
const stats = fs.statSync(filePath);
|
const stats = fs.statSync(filePath);
|
||||||
files.push({
|
files.push({
|
||||||
name: file,
|
name: file,
|
||||||
path: `Audio/general/${file}`,
|
path: `Audio/beep/${file}`,
|
||||||
type: file === 'line.txt' ? 'config' : 'beep',
|
type: file === 'line.txt' ? 'config' : 'beep',
|
||||||
size: stats.size,
|
size: stats.size,
|
||||||
mtime: stats.mtimeMs
|
mtime: stats.mtimeMs
|
||||||
@@ -1183,7 +1220,7 @@ function buildConcatListFile(seq) {
|
|||||||
|
|
||||||
const candidates = [
|
const candidates = [
|
||||||
path.join(audioStationDir, `${item}.wav`),
|
path.join(audioStationDir, `${item}.wav`),
|
||||||
path.join(audioGeneralDir, `${item}.wav`)
|
...getBeepAudioDirs().map(dir => path.join(dir, `${item}.wav`))
|
||||||
];
|
];
|
||||||
|
|
||||||
const found = candidates.find(p => fs.existsSync(p));
|
const found = candidates.find(p => fs.existsSync(p));
|
||||||
@@ -2527,7 +2564,7 @@ app.post('/api/tts/import', express.json(), (req, res) => {
|
|||||||
const jaSource = path.join(uploadDir, jaTemp);
|
const jaSource = path.join(uploadDir, jaTemp);
|
||||||
const safeStationCode = String(stationCode).replace(/\//g, '-');
|
const safeStationCode = String(stationCode).replace(/\//g, '-');
|
||||||
|
|
||||||
const targetDir = type === 'line' ? audioGeneralDir : audioStationDir;
|
const targetDir = type === 'line' ? audioBeepDir : audioStationDir;
|
||||||
const zhDest = path.join(targetDir, `ch-${safeStationCode}.wav`);
|
const zhDest = path.join(targetDir, `ch-${safeStationCode}.wav`);
|
||||||
const jaDest = path.join(targetDir, `ja-${safeStationCode}.wav`);
|
const jaDest = path.join(targetDir, `ja-${safeStationCode}.wav`);
|
||||||
|
|
||||||
@@ -2558,7 +2595,7 @@ function findWavByBaseName(baseName) {
|
|||||||
const safeBase = String(baseName || '').trim().replace(/\.wav$/i, '');
|
const safeBase = String(baseName || '').trim().replace(/\.wav$/i, '');
|
||||||
if (!safeBase) return null;
|
if (!safeBase) return null;
|
||||||
const candidates = [
|
const candidates = [
|
||||||
path.join(audioGeneralDir, `${safeBase}.wav`),
|
...getBeepAudioDirs().map(dir => path.join(dir, `${safeBase}.wav`)),
|
||||||
path.join(audioStationDir, `${safeBase}.wav`),
|
path.join(audioStationDir, `${safeBase}.wav`),
|
||||||
path.join(uploadDir, `${safeBase}.wav`)
|
path.join(uploadDir, `${safeBase}.wav`)
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user