1- // main.js
1+ // --- main.js (полный код с изменениями) ---
22
33const { app, BrowserWindow, ipcMain, Menu, clipboard, dialog, shell, protocol } = require ( 'electron' ) ;
44const path = require ( 'path' ) ;
@@ -13,6 +13,7 @@ const WebSocket = require('ws');
1313const crypto = require ( 'crypto' ) ;
1414const ffmpeg = require ( '@ffmpeg-installer/ffmpeg' ) ;
1515const keytar = require ( 'keytar' ) ;
16+ const { autoUpdater } = require ( 'electron-updater' ) ;
1617
1718// Отключаем sandbox для Linux, чтобы избежать проблем с AppImage
1819if ( process . platform === 'linux' ) {
@@ -124,7 +125,7 @@ async function startRecording(camera) {
124125 ffmpegProcess . on ( 'close' , ( code ) => {
125126 console . log ( `[REC FFMPEG] Finished for "${ camera . name } " with code ${ code } .` ) ;
126127 delete recordingManager [ camera . id ] ;
127- if ( mainWindow ) {
128+ if ( mainWindow && ! mainWindow . isDestroyed ( ) ) {
128129 mainWindow . webContents . send ( 'recording-state-change' , {
129130 cameraId : camera . id ,
130131 recording : false ,
@@ -134,7 +135,7 @@ async function startRecording(camera) {
134135 }
135136 } ) ;
136137 console . log ( `[REC] Starting for "${ camera . name } " to ${ outputPath } ` ) ;
137- if ( mainWindow ) mainWindow . webContents . send ( 'recording-state-change' , { cameraId : camera . id , recording : true } ) ;
138+ if ( mainWindow && ! mainWindow . isDestroyed ( ) ) mainWindow . webContents . send ( 'recording-state-change' , { cameraId : camera . id , recording : true } ) ;
138139 return { success : true } ;
139140}
140141
@@ -203,6 +204,13 @@ function createWindow() {
203204 webPreferences : { preload : path . join ( __dirname , 'preload.js' ) }
204205 } ) ;
205206 mainWindow . loadFile ( 'index.html' ) ;
207+
208+ mainWindow . once ( 'ready-to-show' , ( ) => {
209+ if ( app . isPackaged ) {
210+ console . log ( '[Updater] App ready, checking for updates...' ) ;
211+ autoUpdater . checkForUpdates ( ) ;
212+ }
213+ } ) ;
206214}
207215
208216function createFileManagerWindow ( camera ) {
@@ -387,7 +395,7 @@ ipcMain.handle('load-configuration', async () => {
387395 cameras : [ ] ,
388396 groups : [ ] ,
389397 layout : { cols : 2 , rows : 2 } ,
390- gridState : [ null , null , null , null ]
398+ gridState : Array ( 64 ) . fill ( null )
391399 } ;
392400 let config = defaultConfig ;
393401 let needsResave = false ;
@@ -408,6 +416,9 @@ ipcMain.handle('load-configuration', async () => {
408416 await fsPromises . access ( configPath ) ;
409417 const data = await fsPromises . readFile ( configPath , 'utf-8' ) ;
410418 config = { ...defaultConfig , ...JSON . parse ( data ) } ;
419+ if ( ! config . gridState || config . gridState . length < 64 ) {
420+ config . gridState = Array ( 64 ) . fill ( null ) ;
421+ }
411422 } catch ( e ) {
412423 const migratedConfig = await migrateOldFile ( ) ;
413424 if ( migratedConfig ) {
@@ -779,6 +790,63 @@ ipcMain.handle('show-recording-in-folder', async (event, filename) => {
779790 }
780791} ) ;
781792
793+ // НОВЫЙ ОБРАБОТЧИК ДЛЯ РУЧНОЙ ПРОВЕРКИ
794+ ipcMain . handle ( 'check-for-updates' , ( ) => {
795+ if ( ! app . isPackaged ) {
796+ console . log ( '[Updater] Skipping update check in dev mode.' ) ;
797+ if ( mainWindow ) {
798+ mainWindow . webContents . send ( 'update-status' , { status : 'error' , message : 'Проверка обновлений доступна только в установленной версии.' } ) ;
799+ }
800+ return ;
801+ }
802+ console . log ( '[Updater] Manual update check initiated.' ) ;
803+ autoUpdater . checkForUpdates ( ) ;
804+ } ) ;
805+
806+ // ОБРАБОТЧИКИ СОБЫТИЙ AUTOUPDATER
807+ autoUpdater . on ( 'update-available' , ( info ) => {
808+ console . log ( '[Updater] Update available.' , info ) ;
809+ if ( mainWindow && ! mainWindow . isDestroyed ( ) ) mainWindow . webContents . send ( 'update-status' , { status : 'available' , message : `Доступна версия ${ info . version } ` } ) ;
810+ } ) ;
811+
812+ autoUpdater . on ( 'update-not-available' , ( info ) => {
813+ console . log ( '[Updater] No new update available.' ) ;
814+ if ( mainWindow && ! mainWindow . isDestroyed ( ) ) mainWindow . webContents . send ( 'update-status' , { status : 'latest' , message : 'У вас последняя версия.' } ) ;
815+ } ) ;
816+
817+ autoUpdater . on ( 'error' , ( err ) => {
818+ console . error ( '[Updater] Error:' , err ? ( err . stack || err ) : 'unknown error' ) ;
819+ if ( mainWindow && ! mainWindow . isDestroyed ( ) ) mainWindow . webContents . send ( 'update-status' , { status : 'error' , message : `Ошибка обновления: ${ err . message } ` } ) ;
820+ } ) ;
821+
822+ autoUpdater . on ( 'download-progress' , ( progressObj ) => {
823+ let log_message = "Download speed: " + progressObj . bytesPerSecond ;
824+ log_message = log_message + ' - Downloaded ' + progressObj . percent . toFixed ( 2 ) + '%' ;
825+ log_message = log_message + ' (' + progressObj . transferred + "/" + progressObj . total + ')' ;
826+ console . log ( '[Updater] ' + log_message ) ;
827+ if ( mainWindow && ! mainWindow . isDestroyed ( ) ) mainWindow . webContents . send ( 'update-status' , {
828+ status : 'downloading' ,
829+ message : `Загрузка... ${ progressObj . percent . toFixed ( 0 ) } %`
830+ } ) ;
831+ } ) ;
832+
833+ autoUpdater . on ( 'update-downloaded' , ( info ) => {
834+ console . log ( '[Updater] Update downloaded.' , info ) ;
835+ if ( mainWindow && ! mainWindow . isDestroyed ( ) ) mainWindow . webContents . send ( 'update-status' , { status : 'downloaded' , message : `Версия ${ info . version } загружена. Перезапустите для установки.` } ) ;
836+
837+ dialog . showMessageBox ( mainWindow , {
838+ type : 'info' ,
839+ title : 'Обновление готово' ,
840+ message : 'Новая версия загружена. Перезапустить приложение сейчас, чтобы установить обновление?' ,
841+ buttons : [ 'Перезапустить' , 'Позже' ] ,
842+ defaultId : 0
843+ } ) . then ( ( { response } ) => {
844+ if ( response === 0 ) {
845+ autoUpdater . quitAndInstall ( ) ;
846+ }
847+ } ) ;
848+ } ) ;
849+
782850app . whenReady ( ) . then ( ( ) => {
783851 protocol . registerFileProtocol ( 'video-archive' , async ( request , callback ) => {
784852 const settings = await getAppSettings ( ) ;
0 commit comments