feat(console): add voice folder to file manager
Add an Audio/voice directory with upload and preview support, and expose it in the console file manager so the structure now shows Configuration, Music, Voice, Beep, and Station.
This commit is contained in:
@@ -813,6 +813,8 @@
|
|||||||
<input type="file" id="upload-config" accept=".txt" onchange="performUpload('listFile', '/api/upload/list', this)">
|
<input type="file" id="upload-config" accept=".txt" onchange="performUpload('listFile', '/api/upload/list', this)">
|
||||||
<input type="file" id="upload-music" multiple accept=".wav,.mp3,.ogg,.flac,.m4a,.aac,.opus,.dfpwm"
|
<input type="file" id="upload-music" multiple accept=".wav,.mp3,.ogg,.flac,.m4a,.aac,.opus,.dfpwm"
|
||||||
onchange="performUpload('musicAudioFiles', '/api/upload/music-audio', this)">
|
onchange="performUpload('musicAudioFiles', '/api/upload/music-audio', this)">
|
||||||
|
<input type="file" id="upload-voice" multiple accept=".wav,.mp3,.ogg,.flac,.m4a,.aac,.opus,.dfpwm"
|
||||||
|
onchange="performUpload('voiceAudioFiles', '/api/upload/voice-audio', this)">
|
||||||
<input type="file" id="upload-general" multiple accept=".wav,.mp3,.ogg,.flac,.m4a,.aac,.opus,.dfpwm"
|
<input type="file" id="upload-general" multiple accept=".wav,.mp3,.ogg,.flac,.m4a,.aac,.opus,.dfpwm"
|
||||||
onchange="performUpload('generalAudioFiles', '/api/upload/general-audio', this)">
|
onchange="performUpload('generalAudioFiles', '/api/upload/general-audio', this)">
|
||||||
<input type="file" id="upload-station" multiple accept=".wav,.mp3,.ogg,.flac,.m4a,.aac,.opus,.dfpwm"
|
<input type="file" id="upload-station" multiple accept=".wav,.mp3,.ogg,.flac,.m4a,.aac,.opus,.dfpwm"
|
||||||
@@ -1143,6 +1145,7 @@
|
|||||||
const nameMap = {
|
const nameMap = {
|
||||||
config: 'Configuration',
|
config: 'Configuration',
|
||||||
music: 'Music',
|
music: 'Music',
|
||||||
|
voice: 'Voice',
|
||||||
beep: 'Beep',
|
beep: 'Beep',
|
||||||
station: 'Station'
|
station: 'Station'
|
||||||
};
|
};
|
||||||
@@ -1160,6 +1163,7 @@
|
|||||||
const folders = [
|
const folders = [
|
||||||
{ id: 'config', name: 'Configuration', count: allFiles.filter(f => f.type === 'config').length },
|
{ id: 'config', name: 'Configuration', count: allFiles.filter(f => f.type === 'config').length },
|
||||||
{ id: 'music', name: 'Music', count: allFiles.filter(f => f.type === 'music').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: 'beep', name: 'Beep', count: allFiles.filter(f => f.type === 'beep').length },
|
||||||
{ id: 'station', name: 'Station', count: allFiles.filter(f => f.type === 'station').length }
|
{ id: 'station', name: 'Station', count: allFiles.filter(f => f.type === 'station').length }
|
||||||
];
|
];
|
||||||
@@ -1187,6 +1191,7 @@
|
|||||||
const typeLabelMap = {
|
const typeLabelMap = {
|
||||||
config: 'Configuration',
|
config: 'Configuration',
|
||||||
music: 'Music',
|
music: 'Music',
|
||||||
|
voice: 'Voice',
|
||||||
beep: 'Beep',
|
beep: 'Beep',
|
||||||
station: 'Station'
|
station: 'Station'
|
||||||
};
|
};
|
||||||
@@ -1264,6 +1269,7 @@
|
|||||||
function triggerUpload() {
|
function triggerUpload() {
|
||||||
if (currentPath === 'config') document.getElementById('upload-config').click();
|
if (currentPath === 'config') document.getElementById('upload-config').click();
|
||||||
else if (currentPath === 'music') document.getElementById('upload-music').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 === 'beep') document.getElementById('upload-general').click();
|
||||||
else if (currentPath === 'station') document.getElementById('upload-station').click();
|
else if (currentPath === 'station') document.getElementById('upload-station').click();
|
||||||
}
|
}
|
||||||
@@ -1366,6 +1372,8 @@
|
|||||||
let source = '/' + path;
|
let source = '/' + path;
|
||||||
if (path.startsWith('Audio/music/')) {
|
if (path.startsWith('Audio/music/')) {
|
||||||
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/')) {
|
||||||
|
source = `/media/audio/voice/${encodeURIComponent(path.slice('Audio/voice/'.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/')) {
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ if (!fs.existsSync(localHlsDir)) fs.mkdirSync(localHlsDir, { recursive: true });
|
|||||||
const uploadDir = path.join(__dirname, 'public', 'uploads');
|
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 audioGeneralDir = path.join(audioRootDir, 'general');
|
const audioGeneralDir = 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');
|
||||||
@@ -50,6 +51,7 @@ const MUSIC_AUDIO_EXTENSIONS = [...STREAMABLE_AUDIO_EXTENSIONS, '.dfpwm'];
|
|||||||
if (!fs.existsSync(uploadDir)) fs.mkdirSync(uploadDir, { recursive: true });
|
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(audioGeneralDir)) fs.mkdirSync(audioGeneralDir, { recursive: true });
|
if (!fs.existsSync(audioGeneralDir)) fs.mkdirSync(audioGeneralDir, { 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 });
|
||||||
@@ -61,6 +63,8 @@ const storage = multer.diskStorage({
|
|||||||
cb(null, appRootDir);
|
cb(null, appRootDir);
|
||||||
} else if (file.fieldname === 'musicAudioFiles') {
|
} else if (file.fieldname === 'musicAudioFiles') {
|
||||||
cb(null, audioMusicDir);
|
cb(null, audioMusicDir);
|
||||||
|
} else if (file.fieldname === 'voiceAudioFiles') {
|
||||||
|
cb(null, audioVoiceDir);
|
||||||
} else if (file.fieldname === 'generalAudioFiles') {
|
} else if (file.fieldname === 'generalAudioFiles') {
|
||||||
cb(null, audioGeneralDir);
|
cb(null, audioGeneralDir);
|
||||||
} else if (file.fieldname === 'stationAudioFiles') {
|
} 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 || ''));
|
const name = path.basename(String(req.params.file || ''));
|
||||||
sendBinaryFile(res, path.join(audioMusicDir, name));
|
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) => {
|
app.get('/media/audio/station/:file', (req, res) => {
|
||||||
const name = path.basename(String(req.params.file || ''));
|
const name = path.basename(String(req.params.file || ''));
|
||||||
@@ -131,6 +139,7 @@ app.get('/media', (req, res) => {
|
|||||||
message: 'Use a concrete media path',
|
message: 'Use a concrete media path',
|
||||||
routes: [
|
routes: [
|
||||||
'/media/audio/music/:file',
|
'/media/audio/music/:file',
|
||||||
|
'/media/audio/voice/:file',
|
||||||
'/media/audio/general/:file',
|
'/media/audio/general/:file',
|
||||||
'/media/audio/station/:file',
|
'/media/audio/station/:file',
|
||||||
'/media/temp/:file'
|
'/media/temp/:file'
|
||||||
@@ -747,8 +756,10 @@ function resolveManagedPath(inputPath) {
|
|||||||
|
|
||||||
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/general/', baseDir: audioGeneralDir, logicalPrefix: 'Audio/general/' },
|
{ prefix: 'Audio/general/', baseDir: audioGeneralDir, logicalPrefix: 'Audio/general/' },
|
||||||
{ 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/general/', baseDir: audioGeneralDir, logicalPrefix: 'Audio/general/' },
|
{ prefix: 'uploads/audio/general/', baseDir: audioGeneralDir, logicalPrefix: 'Audio/general/' },
|
||||||
{ 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/' }
|
||||||
@@ -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)) {
|
if (fs.existsSync(audioGeneralDir)) {
|
||||||
fs.readdirSync(audioGeneralDir).forEach(file => {
|
fs.readdirSync(audioGeneralDir).forEach(file => {
|
||||||
if (file.endsWith('.dfpwm')) {
|
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` });
|
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) => {
|
app.post('/api/upload/station-audio', upload.array('stationAudioFiles'), (req, res) => {
|
||||||
const files = req.files || [];
|
const files = req.files || [];
|
||||||
log('SUCCESS', `Uploaded ${files.length} station audio files`);
|
log('SUCCESS', `Uploaded ${files.length} station audio files`);
|
||||||
|
|||||||
Reference in New Issue
Block a user