Audio extension, handle custome background. Factorised bgm/ambient file listing into one assets listing function on server.js.

This commit is contained in:
Tony Ribeiro 2023-08-23 03:47:13 +02:00
parent b8c051694c
commit 8e38229ed4
2 changed files with 88 additions and 146 deletions

View File

@ -1,16 +1,8 @@
/* /*
TODO: Ideas:
- Emotion-based BGM - cross fading between bgm / start a different time
- per character bgms OK
- simple fade out/in when switching OK
- cross fading ?
- BGM switch cooldown
- group chat
- Background based ambient sounds - Background based ambient sounds
- global sounds OK
- global overides ?
- import option on background UI ? - import option on background UI ?
- One UI with different mixing options OK
- Allow background music edition using background menu - Allow background music edition using background menu
- https://fontawesome.com/icons/music?f=classic&s=solid - https://fontawesome.com/icons/music?f=classic&s=solid
- https://codepen.io/noirsociety/pen/rNQxQwm - https://codepen.io/noirsociety/pen/rNQxQwm
@ -18,13 +10,17 @@ TODO:
*/ */
import { saveSettingsDebounced } from "../../../script.js"; import { saveSettingsDebounced } from "../../../script.js";
import { getContext, getApiUrl, extension_settings, doExtrasFetch, ModuleWorkerWrapper, modules } from "../../extensions.js"; import { getContext, extension_settings, ModuleWorkerWrapper } from "../../extensions.js";
export { MODULE_NAME }; export { MODULE_NAME };
const MODULE_NAME = 'Audio'; const MODULE_NAME = 'Audio';
const DEBUG_PREFIX = "<Audio module> "; const DEBUG_PREFIX = "<Audio module> ";
const UPDATE_INTERVAL = 1000; const UPDATE_INTERVAL = 1000;
const ASSETS_BGM_FOLDER = "audio/bgm";
const ASSETS_AMBIENT_FOLDER = "audio/ambient";
const CHARACTER_BGM_FOLDER = "bgm"
const FALLBACK_EXPRESSION = "neutral"; const FALLBACK_EXPRESSION = "neutral";
const DEFAULT_EXPRESSIONS = [ const DEFAULT_EXPRESSIONS = [
//"talkinghead", //"talkinghead",
@ -147,7 +143,7 @@ async function onBGMVolumeChange() {
$("#audio_character_bgm").prop("volume",extension_settings.audio.bgm_volume * 0.01); $("#audio_character_bgm").prop("volume",extension_settings.audio.bgm_volume * 0.01);
$("#audio_character_bgm_volume").text(extension_settings.audio.bgm_volume); $("#audio_character_bgm_volume").text(extension_settings.audio.bgm_volume);
saveSettingsDebounced(); saveSettingsDebounced();
console.debug(DEBUG_PREFIX,"UPDATED BGM MAX TO",extension_settings.audio.bgm_volume); //console.debug(DEBUG_PREFIX,"UPDATED BGM MAX TO",extension_settings.audio.bgm_volume);
} }
async function onAmbientVolumeChange() { async function onAmbientVolumeChange() {
@ -155,14 +151,14 @@ async function onAmbientVolumeChange() {
$("#audio_ambient").prop("volume",extension_settings.audio.ambient_volume * 0.01); $("#audio_ambient").prop("volume",extension_settings.audio.ambient_volume * 0.01);
$("#audio_ambient_volume").text(extension_settings.audio.ambient_volume); $("#audio_ambient_volume").text(extension_settings.audio.ambient_volume);
saveSettingsDebounced(); saveSettingsDebounced();
console.debug(DEBUG_PREFIX,"UPDATED Ambient MAX TO",extension_settings.audio.ambient_volume); //console.debug(DEBUG_PREFIX,"UPDATED Ambient MAX TO",extension_settings.audio.ambient_volume);
} }
async function onBGMCooldownInput() { async function onBGMCooldownInput() {
extension_settings.audio.bgm_cooldown = ~~($("#audio_bgm_cooldown").val()); extension_settings.audio.bgm_cooldown = ~~($("#audio_bgm_cooldown").val());
cooldownBGM = extension_settings.audio.bgm_cooldown * 1000; cooldownBGM = extension_settings.audio.bgm_cooldown * 1000;
saveSettingsDebounced(); saveSettingsDebounced();
console.debug(DEBUG_PREFIX,"UPDATED BGM cooldown to",extension_settings.audio.bgm_cooldown); //console.debug(DEBUG_PREFIX,"UPDATED BGM cooldown to",extension_settings.audio.bgm_cooldown);
} }
$(document).ready(function () { $(document).ready(function () {
@ -257,27 +253,13 @@ $(document).ready(function () {
// API Calls // // API Calls //
//#############################// //#############################//
async function getAmbientList() { async function getAssetsList(folderPath) {
console.debug(DEBUG_PREFIX, "getting ambient audio files"); console.debug(DEBUG_PREFIX, "getting files from",folderPath);
try { try {
const result = await fetch(`/get_default_ambient_list`); const result = await fetch(`/get_assets_list?folderPath=${folderPath}`);
let musics = result.ok ? (await result.json()) : []; let file_paths = result.ok ? (await result.json()) : [];
return musics; return file_paths;
}
catch (err) {
console.log(err);
return [];
}
}
async function getDefaultBgmList() {
console.debug(DEBUG_PREFIX, "getting default bgm files");
try {
const result = await fetch(`/get_default_bgm_list`);
let musics = result.ok ? (await result.json()) : [];
return musics;
} }
catch (err) { catch (err) {
console.log(err); console.log(err);
@ -289,7 +271,7 @@ async function getCharacterBgmList(name) {
console.debug(DEBUG_PREFIX, "getting bgm list for", name); console.debug(DEBUG_PREFIX, "getting bgm list for", name);
try { try {
const result = await fetch(`/get_character_bgm_list?name=${encodeURIComponent(name)}`); const result = await fetch(`/get_character_assets_list?name=${encodeURIComponent(name)}&assetsFolder=${CHARACTER_BGM_FOLDER}`);
let musics = result.ok ? (await result.json()) : []; let musics = result.ok ? (await result.json()) : [];
return musics; return musics;
} }
@ -313,24 +295,32 @@ async function getCharacterBgmList(name) {
async function moduleWorker() { async function moduleWorker() {
const moduleEnabled = extension_settings.audio.enabled; const moduleEnabled = extension_settings.audio.enabled;
//console.debug(DEBUG_PREFIX, getContext());
if (moduleEnabled) { if (moduleEnabled) {
cooldownBGM -= UPDATE_INTERVAL; cooldownBGM -= UPDATE_INTERVAL;
//console.debug(DEBUG_PREFIX,currentCharacterBGM,currentExpressionBGM);
if (fallback_BGMS == null){ if (fallback_BGMS == null){
fallback_BGMS = await getDefaultBgmList(); console.debug(DEBUG_PREFIX,"Updating audio bgm assets...");
fallback_BGMS = await getAssetsList(ASSETS_BGM_FOLDER);
fallback_BGMS = fallback_BGMS.filter((filename) => filename != ".placeholder")
console.debug(DEBUG_PREFIX,"Detected assets:",fallback_BGMS);
} }
if (ambients == null){ if (ambients == null){
ambients = await getAmbientList(); console.debug(DEBUG_PREFIX,"Updating audio ambient assets...");
ambients = await getAssetsList(ASSETS_AMBIENT_FOLDER);
ambients = ambients.filter((filename) => filename != ".placeholder")
console.debug(DEBUG_PREFIX,"Detected assets:",ambients);
} }
// 1) Update ambient audio // 1) Update ambient audio
// --------------------------- // ---------------------------
let newBackground = $("#bg1").css("background-image"); let newBackground = $("#bg1").css("background-image");
newBackground = newBackground.substring(newBackground.lastIndexOf("/")+1).replace(/\.[^/.]+$/, "").replaceAll("%20","-"); // remove path and spaces const custom_background = getContext()["chatMetadata"]["custom_background"];
if(custom_background !== undefined)
newBackground = custom_background
newBackground = newBackground.substring(newBackground.lastIndexOf("/")+1).replace(/\.[^/.]+$/, "").replaceAll("%20","-").replaceAll(" ","-"); // remove path and spaces
//console.debug(DEBUG_PREFIX,"Current backgroung:",newBackground); //console.debug(DEBUG_PREFIX,"Current backgroung:",newBackground);
@ -359,8 +349,8 @@ async function moduleWorker() {
// 1.1) First time loading chat // 1.1) First time loading chat
if (characterMusics[newCharacter] === undefined) { if (characterMusics[newCharacter] === undefined) {
await loadCharacterBGM(newCharacter) await loadCharacterBGM(newCharacter);
//currentExpressionBGM = FALLBACK_EXPRESSION; currentExpressionBGM = FALLBACK_EXPRESSION;
//currentCharacterBGM = newCharacter; //currentCharacterBGM = newCharacter;
//updateBGM(); //updateBGM();
@ -460,7 +450,7 @@ async function loadCharacterBGM(newCharacter) {
// 1.1) First time character appear, load its music folder // 1.1) First time character appear, load its music folder
const audio_file_paths = await getCharacterBgmList(newCharacter); const audio_file_paths = await getCharacterBgmList(newCharacter);
console.debug(DEBUG_PREFIX, "Recieved", audio_file_paths); //console.debug(DEBUG_PREFIX, "Recieved", audio_file_paths);
// Initialise expression/files mapping // Initialise expression/files mapping
characterMusics[newCharacter] = {}; characterMusics[newCharacter] = {};
@ -470,8 +460,8 @@ async function loadCharacterBGM(newCharacter) {
for(const i of audio_file_paths) { for(const i of audio_file_paths) {
//console.debug(DEBUG_PREFIX,"File found:",i); //console.debug(DEBUG_PREFIX,"File found:",i);
for(const e of DEFAULT_EXPRESSIONS) for(const e of DEFAULT_EXPRESSIONS)
if (i["label"].includes(e)) if (i.includes(e))
characterMusics[newCharacter][e].push(i["path"]); characterMusics[newCharacter][e].push(i);
} }
console.debug(DEBUG_PREFIX,"Updated BGM map of",newCharacter,"to",characterMusics[newCharacter]); console.debug(DEBUG_PREFIX,"Updated BGM map of",newCharacter,"to",characterMusics[newCharacter]);
} }

154
server.js
View File

@ -318,8 +318,7 @@ const directories = {
context: 'public/context', context: 'public/context',
backups: 'backups/', backups: 'backups/',
quickreplies: 'public/QuickReplies', quickreplies: 'public/QuickReplies',
assets_bgm: 'public/assets/audio/bgm', assets: 'public/assets',
assets_ambient: 'public/assets/audio/ambient'
}; };
// CSRF Protection // // CSRF Protection //
@ -4935,108 +4934,36 @@ app.post('/delete_extension', jsonParser, async (request, response) => {
} }
}); });
/** /**
* HTTP POST handler function to retrieve local ST default BGM files. * HTTP POST handler function to retrieve name of all files of a given folder path.
* *
* @param {Object} request - HTTP Request object. * @param {Object} request - HTTP Request object. Require folder path in query
* @param {Object} response - HTTP Response object will contain a list of audio file path. * @param {Object} response - HTTP Response object will contain a list of file path.
* *
* @returns {void} * @returns {void}
*/ */
app.get('/get_default_bgm_list', jsonParser, function (request, response) { app.get('/get_assets_list', jsonParser, function (request, response) {
const musicsPath = directories.assets_bgm; const folderPath = path.join(directories.assets,request.query.folderPath);
let files = []; let output = []
let files_paths = []; //console.info("Checking files into",folderPath);
//console.info("Checking audio file into",musicsPath);
try { try {
if (fs.existsSync(musicsPath) && fs.statSync(musicsPath).isDirectory()) { if (fs.existsSync(folderPath) && fs.statSync(folderPath).isDirectory()) {
files = fs.readdirSync(musicsPath) const files = fs.readdirSync(folderPath)
.filter(file => { .filter(filename => {
const mimeType = mime.lookup(file); return filename != ".placeholder";
return mimeType && mimeType.startsWith('audio/'); });
});
for(const i of files) for (i of files)
files_paths.push(`/assets/audio/bgm/${i}`); output.push(`/assets/${request.query.folderPath}/${i}`);
} }
} }
catch (err) { catch (err) {
console.log(err); console.log(err);
} }
finally { finally {
return response.send(files_paths); return response.send(output);
}
});
/**
* HTTP POST handler function to retrieve a ST background ambient sounds files.
*
* @param {Object} request - HTTP Request object.
* @param {Object} response - HTTP Response object will contain a list of audio file path.
*
* @returns {void}
*/
app.get('/get_default_ambient_list', jsonParser, function (request, response) {
const musicsPath = directories.assets_ambient;
let files = [];
let files_paths = [];
//console.info("Checking audio file into",musicsPath);
try {
if (fs.existsSync(musicsPath) && fs.statSync(musicsPath).isDirectory()) {
files = fs.readdirSync(musicsPath)
.filter(file => {
const mimeType = mime.lookup(file);
return mimeType && mimeType.startsWith('audio/');
});
for(const i of files)
files_paths.push(path.join(`/assets/audio/ambient/${i}`));
}
}
catch (err) {
console.log(err);
}
finally {
return response.send(files_paths);
}
});
/**
* HTTP POST handler function to retrieve a character background music list.
*
* @param {Object} request - HTTP Request object, expects a character name in the query.
* @param {Object} response - HTTP Response object will contain a list of audio file path.
*
* @returns {void}
*/
app.get('/get_character_bgm_list', jsonParser, function (request, response) {
const AUDIO_FOLDER = "audio"
const BGM_FOLDER = "bgm"
const name = request.query.name;
const musicsPath = path.join(directories.characters, name, AUDIO_FOLDER,BGM_FOLDER);
let musics = [];
try {
if (fs.existsSync(musicsPath) && fs.statSync(musicsPath).isDirectory()) {
musics = fs.readdirSync(musicsPath)
.filter(file => {
const mimeType = mime.lookup(file);
return mimeType && mimeType.startsWith('audio/');
})
.map((file) => {
const pathToMusic = path.join(musicsPath, file);
return {
label: path.parse(pathToMusic).name.toLowerCase(),
path: `/characters/${name}/${AUDIO_FOLDER}/${BGM_FOLDER}/${file}`,
};
});
}
}
catch (err) {
console.log(err);
}
finally {
return response.send(musics);
} }
}); });
@ -5068,15 +4995,40 @@ app.get('/asset_download', jsonParser, function (request, response) {
}); });
downloadFile(url, file_path) downloadFile(url, file_path)
/* });
https.get(url, function(response) {
const fileStream = fs.createWriteStream(file_path);
response.pipe(fileStream);
// after download completed close filestream
fileStream.on("finish", () => {
fileStream .close(); ///////////////////////////////
console.log("Download Completed"); /**
}); * HTTP POST handler function to retrieve a character background music list.
});*/ *
* @param {Object} request - HTTP Request object, expects a character name in the query.
* @param {Object} response - HTTP Response object will contain a list of audio file path.
*
* @returns {void}
*/
app.get('/get_character_assets_list', jsonParser, function (request, response) {
const name = request.query.name;
const assetsFolder = request.query.assetsFolder
const folderPath = path.join(directories.characters, name, assetsFolder);
let output = [];
try {
if (fs.existsSync(folderPath) && fs.statSync(folderPath).isDirectory()) {
const files = fs.readdirSync(folderPath)
.filter(filename => {
return filename != ".placeholder";
});
for (i of files)
output.push(`/characters/${name}/${assetsFolder}/${i}`);
}
}
catch (err) {
console.log(err);
}
finally {
return response.send(output);
}
}); });