Skip to content

Commit

Permalink
Make it so mechvibes can process zip files (#324)
Browse files Browse the repository at this point in the history
* remove extra console.log

* Remove file check during loading, it causes issues with loading from zip files

* Add loading indicator during pack switching

* Add loading zipped packs

* Allow zipped packs to be discovered

* Add packages for zipped packs

* fix loading issue with zips

* Report pack load errors

* fix packs that load instantly getting stuck

* fix for audio-only video files

* make file names consistent when using zip files

* fix for zip files that fail to load being included in the selection dropdown
  • Loading branch information
NotLazy authored Jul 6, 2024
1 parent 18b624e commit 212a82f
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 49 deletions.
19 changes: 17 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,16 @@
}
},
"dependencies": {
"adm-zip": "^0.5.10",
"easy-volume": "^1.1.0",
"electron-fetch": "^1.9.1",
"electron-log": "^4.4.8",
"electron-store": "^6.0.0",
"fs-extra": "^9.0.1",
"glob": "^7.1.6",
"howler": "^2.1.2",
"iohook": "^0.9.3"
"howler": "^2.2.4",
"iohook": "^0.9.3",
"mime-types": "^2.1.35"
},
"devDependencies": {
"electron": "^12.2.3",
Expand Down
150 changes: 107 additions & 43 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ const { shell, remote, ipcRenderer } = require('electron');
const fs = require('fs');
const glob = require('glob');
const path = require('path');
const mime = require('mime-types');
const Zip = require('adm-zip');
const { platform } = process;
const remapper = require('./utils/remapper');

Expand Down Expand Up @@ -66,12 +68,15 @@ function loadPack(packId = null){
const app_body = document.getElementById('app-body');

log.info(`Loading ${packId}`)
app_logo.innerHTML = 'Loading...';
app_body.classList.add('loading');
_loadPack(packId).then(() => {
log.info("loaded");
app_logo.innerHTML = 'Mechvibes';
app_body.classList.remove('loading');
}).catch(() => {
}).catch((e) => {
app_logo.innerHTML = 'Failed';
log.warn(`Failed to load pack: ${e}`);
});
}

Expand Down Expand Up @@ -103,29 +108,17 @@ function _loadPack(packId){
}
}
Object.keys(pack.sound_data).map((kc) => {
let missing = false;
if(pack.sound_data[kc] !== undefined){
Object.keys(pack.sound_data[kc].src).map((i) => {
const path = pack.sound_data[kc].src[i];
console.log(path);
if(!fs.existsSync(path)){
missing = true;
}
})
}else{
missing = true;
}
if(missing){
// reject(5);
check();
return;
}
const audio = new Howl(pack.sound_data[kc]);
loaded_sounds[kc] = false;
audio.once('load', function(){
if(audio.state() == "loaded"){
loaded_sounds[kc] = audio;
check();
})
}else{
loaded_sounds[kc] = false;
audio.once('load', function(){
loaded_sounds[kc] = audio;
check();
})
}
})
}
}else{
Expand Down Expand Up @@ -167,25 +160,96 @@ function unloadAllPacks(){
// load all pack
async function loadPacks() {
// get all audio folders
const official_packs = await glob.sync(OFFICIAL_PACKS_DIR + '/*/');
const custom_packs = await glob.sync(CUSTOM_PACKS_DIR + '/*/');
const official_packs = await glob.sync(OFFICIAL_PACKS_DIR + '/*');
const custom_packs = await glob.sync(CUSTOM_PACKS_DIR + '/*');
const folders = [...official_packs, ...custom_packs];

log.info(`Loading ${folders.length} packs`);
log.debug(OFFICIAL_PACKS_DIR);
log.debug(CUSTOM_PACKS_DIR);

// get pack data
folders.map((folder) => {
// define group by types
const is_custom = folder.indexOf('mechvibes_custom') > -1 ? true : false;

// get folder name
const splited = folder.split('/');
const folder_name = splited[splited.length - 2];

// define config file path
const config_file = `${folder.replace(/\/$/, '')}/config.json`;
const folder_name = path.basename(folder);
// define if custom pack
const is_custom = (folder.substring(0, CUSTOM_PACKS_DIR.length) == CUSTOM_PACKS_DIR) ? true : false;
const is_archive = path.extname(folder) == '.zip';

if(!is_archive){
// define config file path
const config_file = `${folder.replace(/\/$/, '')}/config.json`;

// get pack info and defines data
if(fs.existsSync(config_file)){
const { name, includes_numpad, sound = '', defines, key_define_type = 'single' } = require(config_file);

// pack sound pack data
const pack_data = {
pack_id: `${is_custom ? 'custom' : 'default'}-${folder_name}`,
group: is_custom ? 'Custom' : 'Default',
abs_path: folder,
key_define_type,
name,
includes_numpad,
};

// init sound data
if (key_define_type == 'single') {
// define sound path
const sound_path = `${folder}/${sound}`;
if(!fs.existsSync(sound_path)){
return;
}
const sound_data = { src: [sound_path], sprite: keycodesRemap(defines) };
Object.assign(pack_data, { sound_data: sound_data });
} else {
const sound_data = {};
Object.keys(defines).map((kc) => {
if (defines[kc]) {
// define sound path
const sound_path = `${folder}/${defines[kc]}`;
if(!fs.existsSync(sound_path)){
return;
}
sound_data[kc] = { src: [sound_path] };
}
});
if (Object.keys(sound_data).length) {
Object.assign(pack_data, { sound_data: keycodesRemap(sound_data) });
}
}

// push pack data to pack list
packs.push(pack_data);
}
}else{
log.warn(`Archive packs are not supported yet. ${folder}`);
const zip = new Zip(folder);
const zipFiles = zip.getEntries();
let files = {};
zipFiles.map((file) => {
if(file.isDirectory){
return;
}
const fileName = path.basename(file.entryName).toLowerCase();
if(fileName == 'config.json'){
files[fileName] = file.getData().toString('utf8');
}else{
const _mimeType = mime.lookup(fileName);
// HACK: some soundpacks have mp4 files, which are actually audio files, and howler can play them but refuses
// to load them because it thinks they are video files. So we change the mimeType to audio/* but also mime.lookup
// doesn't seem to return a string, so we also need to convert it to a string.
let mimeType = `${_mimeType}`;
if(mimeType.substring(0, 6) == 'video/'){
mimeType = mimeType.replace('video/', 'audio/');
}
files[fileName] = `data:${mimeType};base64,${file.getData().toString('base64')}`;
}
});

// get pack info and defines data
if(fs.existsSync(config_file)){
const { name, includes_numpad, sound = '', defines, key_define_type = 'single' } = require(config_file);
// get config vars
const { name, includes_numpad, sound = '', defines, key_define_type = 'single' } = JSON.parse(files['config.json']);

// pack sound pack data
const pack_data = {
Expand All @@ -196,12 +260,10 @@ async function loadPacks() {
name,
includes_numpad,
};

// init sound data
if (key_define_type == 'single') {
// define sound path
const sound_path = `${folder}${sound}`;
if(!fs.existsSync(sound_path)){
if(files[sound] === undefined){
return;
}
const sound_data = { src: [sound_path], sprite: keycodesRemap(defines) };
Expand All @@ -211,18 +273,21 @@ async function loadPacks() {
Object.keys(defines).map((kc) => {
if (defines[kc]) {
// define sound path
const sound_path = `${folder}${defines[kc]}`;
if(!fs.existsSync(sound_path)){
const definition = defines[kc].toLowerCase();
if(files[definition] === undefined){
return;
}
sound_data[kc] = { src: [sound_path] };
const file = files[definition];
sound_data[kc] = { src: [file] };
}
});
if (Object.keys(sound_data).length) {
Object.assign(pack_data, { sound_data: keycodesRemap(sound_data) });
}else{
return;
}
}

// push pack data to pack list
packs.push(pack_data);
}
Expand Down Expand Up @@ -385,7 +450,6 @@ function packsToOptions(packs, pack_list) {
loadPack()

// handle tray hiding
console.log(store.get(MV_TRAY_LSID));
if (store.get(MV_TRAY_LSID) !== undefined){
tray_icon_toggle.checked = store.get(MV_TRAY_LSID);
}
Expand Down
13 changes: 11 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,15 @@
resolved "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz"
integrity sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==

"7zip-bin@~5.1.1":
version "5.1.1"
resolved "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.1.1.tgz"
integrity sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ==

adm-zip@^0.5.10:
version "0.5.10"
resolved "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz"
integrity sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==
agent-base@6:
version "6.0.2"
resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz"
Expand Down Expand Up @@ -1195,7 +1204,7 @@ hosted-git-info@^4.1.0:
dependencies:
lru-cache "^6.0.0"

howler@^2.1.2:
howler@^2.2.4:
version "2.2.4"
resolved "https://registry.npmjs.org/howler/-/howler-2.2.4.tgz"
integrity sha512-iARIBPgcQrwtEr+tALF+rapJ8qSc+Set2GJQl7xT1MQzWaVkFebdJhR3alVlSiUf5U7nAANKuj3aWpwerocD5w==
Expand Down Expand Up @@ -1491,7 +1500,7 @@ [email protected]:
resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==

mime-types@^2.1.12, mime-types@~2.1.19:
mime-types@^2.1.12, mime-types@^2.1.35, mime-types@~2.1.19:
version "2.1.35"
resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
Expand Down

0 comments on commit 212a82f

Please sign in to comment.