diff --git a/js/ui.js b/js/ui.js
index 9db8c26c..bd9b37e6 100644
--- a/js/ui.js
+++ b/js/ui.js
@@ -5302,7 +5302,7 @@ async function readLabels(labelFile, updating){
// Create toast body
const toastBody = document.createElement('div');
toastBody.className = 'toast-body';
- toastBody.textContent = message; // Assuming message is defined
+ toastBody.innerHTML = message; // Assuming message is defined
// Append header and body to the wrapper
wrapper.appendChild(toastHeader);
diff --git a/js/worker.js b/js/worker.js
index a78e25c3..96738f32 100644
--- a/js/worker.js
+++ b/js/worker.js
@@ -3518,137 +3518,28 @@ async function setIncludedIDs(lat, lon, week) {
///////// Database compression and archive ////
-// async function convertAndOrganiseFiles() {
-// const db = diskDB;
-// let count = 0;
-// let {totalToConvert} = await db.getAsync('SELECT COUNT(*) as totalToConvert from files');
-// const fileProgressMap = {};
-// // Ensure 'archiveName' column exists in the files table
-// await db.runAsync("ALTER TABLE files ADD COLUMN archiveName TEXT")
-// .catch(err => {
-// if (err.message.includes("duplicate column")) {
-// DEBUG && console.log("Column 'archiveName' already exists");
-// } else {
-// console.error("Error adding 'archiveName' column:", err);
-// }
-// });
-// // Query the files table to get the necessary data
-// db.each("SELECT f.id, f.name, f.duration, f.filestart, l.place FROM files f LEFT JOIN locations l ON f.locationID = l.id", async function(err, row) {
-// if (err) {
-// console.error("Error querying the database:", err);
-// return;
-// }
-// row.place ??= STATE.place;
-// // Create the output directory structure based on place and file date
-// const fileDate = new Date(row.filestart);
-// const year = String(fileDate.getFullYear());
-// const month = fileDate.toLocaleString('default', { month: 'long' }); // Get full month name
-// //const day = ''; //String(fileDate.getDate()).padStart(2, '0');
-// const place = row.place?.replace(/[\/\\?%*:|"<>]/g, '_').trim(); // Sanitize the place name
-
-// const inputFilePath = row.name;
-// const outputDir = p.join(place, year, month);
-// const outputFileName = p.basename(inputFilePath, p.extname(inputFilePath)) + '.' + STATE.archive.format;
-// // Check if the file already exists, as is complete
-// const {archiveName} = await db.getAsync('SELECT archiveName FROM files WHERE name = ?', inputFilePath);
-// const fullPath = p.join(STATE.archive.location, outputDir)
-// const fullFilePath = p.join(fullPath, outputFileName)
-// const dbArchiveName = p.join(outputDir, outputFileName)
-// if (archiveName === dbArchiveName && fs.existsSync(fullFilePath)) {
-// totalToConvert--;
-// DEBUG && console.log(`File ${inputFilePath} already converted. Skipping conversion.`);
-// return;
-// }
-
-// if (!fs.existsSync(fullPath)) {
-// fs.mkdirSync(fullPath, { recursive: true });
-// }
-
-// // Convert the file using fluent-ffmpeg
-// let command = ffmpeg(inputFilePath)
-// if (STATE.archive.format === 'opus') {
-// command.audioBitrate('128k')
-// .audioChannels(1) // Set to mono
-// .audioFrequency(26_000) // Set sample rate for BirdNET
-// }
-// let scaleFactor = 1; // When ffmpeg reports progress, it does so against the full length of the file
-// if (STATE.detect.nocmig){
-// METADATA[inputFilePath] || await setMetadata({file: inputFilePath});
-// const boundaries = await setStartEnd(inputFilePath);
-// if (boundaries.length > 1) {
-// UI.postMessage({event: 'generate-alert', message: `Multi-day operations are not yet supported: ${inputFilePath} will not be trimmed`});
-// } else {
-// const {start, end} = boundaries[0];
-// if (start === end) return
-// command.seekInput(start).duration(end - start)
-// scaleFactor = row.duration / (end-start);
-// // Now update the duration for the truncated file to ensure accurate mtimes are set
-// row.duration = end - start;
-// }
-
-// }
-// command.output(fullFilePath)
-// .on('end', () => {
-// console.log(`Converted ${inputFilePath} to ${fullFilePath}`);
-// const newfileMtime = new Date(Math.round(row.filestart + (row.duration * 1000)));
-// utimesSync(fullFilePath, {atime: Date.now(), mtime: newfileMtime});
-// // Update the database with the new file path
-// db.run("UPDATE files SET archiveName = ? WHERE id = ?", [dbArchiveName, row.id], (err) => {
-// if (err) {
-// console.error("Error updating the database:", err);
-// } else {
-// console.log(`Updated database for file: ${inputFilePath}`);
-// }
-// count++;
-// UI.postMessage({event: 'generate-alert', message: `Finished conversion for ${inputFilePath}
-// ${count} of ${totalToConvert} completed`})
-// });
-// })
-// .on('error', (err) => {
-// count++;
-// DEBUG && console.error(`Error converting file ${inputFilePath}:`, err);
-// UI.postMessage({event: 'generate-alert', message: `File not found: ${inputFilePath}`, file: inputFilePath})
-// })
-// .on('start', function (commandLine) {
-// DEBUG && console.log('FFmpeg command: ' + commandLine);
-// })
-// .on('progress', (progress) => {
-// if (!isNaN(progress.percent)){
-// // Calculate the cumulative progress
-// fileProgressMap[inputFilePath] = progress.percent * scaleFactor;
-// console.log(`${inputFilePath} progress: ${fileProgressMap[inputFilePath].toFixed(1)}%`)
-// const values = Object.values(fileProgressMap);
-// // Calculate the sum of the values
-// const sum = values.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
-
-// // Calculate the average
-// const average = sum / values.length;
-
-// UI.postMessage({
-// event: `conversion-progress`,
-// progress: { percent: average }, // Use cumulative progress for smooth transition
-// text: `Archive file conversion progress: ${average.toFixed(1)}% `
-// });
-// }
-// })
-// .run();
-// }
-// );
-// }
const pLimit = require('p-limit');
async function convertAndOrganiseFiles(threadLimit) {
+ // SANITY checks: archive location exists and is writeable?
+ if (!fs.existsSync(STATE.archive.location)) {
+ UI.postMessage({event: 'generate-alert', type: 'error', message: `Cannot access archive location: ${STATE.archive.location}.
Operation aborted`});
+ return false;
+ }
+ try {
+ fs.accessSync(STATE.archive.location, fs.constants.W_OK);
+ } catch {
+ UI.postMessage({event: 'generate-alert', type: 'error', message: `Cannot write to archive location: ${STATE.archive.location}.
Operation aborted`});
+ return false;
+ }
threadLimit ??= 4; // Set a default
- let mkkDirFailed = false;
+ const limit = pLimit(threadLimit);
+
const db = diskDB;
- let count = 0;
- let {totalToConvert} = await db.getAsync('SELECT COUNT(*) as totalToConvert from files');
const fileProgressMap = {};
- const limit = pLimit(threadLimit); // Set the limit based on the number of threads
const conversions = []; // Array to hold the conversion promises
-
// Ensure 'archiveName' column exists in the files table
await db.runAsync("ALTER TABLE files ADD COLUMN archiveName TEXT")
.catch(err => {
@@ -3656,11 +3547,13 @@ async function convertAndOrganiseFiles(threadLimit) {
DEBUG && console.log("Column 'archiveName' already exists");
} else {
console.error("Error adding 'archiveName' column:", err);
+ return
}
});
// Query the files table to get the necessary data
const rows = await db.allAsync("SELECT f.id, f.name, f.duration, f.filestart, l.place FROM files f LEFT JOIN locations l ON f.locationID = l.id");
+
for (const row of rows){
row.place ??= STATE.place;
const fileDate = new Date(row.filestart);
@@ -3671,13 +3564,21 @@ async function convertAndOrganiseFiles(threadLimit) {
const inputFilePath = row.name;
const outputDir = p.join(place, year, month);
const outputFileName = p.basename(inputFilePath, p.extname(inputFilePath)) + '.' + STATE.archive.format;
- const {archiveName} = await db.getAsync('SELECT archiveName FROM files WHERE name = ?', inputFilePath);
const fullPath = p.join(STATE.archive.location, outputDir);
const fullFilePath = p.join(fullPath, outputFileName);
const dbArchiveName = p.join(outputDir, outputFileName);
+ // Does the file we want to convert exist?
+ if (!fs.existsSync(inputFilePath)) {
+ UI.postMessage({
+ event: 'generate-alert', type: 'Warning',
+ message: `Cannot access: ${inputFilePath}
Skipping conversion.`
+ });
+ continue;
+ }
+
+ const {archiveName} = await db.getAsync('SELECT archiveName FROM files WHERE name = ?', inputFilePath);
if (archiveName === dbArchiveName && fs.existsSync(fullFilePath)) {
- totalToConvert--;
DEBUG && console.log(`File ${inputFilePath} already converted. Skipping conversion.`);
continue;
}
@@ -3686,21 +3587,17 @@ async function convertAndOrganiseFiles(threadLimit) {
try {
fs.mkdirSync(fullPath, { recursive: true });
} catch (err) {
- if (!mkkDirFailed){
- mkkDirFailed = true;
- UI.postMessage({
- event: 'generate-alert', type: 'error',
- message: `Failed to create directory: ${fullPath}
Error: ${err.message}`
- });
- }
- totalToConvert--;
+ UI.postMessage({
+ event: 'generate-alert', type: 'error',
+ message: `Failed to create directory: ${fullPath}
Error: ${err.message}`
+ });
continue;
}
}
// Add the file conversion to the pool
fileProgressMap[inputFilePath] = 0;
- conversions.push(limit(() => convertFile(inputFilePath, fullFilePath, row, db, dbArchiveName, fileProgressMap, totalToConvert, count++)));
+ conversions.push(limit(() => convertFile(inputFilePath, fullFilePath, row, db, dbArchiveName, fileProgressMap)));
console.log(conversions)
}
@@ -3741,10 +3638,11 @@ async function convertAndOrganiseFiles(threadLimit) {
event: `generate-alert`,
message: summaryMessage
});
+ console.log('Conversion finished:', conversions)
})
};
-async function convertFile(inputFilePath, fullFilePath, row, db, dbArchiveName, fileProgressMap, totalToConvert, count) {
+async function convertFile(inputFilePath, fullFilePath, row, db, dbArchiveName, fileProgressMap) {
METADATA[inputFilePath] || await setMetadata({file: inputFilePath});
const boundaries = await setStartEnd(inputFilePath);
@@ -3763,7 +3661,10 @@ async function convertFile(inputFilePath, fullFilePath, row, db, dbArchiveName,
UI.postMessage({event: 'generate-alert', type: 'warning', message: `Multi-day operations are not yet supported: ${inputFilePath} will not be trimmed`});
} else {
const {start, end} = boundaries[0];
- if (start === end) return;
+ if (start === end) {
+ UI.postMessage({event: 'generate-alert', type: 'warning', message: `${inputFilePath} will not be added to the archive as it is entirely during daylight.`});
+ return resolve();
+ }
command.seekInput(start).duration(end - start);
scaleFactor = row.duration / (end-start);
row.duration = end - start;
@@ -3781,14 +3682,13 @@ async function convertFile(inputFilePath, fullFilePath, row, db, dbArchiveName,
if (err) {
console.error("Error updating the database:", err);
} else {
- UI.postMessage({event: 'generate-alert', message: `Finished conversion for ${inputFilePath}
${count} of ${totalToConvert} completed`});
+ UI.postMessage({event: 'generate-alert', message: `Finished conversion for ${inputFilePath}`});
}
resolve();
});
})
.on('error', (err) => {
- DEBUG && console.error(`Error converting file ${inputFilePath}:`, err);
- UI.postMessage({event: 'generate-alert', type: 'error', message: `File not found: ${inputFilePath}`, file: inputFilePath});
+ UI.postMessage({event: 'generate-alert', type: 'error', message: `Error converting file ${inputFilePath}:`, err});
reject(err);
})
.on('progress', (progress) => {