\ No newline at end of file
diff --git a/processing/split.html b/processing/split.html
index c00c040..e2b818a 100644
--- a/processing/split.html
+++ b/processing/split.html
@@ -21,14 +21,14 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
Maximum file length:
@@ -39,7 +39,7 @@
-
+
@@ -57,7 +57,7 @@
-
+
@@ -68,52 +68,8 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Writing WAV files to source folder.
-
-
-
-
-
-
-
@@ -165,9 +121,11 @@
+
+
-
+
@@ -176,6 +134,7 @@
diff --git a/processing/uiCommon.js b/processing/uiCommon.js
index 6262386..b6dce1b 100644
--- a/processing/uiCommon.js
+++ b/processing/uiCommon.js
@@ -7,23 +7,11 @@
/* Functions which control elements common to the expansion and split windows */
const electron = require('electron');
-const dialog = electron.remote.dialog;
-
-const path = require('path');
-const fs = require('fs');
const nightMode = require('../nightMode.js');
-const currentWindow = electron.remote.getCurrentWindow();
-
const fileButton = document.getElementById('file-button');
-const prefixInput = document.getElementById('prefix-input');
-const prefixCheckbox = document.getElementById('prefix-checkbox');
-const prefixLabel = document.getElementById('prefix-label');
-
-const outputLabel = document.getElementById('output-label');
-
function getSelectedRadioValue (radioName) {
return parseInt(document.querySelector('input[name="' + radioName + '"]:checked').value);
@@ -78,148 +66,3 @@ exports.updateButtonText = () => {
}
};
-
-/* Open dialog and set files to be expanded */
-
-exports.selectRecordings = (fileRegex) => {
-
- let folderContents, filePath, fileName, recordings;
-
- const selectionTypes = ['openFile', 'openDirectory'];
- const selectionType = getSelectedRadioValue('selection-radio');
- const properties = [selectionTypes[selectionType], 'multiSelections'];
-
- /* If files are being selected, allow users to selectt more than one item. Only a single folder can be selected */
-
- if (selectionType === 0) {
-
- properties.push('multiSelections');
-
- }
-
- /* If files are being selected, limit selection to .wav files */
-
- const filters = (selectionType === 0) ? [{name: 'wav', extensions: ['wav']}] : [];
-
- const selection = dialog.showOpenDialogSync(currentWindow, {
- title: 'Select recording file or folder containing recordings',
- nameFieldLabel: 'Recordings',
- properties: properties,
- filters: filters
- });
-
- if (selection) {
-
- recordings = [];
-
- if (selectionType === 0) {
-
- for (let i = 0; i < selection.length; i++) {
-
- filePath = selection[i];
- fileName = path.basename(filePath);
-
- /* Check if wav files match a given regex and thus can be expanded/split */
-
- if (filePath.charAt(0) !== '.' && fileRegex.test(fileName.toUpperCase())) {
-
- recordings.push(filePath);
-
- }
-
- }
-
- } else {
-
- for (let i = 0; i < selection.length; i++) {
-
- folderContents = fs.readdirSync(selection[i]);
-
- for (let j = 0; j < folderContents.length; j++) {
-
- filePath = folderContents[j];
-
- if (filePath.charAt(0) !== '.' && fileRegex.test(filePath.toUpperCase())) {
-
- recordings.push(path.join(selection[i], filePath));
-
- }
-
- }
-
- }
-
- }
-
- return recordings;
-
- }
-
-};
-
-/* Remove all characters which aren't A-Z, a-z, 0-9, and _ */
-
-prefixInput.addEventListener('keydown', (e) => {
-
- if (prefixInput.disabled) {
-
- e.preventDefault();
- return;
-
- }
-
- var reg = /[^A-Za-z-_0-9]{1}/g;
-
- if (reg.test(e.key)) {
-
- e.preventDefault();
-
- }
-
-});
-
-prefixInput.addEventListener('paste', (e) => {
-
- e.stopPropagation();
- e.preventDefault();
-
- if (prefixInput.disabled) {
-
- return;
-
- }
-
- /* Read text from clipboard */
-
- const clipboardData = e.clipboardData || window.clipboardData;
- const pastedData = clipboardData.getData('Text');
-
- /* Perform paste, but remove all unsupported characters */
-
- prefixInput.value += pastedData.replace(/[^A-Za-z_0-9]{1}/g, '');
-
- /* Limit max number of characters */
-
- prefixInput.value = prefixInput.value.substring(0, prefixInput.maxLength);
-
-});
-
-/* Add listener to handle enabling/disabling prefix UI */
-
-prefixCheckbox.addEventListener('change', () => {
-
- if (prefixCheckbox.checked) {
-
- prefixLabel.classList.remove('grey');
- prefixInput.classList.remove('grey');
- prefixInput.disabled = false;
-
- } else {
-
- prefixLabel.classList.add('grey');
- prefixInput.classList.add('grey');
- prefixInput.disabled = true;
-
- }
-
-});
diff --git a/processing/uiDownsampling.js b/processing/uiDownsampling.js
index 55a4744..0ec3748 100644
--- a/processing/uiDownsampling.js
+++ b/processing/uiDownsampling.js
@@ -44,7 +44,7 @@ const downsampleButton = document.getElementById('downsample-button');
var files = [];
var downsampling = false;
-const DEFAULT_SLEEP_AMOUNT = 3000;
+const DEFAULT_SLEEP_AMOUNT = 2000;
/* Disable UI elements in main window while progress bar is open and downsample is in progress */
@@ -91,8 +91,6 @@ function enableUI () {
}
-/* Split selected files */
-
function downsampleFiles () {
if (!files) {
@@ -111,6 +109,11 @@ function downsampleFiles () {
let sleepAmount = DEFAULT_SLEEP_AMOUNT;
let successesWithoutError = 0;
+ const unwrittenErrors = [];
+ let lastErrorWrite = -1;
+
+ let errorFileStream;
+
for (let i = 0; i < files.length; i++) {
/* If progress bar is closed, the downsample task is considered cancelled. This will contact the main thread and ask if that has happened */
@@ -127,7 +130,7 @@ function downsampleFiles () {
/* Let the main thread know what value to set the progress bar to */
- electron.ipcRenderer.send('set-downsample-bar-progress', i, 0, path.basename(files[i]));
+ electron.ipcRenderer.send('set-downsample-bar-progress', i, 0);
const sampleRate = SAMPLE_RATES[ui.getSelectedRadioValue('sample-rate-radio')];
@@ -138,12 +141,36 @@ function downsampleFiles () {
/* Check if the optional prefix/output directory setttings are being used. If left as null, splitter will put file(s) in the same directory as the input with no prefix */
- const outputPath = uiOutput.isChecked() ? uiOutput.getOutputDir() : null;
+ let outputPath = null;
+
+ if (uiOutput.isCustomDestinationEnabled()) {
+
+ outputPath = uiOutput.getOutputDir();
+
+ if (uiOutput.isCreateSubdirectoriesEnabled() && selectionRadios[1].checked) {
+
+ const dirnames = path.dirname(files[i]).replace(/\\/g, '/').split('/');
+
+ const folderName = dirnames[dirnames.length - 1];
+
+ outputPath = path.join(outputPath, folderName);
+
+ if (!fs.existsSync(outputPath)) {
+
+ fs.mkdirSync(outputPath);
+
+ }
+
+ }
+
+ }
+
const prefix = (prefixCheckbox.checked && prefixInput.value !== '') ? prefixInput.value : null;
const response = audiomothUtils.downsample(files[i], outputPath, prefix, sampleRate, (progress) => {
- electron.ipcRenderer.send('set-downsample-bar-progress', i, progress, path.basename(files[i]));
+ electron.ipcRenderer.send('set-downsample-bar-progress', i, progress);
+ electron.ipcRenderer.send('set-downsample-bar-file', i, path.basename(files[i]));
});
@@ -162,6 +189,7 @@ function downsampleFiles () {
/* Add error to log file */
+ unwrittenErrors.push(errorCount);
successesWithoutError = 0;
errorCount++;
errors.push(response.error);
@@ -171,31 +199,47 @@ function downsampleFiles () {
if (errorCount === 1) {
- const errorFileLocation = uiOutput.isChecked() ? uiOutput.getOutputDir() : path.dirname(errorFiles[0]);
-
+ const errorFileLocation = uiOutput.isCustomDestinationEnabled() ? uiOutput.getOutputDir() : path.dirname(errorFiles[0]);
errorFilePath = path.join(errorFileLocation, 'ERRORS.TXT');
+ errorFileStream = fs.createWriteStream(errorFilePath, {flags: 'a'});
+
+ errorFileStream.write('-- Downsample --\n');
}
- let fileContent = '';
+ const currentTime = new Date();
+ const timeSinceLastErrorWrite = currentTime - lastErrorWrite;
- for (let j = 0; j < errorCount; j++) {
+ if (timeSinceLastErrorWrite > 1000 || lastErrorWrite === -1) {
- fileContent += path.basename(errorFiles[j]) + ' - ' + errors[j] + '\n';
+ lastErrorWrite = new Date();
- }
+ const unwrittenErrorCount = unwrittenErrors.length;
- try {
+ console.log('Writing', unwrittenErrorCount, 'errors');
- fs.writeFileSync(errorFilePath, fileContent);
+ let fileContent = '';
- console.log('Error summary written to ' + errorFilePath);
+ for (let e = 0; e < unwrittenErrorCount; e++) {
- } catch (err) {
+ const unwrittenErrorIndex = unwrittenErrors.pop();
+ fileContent += path.basename(errorFiles[unwrittenErrorIndex]) + ' - ' + errors[unwrittenErrorIndex] + '\n';
- console.error(err);
- electron.ipcRenderer.send('set-downsample-bar-completed', successCount, errorCount, true);
- return;
+ }
+
+ try {
+
+ errorFileStream.write(fileContent);
+
+ console.log('Error summary written to ' + errorFilePath);
+
+ } catch (err) {
+
+ console.error(err);
+ electron.ipcRenderer.send('set-downsample-bar-completed', successCount, errorCount, true);
+ return;
+
+ }
}
@@ -206,6 +250,41 @@ function downsampleFiles () {
}
+ /* If any errors occurred, do a final error write */
+
+ const unwrittenErrorCount = unwrittenErrors.length;
+
+ if (unwrittenErrorCount > 0) {
+
+ console.log('Writing remaining', unwrittenErrorCount, 'errors');
+
+ let fileContent = '';
+
+ for (let e = 0; e < unwrittenErrorCount; e++) {
+
+ const unwrittenErrorIndex = unwrittenErrors.pop();
+ fileContent += path.basename(errorFiles[unwrittenErrorIndex]) + ' - ' + errors[unwrittenErrorIndex] + '\n';
+
+ }
+
+ try {
+
+ errorFileStream.write(fileContent);
+
+ console.log('Error summary written to ' + errorFilePath);
+
+ errorFileStream.end();
+
+ } catch (err) {
+
+ console.error(err);
+ electron.ipcRenderer.send('set-downsample-bar-completed', successCount, errorCount, true);
+ return;
+
+ }
+
+ }
+
/* Notify main thread that split is complete so progress bar is closed */
electron.ipcRenderer.send('set-downsample-bar-completed', successCount, errorCount, false);
@@ -260,7 +339,7 @@ selectionRadios[1].addEventListener('change', resetUI);
fileButton.addEventListener('click', () => {
- files = ui.selectRecordings(FILE_REGEX);
+ files = uiOutput.selectRecordings(FILE_REGEX);
updateInputDirectoryDisplay(files);
@@ -276,7 +355,7 @@ downsampleButton.addEventListener('click', () => {
}
- if ((!prefixCheckbox.checked || prefixInput.value === '') && (!uiOutput.isChecked() || uiOutput.getOutputDir() === '')) {
+ if ((!prefixCheckbox.checked || prefixInput.value === '') && (!uiOutput.isCustomDestinationEnabled() || uiOutput.getOutputDir() === '')) {
dialog.showMessageBox(currentWindow, {
type: 'error',
@@ -288,6 +367,50 @@ downsampleButton.addEventListener('click', () => {
}
+ /* Check if output location is the same as input */
+
+ for (let i = 0; i < files.length; i++) {
+
+ if (!prefixCheckbox.checked || prefixInput.value === '') {
+
+ /* If folder mode is enabled, the input folder is the same as the output and subdirectories are enabled, files will be overwritten as the paths will match */
+
+ if (selectionRadios[1].checked && uiOutput.isCustomDestinationEnabled() && uiOutput.isCreateSubdirectoriesEnabled()) {
+
+ /* Get the parent folder of the selected file and compare that to the output directory */
+
+ const fileFolderPath = path.dirname(path.dirname(files[i]));
+
+ if (uiOutput.getOutputDir() === fileFolderPath) {
+
+ dialog.showMessageBox(currentWindow, {
+ type: 'error',
+ title: 'Cannot downsample with current settings',
+ message: 'Output destination is the same as input destination and no prefix is selected.'
+ });
+
+ return;
+
+ }
+
+ }
+
+ if (uiOutput.getOutputDir() === path.dirname(files[i])) {
+
+ dialog.showMessageBox(currentWindow, {
+ type: 'error',
+ title: 'Cannot downsample with current settings',
+ message: 'Output destination is the same as input destination and no prefix is selected.'
+ });
+
+ return;
+
+ }
+
+ }
+
+ }
+
downsampling = true;
disableUI();
diff --git a/processing/uiExpansion.js b/processing/uiExpansion.js
index 1a753d8..74be30b 100644
--- a/processing/uiExpansion.js
+++ b/processing/uiExpansion.js
@@ -9,6 +9,7 @@
/* global document */
const electron = require('electron');
+const dialog = electron.remote.dialog;
/* Get functions which control elements common to the expansion, split, and downsample windows */
const ui = require('./uiCommon.js');
@@ -19,6 +20,8 @@ const fs = require('fs');
const audiomothUtils = require('audiomoth-utils');
+var currentWindow = electron.remote.getCurrentWindow();
+
const MAX_LENGTHS = [1, 5, 10, 15, 30, 60, 300, 600, 3600];
const MAX_LENGTH_STRINGS = ['1 second', '5 seconds', '10 seconds', '15 seconds', '30 seconds', '1 minute', '5 minutes', '10 minutes', '1 hour'];
@@ -52,7 +55,7 @@ var expanding = false;
var expansionType = 'DURATION';
-const DEFAULT_SLEEP_AMOUNT = 3000;
+const DEFAULT_SLEEP_AMOUNT = 2000;
/* Disable UI elements in main window while progress bar is open and expansion is in progress */
@@ -187,6 +190,11 @@ function expandFiles () {
let sleepAmount = DEFAULT_SLEEP_AMOUNT;
let successesWithoutError = 0;
+ const unwrittenErrors = [];
+ let lastErrorWrite = -1;
+
+ let errorFileStream;
+
for (let i = 0; i < files.length; i++) {
/* If progress bar is closed, the expansion task is considered cancelled. This will contact the main thread and ask if that has happened */
@@ -203,7 +211,7 @@ function expandFiles () {
/* Let the main thread know what value to set the progress bar to */
- electron.ipcRenderer.send('set-expansion-bar-progress', i, 0, path.basename(files[i]));
+ electron.ipcRenderer.send('set-expansion-bar-progress', i, 0);
/* If max length is enabled for the current expansion mode (there is separate UI for each and only the relevant one is shown) */
@@ -235,12 +243,36 @@ function expandFiles () {
/* Check if the optional prefix/output directory setttings are being used. If left as null, expander will put expanded file(s) in the same directory as the input with no prefix */
- const outputPath = uiOutput.isChecked() ? uiOutput.getOutputDir() : null;
+ let outputPath = null;
+
+ if (uiOutput.isCustomDestinationEnabled()) {
+
+ outputPath = uiOutput.getOutputDir();
+
+ if (uiOutput.isCreateSubdirectoriesEnabled() && selectionRadios[1].checked) {
+
+ const dirnames = path.dirname(files[i]).replace(/\\/g, '/').split('/');
+
+ const folderName = dirnames[dirnames.length - 1];
+
+ outputPath = path.join(outputPath, folderName);
+
+ if (!fs.existsSync(outputPath)) {
+
+ fs.mkdirSync(outputPath);
+
+ }
+
+ }
+
+ }
+
const prefix = (prefixCheckbox.checked && prefixInput.value !== '') ? prefixInput.value : null;
const response = audiomothUtils.expand(files[i], outputPath, prefix, expansionType, maxLength, generateSilentFiles, alignToSecondTransitions, (progress) => {
- electron.ipcRenderer.send('set-expansion-bar-progress', i, progress, path.basename(files[i]));
+ electron.ipcRenderer.send('set-expansion-bar-progress', i, progress);
+ electron.ipcRenderer.send('set-expansion-bar-file', i, path.basename(files[i]));
});
@@ -259,6 +291,7 @@ function expandFiles () {
/* Add error to log file */
+ unwrittenErrors.push(errorCount);
successesWithoutError = 0;
errorCount++;
errors.push(response.error);
@@ -268,31 +301,47 @@ function expandFiles () {
if (errorCount === 1) {
- const errorFileLocation = uiOutput.isChecked() ? uiOutput.getOutputDir() : path.dirname(errorFiles[0]);
-
+ const errorFileLocation = uiOutput.isCustomDestinationEnabled() ? uiOutput.getOutputDir() : path.dirname(errorFiles[0]);
errorFilePath = path.join(errorFileLocation, 'ERRORS.TXT');
+ errorFileStream = fs.createWriteStream(errorFilePath, {flags: 'a'});
+
+ errorFileStream.write('-- Expansion --\n');
}
- let fileContent = '';
+ const currentTime = new Date();
+ const timeSinceLastErrorWrite = currentTime - lastErrorWrite;
- for (let j = 0; j < errorCount; j++) {
+ if (timeSinceLastErrorWrite > 1000 || lastErrorWrite === -1) {
- fileContent += path.basename(errorFiles[j]) + ' - ' + errors[j] + '\n';
+ lastErrorWrite = new Date();
- }
+ const unwrittenErrorCount = unwrittenErrors.length;
- try {
+ console.log('Writing', unwrittenErrorCount, 'errors');
- fs.writeFileSync(errorFilePath, fileContent);
+ let fileContent = '';
- console.log('Error summary written to ' + errorFilePath);
+ for (let e = 0; e < unwrittenErrorCount; e++) {
- } catch (err) {
+ const unwrittenErrorIndex = unwrittenErrors.pop();
+ fileContent += path.basename(errorFiles[unwrittenErrorIndex]) + ' - ' + errors[unwrittenErrorIndex] + '\n';
- console.error(err);
- electron.ipcRenderer.send('set-expansion-bar-completed', successCount, errorCount, true);
- return;
+ }
+
+ try {
+
+ errorFileStream.write(fileContent);
+
+ console.log('Error summary written to ' + errorFilePath);
+
+ } catch (err) {
+
+ console.error(err);
+ electron.ipcRenderer.send('set-expansion-bar-completed', successCount, errorCount, true);
+ return;
+
+ }
}
@@ -303,6 +352,41 @@ function expandFiles () {
}
+ /* If any errors occurred, do a final error write */
+
+ const unwrittenErrorCount = unwrittenErrors.length;
+
+ if (unwrittenErrorCount > 0) {
+
+ console.log('Writing remaining', unwrittenErrorCount, 'errors');
+
+ let fileContent = '';
+
+ for (let e = 0; e < unwrittenErrorCount; e++) {
+
+ const unwrittenErrorIndex = unwrittenErrors.pop();
+ fileContent += path.basename(errorFiles[unwrittenErrorIndex]) + ' - ' + errors[unwrittenErrorIndex] + '\n';
+
+ }
+
+ try {
+
+ errorFileStream.write(fileContent);
+
+ console.log('Error summary written to ' + errorFilePath);
+
+ errorFileStream.end();
+
+ } catch (err) {
+
+ console.error(err);
+ electron.ipcRenderer.send('set-expansion-bar-completed', successCount, errorCount, true);
+ return;
+
+ }
+
+ }
+
/* Notify main thread that expansion is complete so progress bar is closed */
electron.ipcRenderer.send('set-expansion-bar-completed', successCount, errorCount, false);
@@ -493,7 +577,7 @@ selectionRadios[1].addEventListener('change', resetUI);
fileButton.addEventListener('click', () => {
- files = ui.selectRecordings(FILE_REGEX);
+ files = uiOutput.selectRecordings(FILE_REGEX);
updateInputDirectoryDisplay(files);
@@ -509,6 +593,50 @@ expandButton.addEventListener('click', () => {
}
+ /* Check if output location is the same as input */
+
+ for (let i = 0; i < files.length; i++) {
+
+ if (!prefixCheckbox.checked || prefixInput.value === '') {
+
+ /* If folder mode is enabled, the input folder is the same as the output and subdirectories are enabled, files will be overwritten as the paths will match */
+
+ if (selectionRadios[1].checked && uiOutput.isCustomDestinationEnabled() && uiOutput.isCreateSubdirectoriesEnabled()) {
+
+ /* Get the parent folder of the selected file and compare that to the output directory */
+
+ const fileFolderPath = path.dirname(path.dirname(files[i]));
+
+ if (uiOutput.getOutputDir() === fileFolderPath) {
+
+ dialog.showMessageBox(currentWindow, {
+ type: 'error',
+ title: 'Cannot downsample with current settings',
+ message: 'Output destination is the same as input destination and no prefix is selected.'
+ });
+
+ return;
+
+ }
+
+ }
+
+ if (uiOutput.getOutputDir() === path.dirname(files[i])) {
+
+ dialog.showMessageBox(currentWindow, {
+ type: 'error',
+ title: 'Cannot downsample with current settings',
+ message: 'Output destination is the same as input destination and no prefix is selected.'
+ });
+
+ return;
+
+ }
+
+ }
+
+ }
+
expanding = true;
disableUI();
diff --git a/processing/uiOutput.js b/processing/uiOutput.js
index 0e91562..2b17183 100644
--- a/processing/uiOutput.js
+++ b/processing/uiOutput.js
@@ -10,47 +10,76 @@
const electron = require('electron');
const dialog = electron.remote.dialog;
+const currentWindow = electron.remote.getCurrentWindow();
+
+const path = require('path');
+const fs = require('fs');
const outputCheckbox = document.getElementById('output-checkbox');
const outputButton = document.getElementById('output-button');
const outputLabel = document.getElementById('output-label');
+const subdirectoriesLabel = document.getElementById('subdirectories-label');
+const subdirectoriesCheckbox = document.getElementById('subdirectories-checkbox');
+
var outputDir = '';
+const prefixInput = document.getElementById('prefix-input');
+const prefixCheckbox = document.getElementById('prefix-checkbox');
+const prefixLabel = document.getElementById('prefix-label');
+
+const selectionRadios = document.getElementsByName('selection-radio');
+
+function getSelectedRadioValue (radioName) {
+
+ return parseInt(document.querySelector('input[name="' + radioName + '"]:checked').value);
+
+}
+
/* Update label to notify user if a custom output directtory is being used */
-function updateOutputLabel (outputDir) {
+function updateOutputLabel () {
- if (outputDir === '') {
+ if (outputDir === '' || !outputCheckbox.checked) {
outputLabel.textContent = 'Writing WAV files to source folder.';
} else {
- outputLabel.textContent = 'Writing WAV files to custom folder.';
+ outputLabel.textContent = 'Writing WAV files to destination folder.';
}
};
-/* Add listener which handles enabling/disabling custom output directory UI */
+function updateSubdirectoriesCheckbox () {
-outputCheckbox.addEventListener('change', () => {
+ const selectionType = getSelectedRadioValue('selection-radio');
- if (outputCheckbox.checked) {
+ if (outputCheckbox.checked && outputDir !== '' && selectionType === 1) {
- outputLabel.classList.remove('grey');
- outputButton.disabled = false;
+ subdirectoriesCheckbox.disabled = false;
+ subdirectoriesLabel.classList.remove('grey');
} else {
- outputLabel.classList.add('grey');
- outputButton.disabled = true;
- outputDir = '';
- updateOutputLabel(outputDir);
+ subdirectoriesCheckbox.disabled = true;
+ subdirectoriesLabel.classList.add('grey');
}
+}
+
+/* Add listener which handles enabling/disabling custom output directory UI */
+
+outputCheckbox.addEventListener('change', () => {
+
+ updateOutputLabel();
+
+ updateSubdirectoriesCheckbox();
+
+ outputButton.disabled = !outputCheckbox.checked;
+
});
/* Select a custom output directory. If Cancel is pressed, assume no custom direcotry is wantted */
@@ -66,7 +95,154 @@ outputButton.addEventListener('click', () => {
outputDir = (destinationName !== undefined) ? destinationName[0] : '';
- updateOutputLabel(outputDir);
+ updateOutputLabel();
+
+ updateSubdirectoriesCheckbox();
+
+});
+
+/* Open dialog and set files to be expanded */
+
+exports.selectRecordings = (fileRegex) => {
+
+ let folderContents, filePath, fileName, recordings;
+
+ const selectionTypes = ['openFile', 'openDirectory'];
+ const selectionType = getSelectedRadioValue('selection-radio');
+ const properties = [selectionTypes[selectionType], 'multiSelections'];
+
+ /* If files are being selected, allow users to selectt more than one item. Only a single folder can be selected */
+
+ if (selectionType === 0) {
+
+ properties.push('multiSelections');
+
+ }
+
+ /* If files are being selected, limit selection to .wav files */
+
+ const filters = (selectionType === 0) ? [{name: 'wav', extensions: ['wav']}] : [];
+
+ const selection = dialog.showOpenDialogSync(currentWindow, {
+ title: 'Select recording file or folder containing recordings',
+ nameFieldLabel: 'Recordings',
+ properties: properties,
+ filters: filters
+ });
+
+ if (selection) {
+
+ recordings = [];
+
+ if (selectionType === 0) {
+
+ for (let i = 0; i < selection.length; i++) {
+
+ filePath = selection[i];
+ fileName = path.basename(filePath);
+
+ /* Check if wav files match a given regex and thus can be expanded/split */
+
+ if (filePath.charAt(0) !== '.' && fileRegex.test(fileName.toUpperCase())) {
+
+ recordings.push(filePath);
+
+ }
+
+ }
+
+ } else {
+
+ for (let i = 0; i < selection.length; i++) {
+
+ folderContents = fs.readdirSync(selection[i]);
+
+ for (let j = 0; j < folderContents.length; j++) {
+
+ filePath = folderContents[j];
+
+ if (filePath.charAt(0) !== '.' && fileRegex.test(filePath.toUpperCase())) {
+
+ recordings.push(path.join(selection[i], filePath));
+
+ }
+
+ }
+
+ }
+
+ }
+
+ return recordings;
+
+ }
+
+};
+
+/* Remove all characters which aren't A-Z, a-z, 0-9, and _ */
+
+prefixInput.addEventListener('keydown', (e) => {
+
+ if (prefixInput.disabled) {
+
+ e.preventDefault();
+ return;
+
+ }
+
+ var reg = /[^A-Za-z-_0-9]{1}/g;
+
+ if (reg.test(e.key)) {
+
+ e.preventDefault();
+
+ }
+
+});
+
+prefixInput.addEventListener('paste', (e) => {
+
+ e.stopPropagation();
+ e.preventDefault();
+
+ if (prefixInput.disabled) {
+
+ return;
+
+ }
+
+ /* Read text from clipboard */
+
+ const clipboardData = e.clipboardData || window.clipboardData;
+ const pastedData = clipboardData.getData('Text');
+
+ /* Perform paste, but remove all unsupported characters */
+
+ prefixInput.value += pastedData.replace(/[^A-Za-z_0-9]{1}/g, '');
+
+ /* Limit max number of characters */
+
+ prefixInput.value = prefixInput.value.substring(0, prefixInput.maxLength);
+
+});
+
+/* Add listener to handle enabling/disabling prefix UI */
+
+prefixCheckbox.addEventListener('change', () => {
+
+ if (prefixCheckbox.checked) {
+
+ prefixLabel.classList.remove('grey');
+ prefixInput.classList.remove('grey');
+ prefixInput.disabled = false;
+
+ } else {
+
+ prefixLabel.classList.add('grey');
+ prefixInput.classList.add('grey');
+ prefixInput.disabled = true;
+
+ }
});
@@ -94,7 +270,7 @@ exports.enableOutputButton = () => {
};
-exports.isChecked = () => {
+exports.isCustomDestinationEnabled = () => {
return outputCheckbox.checked;
@@ -105,3 +281,12 @@ exports.getOutputDir = () => {
return outputDir;
};
+
+exports.isCreateSubdirectoriesEnabled = () => {
+
+ return subdirectoriesCheckbox.checked;
+
+};
+
+selectionRadios[0].addEventListener('change', updateSubdirectoriesCheckbox);
+selectionRadios[1].addEventListener('change', updateSubdirectoriesCheckbox);
diff --git a/processing/uiSplit.js b/processing/uiSplit.js
index de17986..74aad04 100644
--- a/processing/uiSplit.js
+++ b/processing/uiSplit.js
@@ -40,7 +40,7 @@ const splitButton = document.getElementById('split-button');
var files = [];
var splitting = false;
-const DEFAULT_SLEEP_AMOUNT = 3000;
+const DEFAULT_SLEEP_AMOUNT = 2000;
/* Disable UI elements in main window while progress bar is open and split is in progress */
@@ -113,6 +113,11 @@ function splitFiles () {
let sleepAmount = DEFAULT_SLEEP_AMOUNT;
let successesWithoutError = 0;
+ const unwrittenErrors = [];
+ let lastErrorWrite = -1;
+
+ let errorFileStream;
+
for (let i = 0; i < files.length; i++) {
/* If progress bar is closed, the split task is considered cancelled. This will contact the main thread and ask if that has happened */
@@ -129,7 +134,7 @@ function splitFiles () {
/* Let the main thread know what value to set the progress bar to */
- electron.ipcRenderer.send('set-split-bar-progress', i, 0, path.basename(files[i]));
+ electron.ipcRenderer.send('set-split-bar-progress', i, 0);
const maxLength = MAX_LENGTHS[ui.getSelectedRadioValue('max-length-radio')];
@@ -140,12 +145,36 @@ function splitFiles () {
/* Check if the optional prefix/output directory setttings are being used. If left as null, splitter will put file(s) in the same directory as the input with no prefix */
- const outputPath = uiOutput.isChecked() ? uiOutput.getOutputDir() : null;
+ let outputPath = null;
+
+ if (uiOutput.isCustomDestinationEnabled()) {
+
+ outputPath = uiOutput.getOutputDir();
+
+ if (uiOutput.isCreateSubdirectoriesEnabled() && selectionRadios[1].checked) {
+
+ const dirnames = path.dirname(files[i]).replace(/\\/g, '/').split('/');
+
+ const folderName = dirnames[dirnames.length - 1];
+
+ outputPath = path.join(outputPath, folderName);
+
+ if (!fs.existsSync(outputPath)) {
+
+ fs.mkdirSync(outputPath);
+
+ }
+
+ }
+
+ }
+
const prefix = (prefixCheckbox.checked && prefixInput.value !== '') ? prefixInput.value : null;
const response = audiomothUtils.split(files[i], outputPath, prefix, maxLength, (progress) => {
- electron.ipcRenderer.send('set-split-bar-progress', i, progress, path.basename(files[i]));
+ electron.ipcRenderer.send('set-split-bar-progress', i, progress);
+ electron.ipcRenderer.send('set-split-bar-file', i, path.basename(files[i]));
});
@@ -164,6 +193,7 @@ function splitFiles () {
/* Add error to log file */
+ unwrittenErrors.push(errorCount);
successesWithoutError = 0;
errorCount++;
errors.push(response.error);
@@ -173,31 +203,47 @@ function splitFiles () {
if (errorCount === 1) {
- const errorFileLocation = uiOutput.isChecked() ? uiOutput.getOutputDir() : path.dirname(errorFiles[0]);
-
+ const errorFileLocation = uiOutput.isCustomDestinationEnabled() ? uiOutput.getOutputDir() : path.dirname(errorFiles[0]);
errorFilePath = path.join(errorFileLocation, 'ERRORS.TXT');
+ errorFileStream = fs.createWriteStream(errorFilePath, {flags: 'a'});
+
+ errorFileStream.write('-- Split --\n');
}
- let fileContent = '';
+ const currentTime = new Date();
+ const timeSinceLastErrorWrite = currentTime - lastErrorWrite;
- for (let j = 0; j < errorCount; j++) {
+ if (timeSinceLastErrorWrite > 1000 || lastErrorWrite === -1) {
- fileContent += path.basename(errorFiles[j]) + ' - ' + errors[j] + '\n';
+ lastErrorWrite = new Date();
- }
+ const unwrittenErrorCount = unwrittenErrors.length;
- try {
+ console.log('Writing', unwrittenErrorCount, 'errors');
- fs.writeFileSync(errorFilePath, fileContent);
+ let fileContent = '';
- console.log('Error summary written to ' + errorFilePath);
+ for (let e = 0; e < unwrittenErrorCount; e++) {
- } catch (err) {
+ const unwrittenErrorIndex = unwrittenErrors.pop();
+ fileContent += path.basename(errorFiles[unwrittenErrorIndex]) + ' - ' + errors[unwrittenErrorIndex] + '\n';
- console.error(err);
- electron.ipcRenderer.send('set-split-bar-completed', successCount, errorCount, true);
- return;
+ }
+
+ try {
+
+ errorFileStream.write(fileContent);
+
+ console.log('Error summary written to ' + errorFilePath);
+
+ } catch (err) {
+
+ console.error(err);
+ electron.ipcRenderer.send('set-split-bar-completed', successCount, errorCount, true);
+ return;
+
+ }
}
@@ -208,6 +254,41 @@ function splitFiles () {
}
+ /* If any errors occurred, do a final error write */
+
+ const unwrittenErrorCount = unwrittenErrors.length;
+
+ if (unwrittenErrorCount > 0) {
+
+ console.log('Writing remaining', unwrittenErrorCount, 'errors');
+
+ let fileContent = '';
+
+ for (let e = 0; e < unwrittenErrorCount; e++) {
+
+ const unwrittenErrorIndex = unwrittenErrors.pop();
+ fileContent += path.basename(errorFiles[unwrittenErrorIndex]) + ' - ' + errors[unwrittenErrorIndex] + '\n';
+
+ }
+
+ try {
+
+ errorFileStream.write(fileContent);
+
+ console.log('Error summary written to ' + errorFilePath);
+
+ errorFileStream.end();
+
+ } catch (err) {
+
+ console.error(err);
+ electron.ipcRenderer.send('set-split-bar-completed', successCount, errorCount, true);
+ return;
+
+ }
+
+ }
+
/* Notify main thread that split is complete so progress bar is closed */
electron.ipcRenderer.send('set-split-bar-completed', successCount, errorCount, false);
@@ -262,7 +343,7 @@ selectionRadios[1].addEventListener('change', resetUI);
fileButton.addEventListener('click', () => {
- files = ui.selectRecordings(FILE_REGEX);
+ files = uiOutput.selectRecordings(FILE_REGEX);
updateInputDirectoryDisplay(files);
@@ -278,7 +359,7 @@ splitButton.addEventListener('click', () => {
}
- if ((!prefixCheckbox.checked || prefixInput.value === '') && (!uiOutput.isChecked() || uiOutput.getOutputDir() === '')) {
+ if ((!prefixCheckbox.checked || prefixInput.value === '') && (!uiOutput.isCustomDestinationEnabled() || uiOutput.getOutputDir() === '')) {
dialog.showMessageBox(currentWindow, {
type: 'error',
@@ -290,6 +371,50 @@ splitButton.addEventListener('click', () => {
}
+ /* Check if output location is the same as input */
+
+ for (let i = 0; i < files.length; i++) {
+
+ if (!prefixCheckbox.checked || prefixInput.value === '') {
+
+ /* If folder mode is enabled, the input folder is the same as the output and subdirectories are enabled, files will be overwritten as the paths will match */
+
+ if (selectionRadios[1].checked && uiOutput.isCustomDestinationEnabled() && uiOutput.isCreateSubdirectoriesEnabled()) {
+
+ /* Get the parent folder of the selected file and compare that to the output directory */
+
+ const fileFolderPath = path.dirname(path.dirname(files[i]));
+
+ if (uiOutput.getOutputDir() === fileFolderPath) {
+
+ dialog.showMessageBox(currentWindow, {
+ type: 'error',
+ title: 'Cannot downsample with current settings',
+ message: 'Output destination is the same as input destination and no prefix is selected.'
+ });
+
+ return;
+
+ }
+
+ }
+
+ if (uiOutput.getOutputDir() === path.dirname(files[i])) {
+
+ dialog.showMessageBox(currentWindow, {
+ type: 'error',
+ title: 'Cannot downsample with current settings',
+ message: 'Output destination is the same as input destination and no prefix is selected.'
+ });
+
+ return;
+
+ }
+
+ }
+
+ }
+
splitting = true;
disableUI();
diff --git a/settings/advanced.html b/settings/advanced.html
index e65fd74..5c4c8d8 100644
--- a/settings/advanced.html
+++ b/settings/advanced.html
@@ -43,7 +43,7 @@