From c0f100a8337191ee5c779d0cafb41577de5c4245 Mon Sep 17 00:00:00 2001
From: HenryDu8133 <813367384@qq.com>
Date: Sat, 1 Aug 2026 11:42:38 +0800
Subject: [PATCH] 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.
---
CloudServer/public/index.html | 2 +
CloudServer/public/tempbroadcast.html | 2 +-
CloudServer/server.js | 77 ++++++++++++++++++++-------
3 files changed, 60 insertions(+), 21 deletions(-)
diff --git a/CloudServer/public/index.html b/CloudServer/public/index.html
index 010377d..2155812 100644
--- a/CloudServer/public/index.html
+++ b/CloudServer/public/index.html
@@ -1585,6 +1585,8 @@
source = `/media/audio/music/${encodeURIComponent(path.slice('Audio/music/'.length))}`;
} else if (path.startsWith('Audio/voice/')) {
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/')) {
source = `/media/audio/general/${encodeURIComponent(path.slice('Audio/general/'.length))}`;
} else if (path.startsWith('Audio/station/')) {
diff --git a/CloudServer/public/tempbroadcast.html b/CloudServer/public/tempbroadcast.html
index cc191fc..5347e58 100644
--- a/CloudServer/public/tempbroadcast.html
+++ b/CloudServer/public/tempbroadcast.html
@@ -206,7 +206,7 @@
- 需要服务端已存在 lsts.wav(建议放在 /opt/CC-StationBroadcast/Audio/general/lsts.wav)。点击“合成”会自动中文 TTS 并拼接生成临时广播,随后可发布到所选站台。
+ 需要服务端已存在 lsts.wav(建议放在 /opt/CC-StationBroadcast/Audio/beep/lsts.wav)。点击“合成”会自动中文 TTS 并拼接生成临时广播,随后可发布到所选站台。
diff --git a/CloudServer/server.js b/CloudServer/server.js
index f12f792..086dad9 100644
--- a/CloudServer/server.js
+++ b/CloudServer/server.js
@@ -43,7 +43,8 @@ const uploadDir = path.join(__dirname, 'public', 'uploads');
const audioRootDir = path.join(appRootDir, 'Audio');
const audioMusicDir = path.join(audioRootDir, 'music');
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 listFilePath = path.join(appRootDir, 'list.txt');
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(audioMusicDir)) fs.mkdirSync(audioMusicDir, { 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(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
const storage = multer.diskStorage({
destination: function (req, file, cb) {
@@ -68,7 +86,7 @@ const storage = multer.diskStorage({
} else if (file.fieldname === 'voiceAudioFiles') {
cb(null, audioVoiceDir);
} else if (file.fieldname === 'generalAudioFiles') {
- cb(null, audioGeneralDir);
+ cb(null, audioBeepDir);
} else if (file.fieldname === 'stationAudioFiles') {
cb(null, audioStationDir);
} else {
@@ -114,7 +132,11 @@ function sendBinaryFile(res, filePath) {
app.get('/media/audio/general/:file', (req, res) => {
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) => {
const name = path.basename(String(req.params.file || ''));
@@ -142,6 +164,7 @@ app.get('/media', (req, res) => {
routes: [
'/media/audio/music/:file',
'/media/audio/voice/:file',
+ '/media/audio/beep/:file',
'/media/audio/general/:file',
'/media/audio/station/:file',
'/media/temp/:file'
@@ -585,7 +608,7 @@ function listMusicTracks() {
const out = new Set();
const scanDirs = [audioMusicDir];
if (fs.existsSync(audioMusicDir) && fs.readdirSync(audioMusicDir).length === 0) {
- scanDirs.push(audioGeneralDir);
+ scanDirs.push(...getBeepAudioDirs());
}
for (const dir of scanDirs) {
@@ -642,7 +665,7 @@ function getPlaybackAudioByBaseName(baseName) {
for (const ext of STREAMABLE_AUDIO_EXTENSIONS) {
const candidates = [
path.join(audioMusicDir, `${base}${ext}`),
- path.join(audioGeneralDir, `${base}${ext}`),
+ ...getBeepAudioDirs().map(dir => path.join(dir, `${base}${ext}`)),
path.join(audioStationDir, `${base}${ext}`)
];
for (const p of candidates) {
@@ -657,7 +680,7 @@ function getPlaybackAudioByBaseName(baseName) {
audioFormat: 'stream',
streamUrl: getManagedMediaUrl(
p.startsWith(audioStationDir) ? 'audio/station' :
- (p.startsWith(audioMusicDir) ? 'audio/music' : 'audio/general'),
+ (p.startsWith(audioMusicDir) ? 'audio/music' : 'audio/beep'),
path.basename(p)
),
durationSec
@@ -667,7 +690,7 @@ function getPlaybackAudioByBaseName(baseName) {
for (const p of [
path.join(audioMusicDir, `${base}.dfpwm`),
- path.join(audioGeneralDir, `${base}.dfpwm`),
+ ...getBeepAudioDirs().map(dir => path.join(dir, `${base}.dfpwm`)),
path.join(audioStationDir, `${base}.dfpwm`)
]) {
if (!fs.existsSync(p)) continue;
@@ -838,17 +861,27 @@ function resolveManagedPath(inputPath) {
return { logicalPath: 'list.txt', fullPath: listFilePath };
}
- if (normalized === 'Audio/general/line.txt' || normalized === 'uploads/audio/general/line.txt') {
- return { logicalPath: 'Audio/general/line.txt', fullPath: path.join(audioGeneralDir, 'line.txt') };
+ if (
+ 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 = [
{ prefix: 'Audio/music/', baseDir: audioMusicDir, logicalPrefix: 'Audio/music/' },
{ 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/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: 'uploads/audio/station/', baseDir: audioStationDir, logicalPrefix: 'Audio/station/' }
];
@@ -916,17 +949,21 @@ function getManagedFiles() {
});
}
- if (fs.existsSync(audioGeneralDir)) {
- fs.readdirSync(audioGeneralDir).forEach(file => {
+ const beepSeen = new Set();
+ 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')) {
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);
files.push({
name: file,
- path: `Audio/general/${file}`,
+ path: `Audio/beep/${file}`,
type: file === 'line.txt' ? 'config' : 'beep',
size: stats.size,
mtime: stats.mtimeMs
@@ -1183,7 +1220,7 @@ function buildConcatListFile(seq) {
const candidates = [
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));
@@ -2527,7 +2564,7 @@ app.post('/api/tts/import', express.json(), (req, res) => {
const jaSource = path.join(uploadDir, jaTemp);
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 jaDest = path.join(targetDir, `ja-${safeStationCode}.wav`);
@@ -2558,7 +2595,7 @@ function findWavByBaseName(baseName) {
const safeBase = String(baseName || '').trim().replace(/\.wav$/i, '');
if (!safeBase) return null;
const candidates = [
- path.join(audioGeneralDir, `${safeBase}.wav`),
+ ...getBeepAudioDirs().map(dir => path.join(dir, `${safeBase}.wav`)),
path.join(audioStationDir, `${safeBase}.wav`),
path.join(uploadDir, `${safeBase}.wav`)
];