diff --git a/CloudServer/public/index.html b/CloudServer/public/index.html
index 2302866..e0db4d4 100644
--- a/CloudServer/public/index.html
+++ b/CloudServer/public/index.html
@@ -811,9 +811,9 @@
-
-
@@ -1322,7 +1322,7 @@
} catch (e) {
content.innerHTML = `
Failed to load content
`;
}
- } else if (file.name.endsWith('.wav')) {
+ } else if (/\.(wav|mp3|ogg|flac|m4a|aac|opus)$/i.test(file.name)) {
saveBtn.style.display = 'none';
content.innerHTML = `
diff --git a/CloudServer/public/music.html b/CloudServer/public/music.html
index 11b1bbb..fbb3b70 100644
--- a/CloudServer/public/music.html
+++ b/CloudServer/public/music.html
@@ -185,7 +185,7 @@
-
+
@@ -363,7 +363,7 @@
await loadTracks();
// Try to select the uploaded file
- const baseName = String(file.name || "").replace(/\.(wav|dfpwm)$/i, "").trim();
+ const baseName = String(file.name || "").replace(/\.(wav|mp3|ogg|flac|m4a|aac|opus|dfpwm)$/i, "").trim();
const sel = document.getElementById("track_select");
for (let i = 0; i < sel.options.length; i++) {
if (sel.options[i].value === baseName) {
diff --git a/CloudServer/server.js b/CloudServer/server.js
index e972280..cd40f7d 100644
--- a/CloudServer/server.js
+++ b/CloudServer/server.js
@@ -43,6 +43,8 @@ const audioGeneralDir = path.join(audioRootDir, 'general');
const audioStationDir = path.join(audioRootDir, 'station');
const listFilePath = path.join(appRootDir, 'list.txt');
const tempBroadcastDir = path.join(appRootDir, 'temp-broadcast');
+const STREAMABLE_AUDIO_EXTENSIONS = ['.wav', '.mp3', '.ogg', '.flac', '.m4a', '.aac', '.opus'];
+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 });
@@ -446,10 +448,25 @@ async function sendLiveWavChunksToStation(stationName, fallbackWs, task) {
const stationMusicStates = new Map();
+function hasSupportedAudioExtension(name, exts = MUSIC_AUDIO_EXTENSIONS) {
+ const lower = String(name || '').trim().toLowerCase();
+ return exts.some(ext => lower.endsWith(ext));
+}
+
+function stripSupportedAudioExtension(name, exts = MUSIC_AUDIO_EXTENSIONS) {
+ const value = String(name || '').trim();
+ for (const ext of exts) {
+ if (value.toLowerCase().endsWith(ext)) {
+ return value.slice(0, -ext.length).trim();
+ }
+ }
+ return value;
+}
+
function normalizeBaseName(name) {
- const s = String(name || '').trim();
+ const s = stripSupportedAudioExtension(name);
if (!s) return null;
- const base = s.replace(/\.(wav|dfpwm)$/i, '').trim();
+ const base = s.trim();
return base || null;
}
@@ -470,7 +487,7 @@ function listMusicTracks() {
if (!file || file.startsWith('.')) continue;
const full = path.join(audioGeneralDir, file);
if (!fs.existsSync(full) || !fs.statSync(full).isFile()) continue;
- if (!file.toLowerCase().endsWith('.wav') && !file.toLowerCase().endsWith('.dfpwm')) continue;
+ if (!hasSupportedAudioExtension(file)) continue;
const base = normalizeBaseName(file);
if (base) out.add(base);
}
@@ -514,35 +531,38 @@ function getPlaybackAudioByBaseName(baseName) {
const base = normalizeBaseName(baseName);
if (!base) throw new Error('Invalid track name');
- const wavCandidates = [
- path.join(audioGeneralDir, `${base}.wav`),
- path.join(audioStationDir, `${base}.wav`)
- ];
- for (const p of wavCandidates) {
- if (fs.existsSync(p)) {
- const buffer = fs.readFileSync(p);
+ for (const ext of STREAMABLE_AUDIO_EXTENSIONS) {
+ const candidates = [
+ path.join(audioGeneralDir, `${base}${ext}`),
+ path.join(audioStationDir, `${base}${ext}`)
+ ];
+ for (const p of candidates) {
+ if (!fs.existsSync(p)) continue;
+ let durationSec = 0;
+ if (ext === '.wav') {
+ const buffer = fs.readFileSync(p);
+ durationSec = getWavDurationSec(buffer) || 0;
+ }
return {
sourcePath: p,
audioFormat: 'stream',
streamUrl: getManagedMediaUrl(p.startsWith(audioStationDir) ? 'audio/station' : 'audio/general', path.basename(p)),
- durationSec: getWavDurationSec(buffer) || 0
+ durationSec
};
}
}
- const dfCandidates = [
+ for (const p of [
path.join(audioGeneralDir, `${base}.dfpwm`),
path.join(audioStationDir, `${base}.dfpwm`)
- ];
- for (const p of dfCandidates) {
- if (fs.existsSync(p)) {
- const buffer = fs.readFileSync(p);
- return {
- buffer,
- audioFormat: 'dfpwm',
- durationSec: buffer.length / 6000
- };
- }
+ ]) {
+ if (!fs.existsSync(p)) continue;
+ const buffer = fs.readFileSync(p);
+ return {
+ buffer,
+ audioFormat: 'dfpwm',
+ durationSec: buffer.length / 6000
+ };
}
throw new Error(`Audio not found: ${base}`);
diff --git a/station_broadcast.lua b/station_broadcast.lua
index 110ce8d..e91e4a1 100644
--- a/station_broadcast.lua
+++ b/station_broadcast.lua
@@ -2,7 +2,7 @@
-- ============================================================================
-- 1. Configuration & Constants
-- ============================================================================
-local APP_VERSION_TEXT = "v1.0.5"
+local APP_VERSION_TEXT = "v1.0.6"
local CONFIG = {
CLOUD_URL = "ws://stabadmin.fse-media.group",
@@ -491,7 +491,15 @@ function Audio.loadStationList()
if codes and files then
local list = {}
for f in files:gmatch("[^,]+") do
- local cleanName = f:match("^%s*(.-)%s*$"):gsub("%.wav$", ""):gsub("%.dfpwm$", "")
+ local cleanName = f:match("^%s*(.-)%s*$")
+ :gsub("%.[Ww][Aa][Vv]$", "")
+ :gsub("%.[Mm][Pp]3$", "")
+ :gsub("%.[Oo][Gg][Gg]$", "")
+ :gsub("%.[Ff][Ll][Aa][Cc]$", "")
+ :gsub("%.[Mm]4[Aa]$", "")
+ :gsub("%.[Aa][Aa][Cc]$", "")
+ :gsub("%.[Oo][Pp][Uu][Ss]$", "")
+ :gsub("%.[Dd][Ff][Pp][Ww][Mm]$", "")
if cleanName ~= "" then
table.insert(list, cleanName)
end