From 1554adf5f15d91f39ec6a5b72bd2cdb9e932aa50 Mon Sep 17 00:00:00 2001 From: HenryDu8133 <813367384@qq.com> Date: Sat, 1 Aug 2026 06:39:18 +0800 Subject: [PATCH] feat(audio): support more upload and music formats Allow the console and music page to upload common audio formats, expand music track detection and playback source selection beyond wav/dfpwm, and update station list name normalization so common extensions resolve consistently. Bump the station build to v1.0.6. --- CloudServer/public/index.html | 6 ++-- CloudServer/public/music.html | 4 +-- CloudServer/server.js | 64 +++++++++++++++++++++++------------ station_broadcast.lua | 12 +++++-- 4 files changed, 57 insertions(+), 29 deletions(-) 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