From 408494564f63d5647ca51608f6e9aa1984023fff Mon Sep 17 00:00:00 2001 From: mattk70 Date: Mon, 19 Feb 2024 22:16:40 +0000 Subject: [PATCH] removed domid from generateToast parameters. they all use the same element. Audio notification for long running analyses - using system notifications if permitted Default Mac Menu is replaced with minimal one - has a chirpity about section. When creating a manual record, the new record is centred and highlighted on the spectrogram and in the results table. Charts fixed --- Help/notification.mp3 | Bin 0 -> 13790 bytes build/icon.icns | Bin 0 -> 147750 bytes index.html | 25 +++++-- js/state.js | 2 +- js/ui.js | 157 +++++++++++++++++++++++------------------- js/worker.js | 98 +++++++++++++++++++------- main.js | 20 +++++- package.json | 5 +- 8 files changed, 200 insertions(+), 107 deletions(-) create mode 100644 Help/notification.mp3 create mode 100644 build/icon.icns diff --git a/Help/notification.mp3 b/Help/notification.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..dd03334922e80e9f5c86aca5a9fcc80b94ce08b2 GIT binary patch literal 13790 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
    C}v6tH-_qBzjINdlsM9WZVBWk??KBt(eaY{$u4Z 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
    -
    +
    Downloading update: @@ -1227,6 +1237,9 @@
    Chirpity Nocmig © Matthew Kirkland
    + @@ -1336,7 +1349,7 @@ } - + \ No newline at end of file diff --git a/js/state.js b/js/state.js index c3f12962..988c257d 100644 --- a/js/state.js +++ b/js/state.js @@ -13,7 +13,7 @@ export class State { this.filteredOffset = {}, // Current species start number for filtered results this.selection = false, this.blocked = [], - this.audio = { gain: 0, format: 'mp3', bitrate: 128, padding: false, fade: false, downmix: false, quality: 5 }, + this.audio = { gain: 0, format: 'mp3', bitrate: 128, padding: false, fade: false, downmix: false, quality: 5, notification: true }, this.filters = { active: false, highPassFrequency: 0, lowShelfFrequency: 0, lowShelfAttenuation: 0, SNR: 0 }, this.detect = { nocmig: false, contextAware: false, confidence: 450 }, this.chart = { range: { start: undefined, end: undefined }, species: undefined }, diff --git a/js/ui.js b/js/ui.js index 1f6996e0..212a1e6d 100644 --- a/js/ui.js +++ b/js/ui.js @@ -112,6 +112,7 @@ const DOM = { audioPadding: document.getElementById('padding'), audioQuality: document.getElementById('quality'), audioQualityContainer: document.getElementById('quality-container'), + audioNotification: document.getElementById('audio-notification'), batchSizeSlider: document.getElementById('batch-size'), batchSizeValue: document.getElementById('batch-size-value'), colourmap: document.getElementById('colourmap'), @@ -889,7 +890,7 @@ function postAnalyseMessage(args) { function fetchLocationAddress(lat, lon) { if (isNaN(lat) || isNaN(lon || lat === '' || lon === '')){ - generateToast({domID:'toastContainer', message:'Both lat and lon values need to be numbers between 180 and -180'}) + generateToast({ message:'Both lat and lon values need to be numbers between 180 and -180'}) return false } return new Promise((resolve, reject) => { @@ -1393,7 +1394,7 @@ window.onload = async () => { tensorflow: { threads: DIAGNOSTICS['Cores'], batchSize: 32 }, webgpu: { threads: 2, batchSize: 32 }, webgl: { threads: 2, batchSize: 32 }, - audio: { gain: 0, format: 'mp3', bitrate: 192, quality: 5, downmix: false, padding: false, fade: false }, + audio: { gain: 0, format: 'mp3', bitrate: 192, quality: 5, downmix: false, padding: false, fade: false, notification: true }, limit: 500, track: true, debug: false @@ -1443,7 +1444,8 @@ window.onload = async () => { // Show Locale document.getElementById('locale').value = config[config.model].locale; - + // remember audio notification setting + DOM.audioNotification.checked = config.audio.notification; config.list === 'location' ? speciesThresholdEl.classList.remove('d-none') : speciesThresholdEl.classList.add('d-none'); @@ -1605,38 +1607,20 @@ const setUpWorkerMessaging = () => { } case "generate-alert": { if (args.updateFilenamePanel) { - renderFilenamePanel(); - window.electron.unsavedRecords(false); - document.getElementById("unsaved-icon").classList.add("d-none"); - } - if (args.file) { - generateToast({domID:'toastContainer', message: args.message}); - } else { - if (args.filter) { - worker.postMessage({ - action: "filter", - species: isSpeciesViewFiltered(true), - active: args.active, - updateSummary: true - }); - resetResults({ - clearSummary: true, - clearPagination: true, - clearResults: true - }); - } else { - generateToast({domID:'toastContainer', message: args.message}); - } + renderFilenamePanel(); + window.electron.unsavedRecords(false); + document.getElementById("unsaved-icon").classList.add("d-none"); } + generateToast({ message: args.message}); break; - } - case "results-complete": {onResultsComplete(args); - hideLoadingSpinner(); - break; - } - case "labels": { - LABELS = args.labels; - break } + } + case "results-complete": {onResultsComplete(args); + hideLoadingSpinner(); + break; + } + case "labels": { + LABELS = args.labels; + break } case "location-list": {LOCATIONS = args.locations; locationID = args.currentLocation; break; @@ -1665,39 +1649,39 @@ const setUpWorkerMessaging = () => { } case "seen-species-list": {generateBirdList("seenSpecies", args.list); break; + } + case "valid-species-list": {populateSpeciesModal(args.included, args.excluded); + break; + } + case "show-spinner": {showLoadingSpinner(500); + break; + } + // case "spawning": {displayWarmUpMessage(); + // break; + // } + case "total-records": {updatePagination(args.total, args.offset); + break; + } + case "unsaved-records": {window.electron.unsavedRecords(true); + document.getElementById("unsaved-icon").classList.remove("d-none"); + break; + } + case "update-audio-duration": {DIAGNOSTICS["Audio Duration"] ??= 0; + DIAGNOSTICS["Audio Duration"] += args.value; + break; + } + case "update-summary": {updateSummary(args); + break; + } + case "worker-loaded-audio": { + onWorkerLoadedAudio(args); + break; + } + default: {generateToast({ message:`Unrecognised message from worker:${args.event}`}); + } } - case "valid-species-list": {populateSpeciesModal(args.included, args.excluded); - break; - } - case "show-spinner": {showLoadingSpinner(500); - break; - } - // case "spawning": {displayWarmUpMessage(); - // break; - // } - case "total-records": {updatePagination(args.total, args.offset); - break; - } - case "unsaved-records": {window.electron.unsavedRecords(true); - document.getElementById("unsaved-icon").classList.remove("d-none"); - break; - } - case "update-audio-duration": {DIAGNOSTICS["Audio Duration"] ??= 0; - DIAGNOSTICS["Audio Duration"] += args.value; - break; - } - case "update-summary": {updateSummary(args); - break; - } - case "worker-loaded-audio": { - onWorkerLoadedAudio(args); - break; - } - default: {generateToast({domID:'toastContainer', message:`Unrecognised message from worker:${args.event}`}); -} -} -}) -}) + }) + }) } function generateBirdList(store, rows) { @@ -2399,7 +2383,7 @@ function onChartData(args) { threads: config[config.backend].threads, list: config.list }); - generateToast({domID:'toastContainer', message:'Operation cancelled'}); + generateToast({ message:'Operation cancelled'}); DOM.progressDiv.classList.add('d-none'); } }, @@ -2614,7 +2598,7 @@ function onChartData(args) { seconds = Math.min(parseFloat(timeArray[2]), 59.999); } else { // Invalid input - generateToast({domID:'toastContainer', message:'Invalid time format. Please enter time in one of the following formats: \n1. Float (for seconds) \n2. Two numbers separated by a colon (for minutes and seconds) \n3. Three numbers separated by colons (for hours, minutes, and seconds)'}); + generateToast({ message:'Invalid time format. Please enter time in one of the following formats: \n1. Float (for seconds) \n2. Two numbers separated by a colon (for minutes and seconds) \n3. Three numbers separated by colons (for hours, minutes, and seconds)'}); return; } let start = hours * 3600 + minutes * 60 + seconds; @@ -2863,6 +2847,7 @@ function onChartData(args) { track(`${config.model}-${config.backend}`, 'Audio Duration', config.backend, Math.round(DIAGNOSTICS['Audio Duration'])); track(`${config.model}-${config.backend}`, 'Analysis Duration', config.backend, parseInt(analysisTime)); track(`${config.model}-${config.backend}`, 'Analysis Rate', config.backend, parseInt(rate)); + generateToast({ message:'Analysis complete.'}) } /* @@ -2986,7 +2971,7 @@ function onChartData(args) { let tr = ''; if (typeof (result) === 'string') { // const nocturnal = config.detect.nocmig ? 'during the night' : ''; - generateToast({domID:'toastContainer', message: result}); + generateToast({ message: result}); return } if (index <= 1) { @@ -3240,7 +3225,7 @@ function onChartData(args) { }) } else { if (!config.seenThanks) { - generateToast({domID:'toastContainer', message:'Thank you, your feedback helps improve Chirpity predictions'}); + generateToast({ message:'Thank you, your feedback helps improve Chirpity predictions'}); config.seenThanks = true; updatePrefs() } @@ -4062,7 +4047,7 @@ DOM.gain.addEventListener('input', () => { case 'species-frequency-threshold' : { if (isNaN(element.value) || element.value === '') { - generateToast({domID:'toastContainer', message:'The threshold must be a number between 0.001 and 1'}); + generateToast({ message:'The threshold must be a number between 0.001 and 1'}); return false } config.speciesThreshold = element.value; @@ -4107,6 +4092,10 @@ DOM.gain.addEventListener('input', () => { handleSNRchange(e); break; } + case 'audio-notification': { + config.audio.notification = element.checked; + break; + } case 'species-week': { config.useWeek = element.checked; @@ -4452,6 +4441,7 @@ function track(event, action, name, value){ const insertManualRecord = (cname, start, end, comment, count, label, action, batch, originalCname, confidence) => { + resetResults({clearPagination: false}) const files = batch ? fileList : currentFile; worker.postMessage({ action: 'insert-manual-record', @@ -4639,8 +4629,8 @@ function track(event, action, name, value){ } } - function generateToast({domID, message}) { - const domEl = document.getElementById(domID); + function generateToast({message}) { + const domEl = document.getElementById('toastContainer'); const wrapper = document.createElement('div'); // Add toast attributes @@ -4663,6 +4653,29 @@ function track(event, action, name, value){ domEl.appendChild(wrapper) const toast = new bootstrap.Toast(wrapper) toast.show() + if (message === 'Analysis complete.'){ + const duration = parseFloat(DIAGNOSTICS['Analysis Duration'].replace(' seconds', '')); + if (config.audio.notification && duration > 30){ + if (Notification.permission === "granted") { + // Check whether notification permissions have already been granted; + // if so, create a notification + const notification = new Notification(`Analysis completed in ${duration.toFixed(0)} seconds`, {requireInteraction: true}); + // … + } else if (Notification.permission !== "denied") { + // We need to ask the user for permission + Notification.requestPermission().then((permission) => { + // If the user accepts, let's create a notification + if (permission === "granted") { + const notification = new Notification(`Analysis completed in ${duration.toFixed(0)} seconds`, {requireInteraction: true}); + // … + } + }); + } else { + notificationSound = document.getElementById('notification'); + notificationSound.play() + } + } + } } function parseSemVer(versionString) { diff --git a/js/worker.js b/js/worker.js index ab86c8f4..3cfa507f 100644 --- a/js/worker.js +++ b/js/worker.js @@ -1862,7 +1862,8 @@ const prepSummaryStatement = (included) => { const updatedID = db.getAsync('SELECT id FROM species WHERE cname = ?', cname); let count = 0; await db.runAsync('BEGIN'); - for (const item of records) { + for (let i = 0; i< records.length; i++) { + const item = records[i]; const { dateTime, speciesID, fileID, position, end, comment, callCount } = item; const { name } = await STATE.db.getAsync('SELECT name FROM files WHERE id = ?', fileID) // Delete existing record @@ -1876,18 +1877,19 @@ const prepSummaryStatement = (included) => { file: name, label: label, batch: false, - originalCname: undefined + originalCname: undefined, + updateResults: i === records.length -1 // trigger a UI update after the last item }) } await db.runAsync('END'); DEBUG && console.log(`Batch record update took ${(Date.now() - t0) / 1000} seconds`) } - const onInsertManualRecord = async ({ cname, start, end, comment, count, file, label, batch, originalCname, confidence, speciesFiltered }) => { + const onInsertManualRecord = async ({ cname, start, end, comment, count, file, label, batch, originalCname, confidence, speciesFiltered, updateResults = true }) => { if (batch) return batchInsertRecords(cname, label, file, originalCname) start = parseFloat(start), end = parseFloat(end); const startMilliseconds = Math.round(start * 1000); - let changes, fileID, fileStart; + let changes = 0, fileID, fileStart; const db = STATE.db; const { speciesID } = await db.getAsync(`SELECT id as speciesID FROM species WHERE cname = ?`, cname); let res = await db.getAsync(`SELECT id,filestart FROM files WHERE name = ?`, file); @@ -1919,15 +1921,12 @@ const prepSummaryStatement = (included) => { if (response.changes){ STATE.db === diskDB ? UI.postMessage({ event: 'diskDB-has-records' }) : UI.postMessage({event: 'unsaved-records'}); } - // WHY NOT USE FILTER DIRECTLY? It's to get species and offset - // UI.postMessage({ - // event: 'generate-alert', - // // message: `${count} ${args.cname} record has been saved to the archive.`, - // filter: true, - // active: active - // }) - await getResults({species:speciesFiltered, select: start}); - await getSummary({species: speciesFiltered}); + if (updateResults){ + const select = {start: start, dateTime: dateTime}; + await getResults({species:speciesFiltered, select: select}); + await getSummary({species: speciesFiltered}); + } + return changes; } const generateInsertQuery = async (latestResult, file) => { @@ -2282,6 +2281,58 @@ const prepSummaryStatement = (included) => { }) }; + + const getPosition = async ({species = undefined, dateTime = undefined, included = []} = {}) => { + const params = [STATE.detect.confidence]; + let positionStmt = ` + WITH ranked_records AS ( + SELECT + dateTime, + RANK() OVER (PARTITION BY records.dateTime ORDER BY records.confidence DESC) AS rank + FROM records + JOIN species ON records.speciesID = species.id + JOIN files ON records.fileID = files.id + WHERE confidence >= ? + `; + // might have two locations with same dates - so need to add files + if (['analyse', 'archive'].includes(STATE.mode) && !STATE.selection) { + positionStmt += ` AND name IN (${prepParams(STATE.filesToAnalyse)}) `; + params.push(...STATE.filesToAnalyse) + } + // Prioritise selection ranges + const range = STATE.selection?.start ? STATE.selection : + STATE.mode === 'explore' ? STATE.explore.range : false; + const useRange = range?.start; + if (useRange) { + positionStmt += ` AND dateTime BETWEEN ${range.start} AND ${range.end} `; + params.push(range.start,range.end) + } + if (filtersApplied(included)){ + const included = await getIncludedIDs(); + positionStmt += ` AND speciesID IN (${prepParams(included)}) `; + params.push(...STATE.included) + } + if (STATE.locationID) { + positionStmt += ` AND locationID = ${STATE.locationID} `; + params.push(STATE.locationID) + } + if (STATE.detect.nocmig){ + positionStmt += ' AND COALESCE(isDaylight, 0) != 1 '; // Backward compatibility for < v0.9. + } + + positionStmt += `) + SELECT + count(*) as count, dateTime + FROM ranked_records + WHERE rank <= ? AND dateTime < ?`; + params.push(STATE.topRankin, dateTime); + if (species) { + positionStmt+= ` AND cname = ? `; + params.push(species) + }; + const {count} = await STATE.db.getAsync(positionStmt, ...params); + return count + } /** * @@ -2306,21 +2357,18 @@ const prepSummaryStatement = (included) => { select = undefined } = {}) => { let confidence = STATE.detect.confidence; - if (offset === undefined) { // Get offset state - if (species) { - if (!STATE.filteredOffset[species]) STATE.filteredOffset[species] = 0; - offset = STATE.filteredOffset[species]; - } else { - offset = STATE.globalOffset; - } - } else { // Set offset state - if (species) STATE.filteredOffset[species] = offset; - else STATE.update({ globalOffset: offset }); + const included = STATE.selection ? [] : await getIncludedIDs(); + if (select) { + const position = await getPosition({species: species, dateTime: select.dateTime, included: included}); + offset = Math.floor(position/limit) * limit; } + offset = offset ?? (species ? (STATE.filteredOffset[species] ?? 0) : STATE.globalOffset); + if (species) STATE.filteredOffset[species] = offset; + else STATE.update({ globalOffset: offset }); + let index = offset; AUDACITY = {}; - const included = STATE.selection ? [] : await getIncludedIDs(); const params = getResultsParams(species, confidence, offset, limit, topRankin, included); prepResultsStatement(species, limit === Infinity, included); @@ -2372,7 +2420,7 @@ const prepSummaryStatement = (included) => { } } } - STATE.selection || UI.postMessage({event: 'results-complete', active: active, select: select}); + STATE.selection || UI.postMessage({event: 'results-complete', active: active, select: select?.start}); }; // Function to format the CSV export diff --git a/main.js b/main.js index f4591846..7fd8fbe2 100644 --- a/main.js +++ b/main.js @@ -1,4 +1,4 @@ -const { app, dialog, ipcMain, MessageChannelMain, BrowserWindow, globalShortcut } = require('electron'); +const { app, Menu, dialog, ipcMain, MessageChannelMain, BrowserWindow, globalShortcut } = require('electron'); const { autoUpdater } = require("electron-updater") const log = require('electron-log'); @@ -24,6 +24,22 @@ console.error = log.error; autoUpdater.logger = log; autoUpdater.logger.transports.file.level = 'info'; +const menu = Menu.buildFromTemplate([{ + label: app.name, + submenu: [ + { role: 'about' }, + { type: 'separator' }, + { role: 'services' }, + { type: 'separator' }, + { role: 'hide' }, + { role: 'hideOthers' }, + { role: 'unhide' }, + { type: 'separator' }, + { role: 'quit' } + ] + }]); + +Menu.setApplicationMenu(menu); // Updates // Function to fetch release notes from GitHub API async function fetchReleaseNotes(version) { @@ -323,6 +339,7 @@ async function createWorker() { // This method will be called when Electron has finished loading app.whenReady().then(async () => { + ipcMain.handle('getPath', () => app.getPath('userData')); ipcMain.handle('getTemp', () => app.getPath('temp')); ipcMain.handle('getVersion', () => app.getVersion()); @@ -357,6 +374,7 @@ app.whenReady().then(async () => { }) } else { // Quit when all windows are closed. + app.setAppUserModelId('chirpity') app.on('window-all-closed', () => { app.quit() }) diff --git a/package.json b/package.json index 8686e79a..32de4648 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "chirpity", - "version": "1.1.0", + "version": "1.2.0", "description": "Chirpity Nocmig", "main": "main.js", "scripts": { @@ -14,6 +14,7 @@ "url": "git+https://github.com/mattk70/Chirpity-Electron.git" }, "build": { + "appId": "com.electron.chirpity", "publish": [ { "provider": "github", @@ -108,7 +109,7 @@ "!node_modules/ffprobe-static-electron/bin/win${/*}", "!node_modules/ffprobe-static-electron/bin/linux${/*}" ], - "icon": "./img/icon/icon.icns", + "icon": "img/icon/icon.icns", "category": "public.app-category.utilities", "fileAssociations": [ {