diff --git a/CloudServer/public/index.html b/CloudServer/public/index.html
index b271f12..38d1875 100644
--- a/CloudServer/public/index.html
+++ b/CloudServer/public/index.html
@@ -813,6 +813,8 @@
+
f.type === 'config').length },
{ id: 'music', name: 'Music', count: allFiles.filter(f => f.type === 'music').length },
+ { id: 'voice', name: 'Voice', count: allFiles.filter(f => f.type === 'voice').length },
{ id: 'beep', name: 'Beep', count: allFiles.filter(f => f.type === 'beep').length },
{ id: 'station', name: 'Station', count: allFiles.filter(f => f.type === 'station').length }
];
@@ -1187,6 +1191,7 @@
const typeLabelMap = {
config: 'Configuration',
music: 'Music',
+ voice: 'Voice',
beep: 'Beep',
station: 'Station'
};
@@ -1264,6 +1269,7 @@
function triggerUpload() {
if (currentPath === 'config') document.getElementById('upload-config').click();
else if (currentPath === 'music') document.getElementById('upload-music').click();
+ else if (currentPath === 'voice') document.getElementById('upload-voice').click();
else if (currentPath === 'beep') document.getElementById('upload-general').click();
else if (currentPath === 'station') document.getElementById('upload-station').click();
}
@@ -1366,6 +1372,8 @@
let source = '/' + path;
if (path.startsWith('Audio/music/')) {
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/general/')) {
source = `/media/audio/general/${encodeURIComponent(path.slice('Audio/general/'.length))}`;
} else if (path.startsWith('Audio/station/')) {
diff --git a/CloudServer/server.js b/CloudServer/server.js
index 3b7aaac..3044a3c 100644
--- a/CloudServer/server.js
+++ b/CloudServer/server.js
@@ -40,6 +40,7 @@ if (!fs.existsSync(localHlsDir)) fs.mkdirSync(localHlsDir, { recursive: true });
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 audioStationDir = path.join(audioRootDir, 'station');
const listFilePath = path.join(appRootDir, 'list.txt');
@@ -50,6 +51,7 @@ const MUSIC_AUDIO_EXTENSIONS = [...STREAMABLE_AUDIO_EXTENSIONS, '.dfpwm'];
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(audioStationDir)) fs.mkdirSync(audioStationDir, { recursive: true });
if (!fs.existsSync(tempBroadcastDir)) fs.mkdirSync(tempBroadcastDir, { recursive: true });
@@ -61,6 +63,8 @@ const storage = multer.diskStorage({
cb(null, appRootDir);
} else if (file.fieldname === 'musicAudioFiles') {
cb(null, audioMusicDir);
+ } else if (file.fieldname === 'voiceAudioFiles') {
+ cb(null, audioVoiceDir);
} else if (file.fieldname === 'generalAudioFiles') {
cb(null, audioGeneralDir);
} else if (file.fieldname === 'stationAudioFiles') {
@@ -114,6 +118,10 @@ app.get('/media/audio/music/:file', (req, res) => {
const name = path.basename(String(req.params.file || ''));
sendBinaryFile(res, path.join(audioMusicDir, name));
});
+app.get('/media/audio/voice/:file', (req, res) => {
+ const name = path.basename(String(req.params.file || ''));
+ sendBinaryFile(res, path.join(audioVoiceDir, name));
+});
app.get('/media/audio/station/:file', (req, res) => {
const name = path.basename(String(req.params.file || ''));
@@ -131,6 +139,7 @@ app.get('/media', (req, res) => {
message: 'Use a concrete media path',
routes: [
'/media/audio/music/:file',
+ '/media/audio/voice/:file',
'/media/audio/general/:file',
'/media/audio/station/:file',
'/media/temp/:file'
@@ -747,8 +756,10 @@ function resolveManagedPath(inputPath) {
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: '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: 'Audio/station/', baseDir: audioStationDir, logicalPrefix: 'Audio/station/' },
{ prefix: 'uploads/audio/station/', baseDir: audioStationDir, logicalPrefix: 'Audio/station/' }
@@ -799,6 +810,24 @@ function getManagedFiles() {
});
}
+ if (fs.existsSync(audioVoiceDir)) {
+ fs.readdirSync(audioVoiceDir).forEach(file => {
+ if (file.endsWith('.dfpwm')) {
+ const wavName = file.replace(/\.dfpwm$/, '.wav');
+ if (fs.existsSync(path.join(audioVoiceDir, wavName))) return;
+ }
+ const filePath = path.join(audioVoiceDir, file);
+ const stats = fs.statSync(filePath);
+ files.push({
+ name: file,
+ path: `Audio/voice/${file}`,
+ type: 'voice',
+ size: stats.size,
+ mtime: stats.mtimeMs
+ });
+ });
+ }
+
if (fs.existsSync(audioGeneralDir)) {
fs.readdirSync(audioGeneralDir).forEach(file => {
if (file.endsWith('.dfpwm')) {
@@ -1898,6 +1927,12 @@ app.post('/api/upload/music-audio', upload.array('musicAudioFiles'), (req, res)
res.json({ success: true, message: `${files.length} music audio files uploaded` });
});
+app.post('/api/upload/voice-audio', upload.array('voiceAudioFiles'), (req, res) => {
+ const files = req.files || [];
+ log('SUCCESS', `Uploaded ${files.length} voice audio files`);
+ res.json({ success: true, message: `${files.length} voice audio files uploaded` });
+});
+
app.post('/api/upload/station-audio', upload.array('stationAudioFiles'), (req, res) => {
const files = req.files || [];
log('SUCCESS', `Uploaded ${files.length} station audio files`);