From f40c4af1397f4226d33438cc4db4701de18c03bf Mon Sep 17 00:00:00 2001 From: justsomedude Date: Wed, 30 Sep 2020 12:40:16 +0200 Subject: [PATCH 01/43] added syncing script and needed extras --- .gitignore | 1 + config/drivename.conf | 5 ++ install-raspbian.sh | 4 + sync-to-drive.js | 185 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 195 insertions(+) create mode 100644 config/drivename.conf create mode 100755 sync-to-drive.js diff --git a/.gitignore b/.gitignore index c5fa957f3..b043d7a1b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ archives/ bower_components/ config/* !config/config.inc.php +!config/drivename.conf data/ digicamcontrol/ manual/faq.html diff --git a/config/drivename.conf b/config/drivename.conf new file mode 100644 index 000000000..e215fa4c8 --- /dev/null +++ b/config/drivename.conf @@ -0,0 +1,5 @@ +# This file is used by the sync-to-drive script! +# It supports comments in this fassion, also behind lines! +# Currently this formats are supported: sdX, /dev/sdX and simply the label of the drive (not the manufacturer) +# In the default config the script will only search for drives with the label photobooth! +photobooth \ No newline at end of file diff --git a/install-raspbian.sh b/install-raspbian.sh index cef3b3ccd..f44ee5b6a 100755 --- a/install-raspbian.sh +++ b/install-raspbian.sh @@ -53,6 +53,7 @@ COMMON_PACKAGES=( 'php-gd' 'php-zip' 'yarn' + 'rsync' ) apache_webserver() { @@ -317,6 +318,9 @@ cat >> /boot/config.txt << EOF dtoverlay=gpio-no-irq EOF +info "### Adding rsync cronjob for file backup" +crontab -l | { cat; echo "*/5 * * * * cd ${INSTALLFOLDERPATH} && ./sync-to-drive.js"; } | crontab - + info "### Congratulations you finished the install process." info "### Have fun with your Photobooth, but first restart your Pi." diff --git a/sync-to-drive.js b/sync-to-drive.js new file mode 100755 index 000000000..546820f4b --- /dev/null +++ b/sync-to-drive.js @@ -0,0 +1,185 @@ +#!/usr/bin/env node +/* eslint-disable node/shebang */ + +const {execSync, spawn} = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +//This script needs to be run from within the photobooth directory +const BASE_DIR = __dirname; +const CONFIG_DIR_NAME = 'config'; +const CONFIG_FILE_NAME = 'drivename.conf'; +const DATA_DIR_NAME = 'data'; +const PLATFORM = process.platform; + +const parseConfig = () => { + const confPath = path.join(BASE_DIR, CONFIG_DIR_NAME, CONFIG_FILE_NAME); + + if (!fs.existsSync(confPath)) { + console.log(`ERROR: Couldn't find the config file: ${confPath}`); + + return null; + } + //Should be checked, becuase the behavior of fs.readFileSync() is platform-specific https://nodejs.org/api/fs.html#fs_fs_readfile_path_options_callback + if (fs.lstatSync(confPath).isDirectory()) { + console.log(`ERROR: ${confPath} is a directory!`); + + return null; + } + + const fileContent = fs.readFileSync(confPath, {encoding: 'utf8'}); + const split = fileContent.split('\n').map((line) => line.replace('\r', '')); + + return split.reduce((arr, line) => { + if (line && !line.startsWith('#')) { + const trimmed = line.trim(); + if (trimmed.includes('#')) { + arr.push(trimmed.substr(0, trimmed.indexOf('#'))); + } else { + arr.push(trimmed); + } + } + + return arr; + }, []); +}; + +const getDriveInfos = (drives) => { + //Assuming that the lsblk version supports JSON output! + let json = null; + + try { + const output = execSync('export LC_ALL=C; lsblk -ablJO 2>/dev/null; unset LC_ALL').toString(); + json = JSON.parse(output); + } catch (err) { + console.log( + 'ERROR: Could not parse the output of lsblk! Please make sure its installed and that it offers JSON output!' + ); + + return null; + } + + if (!json || !json.blockdevices) { + console.log('ERROR: The output of lsblk was malformed!'); + + return null; + } + + return json.blockdevices.reduce((arr, blk) => { + if ( + drives.some((drive) => drive === blk.name || drive === blk.kname || drive === blk.path || drive === blk.label) + ) { + arr.push(blk); + } + + return arr; + }, []); +}; + +const mountDrives = (drives) => { + const result = []; + + for (const drive of drives) { + if (!drive.mountpoint) { + try { + const mountRes = execSync(`export LC_ALL=C; udisksctl mount -b ${drive.path}; unset LC_ALL`).toString(); + const mountPoint = mountRes + .substr(mountRes.indexOf('at') + 3) + .trim() + .replace('\n'); + + drive.mountpoint = mountPoint; + } catch (error) { + console.log(`ERROR: Count mount ${drive.path}`); + } + } + result.push(drive); + } + + return result; +}; + +const startSync = (drives) => { + const dataPath = path.join(BASE_DIR, DATA_DIR_NAME); + + if (!fs.existsSync(dataPath)) { + console.log(`ERROR: Folder ${dataPath} doesn't exist!`); + + return; + } + + for (const drive of drives) { + const cmd = (() => { + switch (process.platform) { + case 'win32': + return null; + case 'linux': + return [ + 'rsync', + '-a', + '--delete', + '-b', + `--backup-dir=${path.join(drive.mountpoint, 'backup')}`, + dataPath, + path.join(drive.mountpoint) + ].join(' '); + default: + return null; + } + })(); + + console.log('Executing:', cmd); + + try { + const spwndCmd = spawn(cmd, { + detached: true, + shell: true, + stdio: 'ignore' + }); + spwndCmd.unref(); + } catch (err) { + console.log('ERROR! Count start sync!'); + } + } +}; + +// https://stackoverflow.com/a/58844917 +const isProcessRunning = (processName) => { + const cmd = (() => { + switch (process.platform) { + case 'win32': + return 'tasklist'; + case 'darwin': + return `ps -ax | grep ${processName}`; + case 'linux': + return 'ps -A'; + default: + return false; + } + })(); + + try { + const result = execSync(cmd).toString(); + + return result.toLowerCase().indexOf(processName.toLowerCase()) > -1; + } catch (error) { + return null; + } +}; + +if (PLATFORM === 'win32') { + console.error('Windows is currently not supported!'); + + return; +} + +if (isProcessRunning('rsync')) { + console.log('WARN: Sync in progress! Aborting!'); + + return; +} + +const parsedConfig = parseConfig(); +const driveInfos = getDriveInfos(parsedConfig); +const mountedDrives = mountDrives(driveInfos); +startSync(mountedDrives); From e36835fc17d62d6f43cd057fedd6c20be01e6d71 Mon Sep 17 00:00:00 2001 From: justsomedude Date: Wed, 30 Sep 2020 12:47:44 +0200 Subject: [PATCH 02/43] fixed whitespace --- config/drivename.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/drivename.conf b/config/drivename.conf index e215fa4c8..914b66066 100644 --- a/config/drivename.conf +++ b/config/drivename.conf @@ -2,4 +2,4 @@ # It supports comments in this fassion, also behind lines! # Currently this formats are supported: sdX, /dev/sdX and simply the label of the drive (not the manufacturer) # In the default config the script will only search for drives with the label photobooth! -photobooth \ No newline at end of file +photobooth \ No newline at end of file From bf5b2a0cf9acb858d58fbd1ec2713ac30aed0d45 Mon Sep 17 00:00:00 2001 From: justsomedude Date: Wed, 30 Sep 2020 13:28:11 +0200 Subject: [PATCH 03/43] add null check --- sync-to-drive.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sync-to-drive.js b/sync-to-drive.js index 546820f4b..d4b890705 100755 --- a/sync-to-drive.js +++ b/sync-to-drive.js @@ -128,6 +128,12 @@ const startSync = (drives) => { } })(); + if (!cmd) { + console.log('ERROR: No command for syncing!'); + + return; + } + console.log('Executing:', cmd); try { From e445f1f35c27f9d2eb8439e6337820345b64f666 Mon Sep 17 00:00:00 2001 From: justsomedude Date: Wed, 30 Sep 2020 16:09:32 +0200 Subject: [PATCH 04/43] only return drives with mountpoint --- sync-to-drive.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sync-to-drive.js b/sync-to-drive.js index d4b890705..884920164 100755 --- a/sync-to-drive.js +++ b/sync-to-drive.js @@ -93,7 +93,10 @@ const mountDrives = (drives) => { console.log(`ERROR: Count mount ${drive.path}`); } } - result.push(drive); + + if (drive.mountpoint) { + result.push(drive); + } } return result; From b39a823be44358ddfb95146eb96f49b695c93735 Mon Sep 17 00:00:00 2001 From: justsomedude Date: Wed, 30 Sep 2020 17:23:50 +0200 Subject: [PATCH 05/43] rsync add delete-after so only deleted files are backed up --- sync-to-drive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sync-to-drive.js b/sync-to-drive.js index 884920164..e110ef28f 100755 --- a/sync-to-drive.js +++ b/sync-to-drive.js @@ -120,7 +120,7 @@ const startSync = (drives) => { return [ 'rsync', '-a', - '--delete', + '--delete-before', '-b', `--backup-dir=${path.join(drive.mountpoint, 'backup')}`, dataPath, From f2e3a1233c38cc4321b0d5f0b5eb4b96cfa8b36a Mon Sep 17 00:00:00 2001 From: justsomedude Date: Wed, 30 Sep 2020 18:31:08 +0200 Subject: [PATCH 06/43] moved comment and added null check --- sync-to-drive.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sync-to-drive.js b/sync-to-drive.js index e110ef28f..a3653d1ea 100755 --- a/sync-to-drive.js +++ b/sync-to-drive.js @@ -45,10 +45,10 @@ const parseConfig = () => { }; const getDriveInfos = (drives) => { - //Assuming that the lsblk version supports JSON output! let json = null; try { + //Assuming that the lsblk version supports JSON output! const output = execSync('export LC_ALL=C; lsblk -ablJO 2>/dev/null; unset LC_ALL').toString(); json = JSON.parse(output); } catch (err) { @@ -189,6 +189,11 @@ if (isProcessRunning('rsync')) { } const parsedConfig = parseConfig(); + +if (!parsedConfig) { + return; +} + const driveInfos = getDriveInfos(parsedConfig); const mountedDrives = mountDrives(driveInfos); startSync(mountedDrives); From 2c0f5ad57a84d4a6df07498543a19a2a765b0b6a Mon Sep 17 00:00:00 2001 From: justsomedude Date: Wed, 30 Sep 2020 19:06:36 +0200 Subject: [PATCH 07/43] add faq and ask if the user wants the cronjob --- faq/faq.md | 21 +++++++++++++++++++++ install-raspbian.sh | 11 +++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/faq/faq.md b/faq/faq.md index d839f6e06..656eda299 100644 --- a/faq/faq.md +++ b/faq/faq.md @@ -363,3 +363,24 @@ If you run into any errors setting up your hotspot we can remove all the setting sudo ./setup-network.sh --clean ``` +### Turn on file backup +If you want to enable the automatic file backup/syncing script you need to add a +cronjob that changes the working directoy to the photobooth folder and calls the +script. Per default the script will run every [5 minutes](https://crowdin.com/project/photobooth). + +**Important: You must make sure change to the photobooth directory because the script expects to be run from within the photobooth folder!**. + +``` +*/5 * * * * cd /var/www/html/ && ./sync-to-drive.js +``` + +Example to add it on the fly: + +``` +crontab -l | { cat; echo "*/5 * * * * cd /var/www/html/ && ./sync-to-drive.js"; } | crontab - +``` + +The default config will look for a drive with the label photobooth. +If you want to add other drives to sync to just add them to the `drivename.conf`. +Either with their name (sdX), path (/dev/sdX) or with the label (photobooth) + diff --git a/install-raspbian.sh b/install-raspbian.sh index f44ee5b6a..10afda919 100755 --- a/install-raspbian.sh +++ b/install-raspbian.sh @@ -318,8 +318,15 @@ cat >> /boot/config.txt << EOF dtoverlay=gpio-no-irq EOF -info "### Adding rsync cronjob for file backup" -crontab -l | { cat; echo "*/5 * * * * cd ${INSTALLFOLDERPATH} && ./sync-to-drive.js"; } | crontab - +echo -e "\033[0;33m### You'd probably like to use the file backup feature." +read -p "### Would you like to add a cronjob for file backup? [y/N] " -n 1 -r +echo -e "\033[0m" +if [[ $REPLY =~ ^[Yy]$ ]] +then + info "### Adding rsync cronjob for file backup" + + crontab -l | { cat; echo "*/5 * * * * cd ${INSTALLFOLDERPATH} && ./sync-to-drive.js"; } | crontab - +fi info "### Congratulations you finished the install process." info "### Have fun with your Photobooth, but first restart your Pi." From a7a2765e44aa8c3bda28c4869f839ab1a7896b9e Mon Sep 17 00:00:00 2001 From: justsomedude Date: Wed, 30 Sep 2020 19:09:57 +0200 Subject: [PATCH 08/43] forget to change link and to build faq Change-Id: I4cddbe8597fd46bb3926958cf193aab48ae728ec --- faq/faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/faq/faq.md b/faq/faq.md index 656eda299..2e7232427 100644 --- a/faq/faq.md +++ b/faq/faq.md @@ -366,7 +366,7 @@ sudo ./setup-network.sh --clean ### Turn on file backup If you want to enable the automatic file backup/syncing script you need to add a cronjob that changes the working directoy to the photobooth folder and calls the -script. Per default the script will run every [5 minutes](https://crowdin.com/project/photobooth). +script. Per default the script will run every [5 minutes](https://crontab.guru/#*/5_*_*_*_*). **Important: You must make sure change to the photobooth directory because the script expects to be run from within the photobooth folder!**. From 3fc0ebfb55ea2e54741973535600617b2b0a35f5 Mon Sep 17 00:00:00 2001 From: justsomedude Date: Wed, 30 Sep 2020 19:46:33 +0200 Subject: [PATCH 09/43] add rsync to update script --- update-booth.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/update-booth.sh b/update-booth.sh index b342f3bf7..19f29934d 100644 --- a/update-booth.sh +++ b/update-booth.sh @@ -93,6 +93,7 @@ COMMON_PACKAGES=( 'php-gd' 'php-zip' 'yarn' + 'rsync' ) if [[ ! -d "${booth_source}" ]]; then From 374dd1e178ad448ba7e04e0222186065056c1b3c Mon Sep 17 00:00:00 2001 From: justsomedude Date: Wed, 30 Sep 2020 19:53:39 +0200 Subject: [PATCH 10/43] added comment about the DATA_DIR_NAME --- sync-to-drive.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sync-to-drive.js b/sync-to-drive.js index a3653d1ea..8741433c3 100755 --- a/sync-to-drive.js +++ b/sync-to-drive.js @@ -9,6 +9,10 @@ const path = require('path'); const BASE_DIR = __dirname; const CONFIG_DIR_NAME = 'config'; const CONFIG_FILE_NAME = 'drivename.conf'; +/* + * This is the folder within the (photobooth) folder structure where data (photos, db, etc.) resides. + * It needs to be changed when the photobooth data storage location changes. + */ const DATA_DIR_NAME = 'data'; const PLATFORM = process.platform; From 94551573a6970c2ad4715ba83d73ba1f39b1ad47 Mon Sep 17 00:00:00 2001 From: justsomedude Date: Wed, 30 Sep 2020 20:27:09 +0200 Subject: [PATCH 11/43] fixed md, added comment about hard coded data path Change-Id: Ida88e40f7951e3af669f1326c16a1668ced72d5a --- faq/faq.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/faq/faq.md b/faq/faq.md index 2e7232427..cdc4038ac 100644 --- a/faq/faq.md +++ b/faq/faq.md @@ -382,5 +382,8 @@ crontab -l | { cat; echo "*/5 * * * * cd /var/www/html/ && ./sync-to-drive.js"; The default config will look for a drive with the label photobooth. If you want to add other drives to sync to just add them to the `drivename.conf`. -Either with their name (sdX), path (/dev/sdX) or with the label (photobooth) +Either with their name (`sdX`), path (`/dev/sdX`) or with the label (`photobooth`) + +**NOTE:** Currently the data directory is hard coded! If you're using a different path for +file storage (e.g. not `/var/www/html/`) you'd need to change it within the `sync-to-drive.js`. From 56b2c0413229aba217acb7cfcf8cadcba40de690 Mon Sep 17 00:00:00 2001 From: justsomedude Date: Thu, 1 Oct 2020 09:11:49 +0200 Subject: [PATCH 12/43] moved config, fixed faq, fixed small bugs Change-Id: I417d10ecf6ffc5c848ee7cc9988eaa9ae740dd5c --- .gitignore | 1 - config/config.inc.php | 7 ++++ config/drivename.conf | 5 --- faq/faq.md | 17 ++++++--- sync-to-drive.js | 85 +++++++++++++++++++++---------------------- 5 files changed, 61 insertions(+), 54 deletions(-) delete mode 100644 config/drivename.conf diff --git a/.gitignore b/.gitignore index b043d7a1b..c5fa957f3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ archives/ bower_components/ config/* !config/config.inc.php -!config/drivename.conf data/ digicamcontrol/ manual/faq.html diff --git a/config/config.inc.php b/config/config.inc.php index b8e8082f9..4117869fe 100644 --- a/config/config.inc.php +++ b/config/config.inc.php @@ -248,3 +248,10 @@ $config['reset_remove_images'] = true; $config['reset_remove_mailtxt'] = true; $config['reset_remove_config'] = true; + + +// B A C K U P / S Y N C S C R I P T +// Currently supported formats are : sdX, /dev/sdX and simply the label of the drive (not the manufacturer) +// In the default config the script will only search for drives with the label photobooth! +$config['sync_script_enabled'] = false; +$config['sync_script_targets'] = ['photobooth']; //Default targets for the sync sctipt diff --git a/config/drivename.conf b/config/drivename.conf deleted file mode 100644 index 914b66066..000000000 --- a/config/drivename.conf +++ /dev/null @@ -1,5 +0,0 @@ -# This file is used by the sync-to-drive script! -# It supports comments in this fassion, also behind lines! -# Currently this formats are supported: sdX, /dev/sdX and simply the label of the drive (not the manufacturer) -# In the default config the script will only search for drives with the label photobooth! -photobooth \ No newline at end of file diff --git a/faq/faq.md b/faq/faq.md index cdc4038ac..30ff4c254 100644 --- a/faq/faq.md +++ b/faq/faq.md @@ -380,10 +380,17 @@ Example to add it on the fly: crontab -l | { cat; echo "*/5 * * * * cd /var/www/html/ && ./sync-to-drive.js"; } | crontab - ``` -The default config will look for a drive with the label photobooth. -If you want to add other drives to sync to just add them to the `drivename.conf`. -Either with their name (`sdX`), path (`/dev/sdX`) or with the label (`photobooth`) +If you want to add other drives to sync files to, just add them via the admin panel in your +browser [localhost/admin](http://localhost/admin) or add them directly to the `'sync_script_targets'` array inside `config/my.config.inc.php`. + +Example: -**NOTE:** Currently the data directory is hard coded! If you're using a different path for -file storage (e.g. not `/var/www/html/`) you'd need to change it within the `sync-to-drive.js`. +``` +'sync_script_targets' => array ( + 'sdX', +) +``` + +Either with their name (`sdX`), path (`/dev/sdX`) or with the label (`photobooth`). +The default config will look for a drive with the label photobooth. diff --git a/sync-to-drive.js b/sync-to-drive.js index 8741433c3..89cefa8d3 100755 --- a/sync-to-drive.js +++ b/sync-to-drive.js @@ -6,49 +6,42 @@ const fs = require('fs'); const path = require('path'); //This script needs to be run from within the photobooth directory -const BASE_DIR = __dirname; -const CONFIG_DIR_NAME = 'config'; -const CONFIG_FILE_NAME = 'drivename.conf'; -/* - * This is the folder within the (photobooth) folder structure where data (photos, db, etc.) resides. - * It needs to be changed when the photobooth data storage location changes. - */ -const DATA_DIR_NAME = 'data'; +const API_DIR_NAME = 'api'; +const API_FILE_NAME = 'config.php'; const PLATFORM = process.platform; -const parseConfig = () => { - const confPath = path.join(BASE_DIR, CONFIG_DIR_NAME, CONFIG_FILE_NAME); +const getConfigFromPHP = () => { + const cmd = `cd ${API_DIR_NAME} && php ./${API_FILE_NAME}`; - if (!fs.existsSync(confPath)) { - console.log(`ERROR: Couldn't find the config file: ${confPath}`); + try { + const stdout = execSync(cmd).toString(); - return null; + return JSON.parse(stdout.slice(stdout.indexOf('{'), -1)); + } catch (err) { + console.log('ERROR: Couldnt get config from PHP', err); } - //Should be checked, becuase the behavior of fs.readFileSync() is platform-specific https://nodejs.org/api/fs.html#fs_fs_readfile_path_options_callback - if (fs.lstatSync(confPath).isDirectory()) { - console.log(`ERROR: ${confPath} is a directory!`); + return null; +}; + +const parseConfig = (config) => { + if (!config) { return null; } - const fileContent = fs.readFileSync(confPath, {encoding: 'utf8'}); - const split = fileContent.split('\n').map((line) => line.replace('\r', '')); - - return split.reduce((arr, line) => { - if (line && !line.startsWith('#')) { - const trimmed = line.trim(); - if (trimmed.includes('#')) { - arr.push(trimmed.substr(0, trimmed.indexOf('#'))); - } else { - arr.push(trimmed); - } - } + try { + return { + dataAbsPath: config.foldersAbs.data, + drives: [...config.sync_script_targets] + }; + } catch (err) { + console.log('ERROR: Couldt parse config!', err); + } - return arr; - }, []); + return null; }; -const getDriveInfos = (drives) => { +const getDriveInfos = ({drives}) => { let json = null; try { @@ -106,11 +99,9 @@ const mountDrives = (drives) => { return result; }; -const startSync = (drives) => { - const dataPath = path.join(BASE_DIR, DATA_DIR_NAME); - - if (!fs.existsSync(dataPath)) { - console.log(`ERROR: Folder ${dataPath} doesn't exist!`); +const startSync = ({dataAbsPath, drives}) => { + if (!fs.existsSync(dataAbsPath)) { + console.log(`ERROR: Folder ${dataAbsPath} doesn't exist!`); return; } @@ -126,9 +117,9 @@ const startSync = (drives) => { '-a', '--delete-before', '-b', - `--backup-dir=${path.join(drive.mountpoint, 'backup')}`, - dataPath, - path.join(drive.mountpoint) + `--backup-dir=${path.join(drive.mountpoint, 'deleted')}`, + dataAbsPath, + path.join(drive.mountpoint, 'sync') ].join(' '); default: return null; @@ -151,7 +142,7 @@ const startSync = (drives) => { }); spwndCmd.unref(); } catch (err) { - console.log('ERROR! Count start sync!'); + console.log('ERROR! Couldnt start sync!'); } } }; @@ -192,12 +183,20 @@ if (isProcessRunning('rsync')) { return; } -const parsedConfig = parseConfig(); +const phpConfig = getConfigFromPHP(); + +if (!phpConfig) { + return; +} else if (!phpConfig.sync_script_enabled) { + console.log('WARN: Sync script was disabled by config! Aborting!'); -if (!parsedConfig) { return; } +const parsedConfig = parseConfig(phpConfig); const driveInfos = getDriveInfos(parsedConfig); const mountedDrives = mountDrives(driveInfos); -startSync(mountedDrives); +startSync({ + dataAbsPath: parsedConfig.dataAbsPath, + drives: mountedDrives +}); From 0f702a2ea67babb5348fb47d098746a0e49ea41b Mon Sep 17 00:00:00 2001 From: justsomedude Date: Thu, 1 Oct 2020 16:36:55 +0200 Subject: [PATCH 13/43] how hard could rsync be? --- sync-to-drive.js | 1 + 1 file changed, 1 insertion(+) diff --git a/sync-to-drive.js b/sync-to-drive.js index 89cefa8d3..15100c93d 100755 --- a/sync-to-drive.js +++ b/sync-to-drive.js @@ -118,6 +118,7 @@ const startSync = ({dataAbsPath, drives}) => { '--delete-before', '-b', `--backup-dir=${path.join(drive.mountpoint, 'deleted')}`, + '--ignore-existing', dataAbsPath, path.join(drive.mountpoint, 'sync') ].join(' '); From 9f094935bc294c229a5a04ab0efc8f78709d410b Mon Sep 17 00:00:00 2001 From: justsomedude Date: Thu, 1 Oct 2020 18:13:01 +0200 Subject: [PATCH 14/43] bug fix point after mountpoint (its different on distros) --- sync-to-drive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sync-to-drive.js b/sync-to-drive.js index 15100c93d..b870774cd 100755 --- a/sync-to-drive.js +++ b/sync-to-drive.js @@ -83,7 +83,7 @@ const mountDrives = (drives) => { const mountPoint = mountRes .substr(mountRes.indexOf('at') + 3) .trim() - .replace('\n'); + .replace(/[\n.]/gu, ''); drive.mountpoint = mountPoint; } catch (error) { From 890c1c2c611d669a3b8c69b3b8aaa24ac4d30353 Mon Sep 17 00:00:00 2001 From: justsomedude Date: Thu, 1 Oct 2020 23:33:19 +0200 Subject: [PATCH 15/43] added all the stuff to make script useable from www-data --- install-raspbian.sh | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/install-raspbian.sh b/install-raspbian.sh index 10afda919..b14c4d244 100755 --- a/install-raspbian.sh +++ b/install-raspbian.sh @@ -319,13 +319,39 @@ dtoverlay=gpio-no-irq EOF echo -e "\033[0;33m### You'd probably like to use the file backup feature." -read -p "### Would you like to add a cronjob for file backup? [y/N] " -n 1 -r +read -p "### Would you like to enable the file backup? [y/N] " -n 1 -r echo -e "\033[0m" if [[ $REPLY =~ ^[Yy]$ ]] then + info "### Disabling automount for pi user" + + mkdir -p /home/pi/.config/pcmanfm/LXDE/ + cat >> /home/pi/.config/pcmanfm/LXDE/pcmanfm.conf <> /etc/polkit-1/localauthority/50-local.d/udisks2.pkla <> /var/log/photoboothsync/synclog.txt 2>&1"; } | sudo -u www-data crontab - fi info "### Congratulations you finished the install process." From 73a212f584243645f2fabe9ba18efa298d49ca29 Mon Sep 17 00:00:00 2001 From: justsomedude Date: Thu, 1 Oct 2020 23:49:28 +0200 Subject: [PATCH 16/43] ofc stuff on the inet isnt correct --- install-raspbian.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/install-raspbian.sh b/install-raspbian.sh index b14c4d244..e207716b8 100755 --- a/install-raspbian.sh +++ b/install-raspbian.sh @@ -325,13 +325,16 @@ if [[ $REPLY =~ ^[Yy]$ ]] then info "### Disabling automount for pi user" - mkdir -p /home/pi/.config/pcmanfm/LXDE/ - cat >> /home/pi/.config/pcmanfm/LXDE/pcmanfm.conf <> /home/pi/.config/pcmanfm/LXDE-pi/pcmanfm.conf <> /etc/polkit-1/localauthority/50-local.d/udisks2.pkla < Date: Thu, 8 Oct 2020 22:40:11 +0100 Subject: [PATCH 17/43] Rewrite to eliminate cron, integrate with Admin GUI and settings WIP --- api/admin.php | 4 +- config/config.inc.php | 6 ++- index.php | 2 +- install-raspbian.sh | 16 +++---- lib/config.php | 3 +- lib/configsetup.inc.php | 18 +++++++ lib/remotebuzzer_config.php | 25 ---------- lib/services_config.php | 29 ++++++++++++ ...tebuzzer_server.php => services_start.php} | 16 ++++++- sync-to-drive.js | 47 +++++++++++++++---- 10 files changed, 119 insertions(+), 47 deletions(-) delete mode 100644 lib/remotebuzzer_config.php create mode 100644 lib/services_config.php rename lib/{remotebuzzer_server.php => services_start.php} (63%) diff --git a/api/admin.php b/api/admin.php index bc53d680f..decd2a9db 100644 --- a/api/admin.php +++ b/api/admin.php @@ -112,7 +112,9 @@ } } -require_once('../lib/remotebuzzer_config.php'); +/* Kill service daemons after config has changed */ +/* this ensures services will be restarted (or not) in adherence to latest config after the change */ +require_once('../lib/services_config.php'); function arrayRecursiveDiff($aArray1, $aArray2) { diff --git a/config/config.inc.php b/config/config.inc.php index 4117869fe..cd44a8829 100644 --- a/config/config.inc.php +++ b/config/config.inc.php @@ -253,5 +253,7 @@ // B A C K U P / S Y N C S C R I P T // Currently supported formats are : sdX, /dev/sdX and simply the label of the drive (not the manufacturer) // In the default config the script will only search for drives with the label photobooth! -$config['sync_script_enabled'] = false; -$config['sync_script_targets'] = ['photobooth']; //Default targets for the sync sctipt +//$config['sync_script_enabled'] = false; +$config['sync_script_targets'] = ['/dev/sda1']; //Default targets for the sync sctipt +$config['synctodrive_enabled'] = false; +$config['synctodrive_targets'] = '/dev/sda1'; //Default targets for the sync sctipt diff --git a/index.php b/index.php index dd556998e..ca4f13cc6 100644 --- a/index.php +++ b/index.php @@ -242,6 +242,6 @@ - + diff --git a/install-raspbian.sh b/install-raspbian.sh index e207716b8..70aa9918f 100755 --- a/install-raspbian.sh +++ b/install-raspbian.sh @@ -347,14 +347,14 @@ ResultActive=yes EOF - info "### Creating and chowning log folder for cronjob log" - mkdir /var/log/photoboothsync/ - chown www-data:www-data /var/log/photoboothsync/ - - info "### Adding rsync cronjob for file backup" - - sudo -u www-data crontab -l | { cat; echo 'MAILTO=""'; } | sudo -u www-data crontab - - sudo -u www-data crontab -l | { cat; echo "*/5 * * * * (cd ${INSTALLFOLDERPATH} && ./sync-to-drive.js) >> /var/log/photoboothsync/synclog.txt 2>&1"; } | sudo -u www-data crontab - +# info "### Creating and chowning log folder for cronjob log" +# mkdir /var/log/photoboothsync/ +# chown www-data:www-data /var/log/photoboothsync/ + +# info "### Adding rsync cronjob for file backup" +# +# sudo -u www-data crontab -l | { cat; echo 'MAILTO=""'; } | sudo -u www-data crontab - +# sudo -u www-data crontab -l | { cat; echo "*/5 * * * * (cd ${INSTALLFOLDERPATH} && ./sync-to-drive.js) >> /var/log/photoboothsync/synclog.txt 2>&1"; } | sudo -u www-data crontab - fi info "### Congratulations you finished the install process." diff --git a/lib/config.php b/lib/config.php index 76d3711c0..ab1cb5bf9 100644 --- a/lib/config.php +++ b/lib/config.php @@ -78,7 +78,8 @@ $config['collage_limit'] = 4; -$config['remotebuzzer_logfile'] = 'io_server.log'; +$config['remotebuzzer_logfile'] = 'remotebuzzer_server.log'; +$config['synctodrive_logfile'] = 'synctodrive_server.log'; $defaultConfig = $config; diff --git a/lib/configsetup.inc.php b/lib/configsetup.inc.php index 4207e3c47..e13303876 100644 --- a/lib/configsetup.inc.php +++ b/lib/configsetup.inc.php @@ -1130,6 +1130,24 @@ 'value' => $config['remotebuzzer_logfile'] ] ], + 'synctodrive' => [ + 'enabled' => [ + 'type' => 'checkbox', + 'name' => 'synctodrive_enabled', + 'value' => $config['synctodrive_enabled'] + ], + 'targets' => [ + 'type' => 'input', + 'placeholder' => $defaultConfig['synctodrive_targets'], + 'name' => 'synctodrive_targets', + 'value' => $config['synctodrive_targets'] + ], + 'logfile' => [ + 'type' => 'hidden', + 'name' => 'synctodrive_logfile', + 'value' => $config['synctodrive_logfile'] + ] + ], 'reset' => [ 'remove_images' => [ 'type' => 'checkbox', diff --git a/lib/remotebuzzer_config.php b/lib/remotebuzzer_config.php deleted file mode 100644 index 54db422bf..000000000 --- a/lib/remotebuzzer_config.php +++ /dev/null @@ -1,25 +0,0 @@ - diff --git a/lib/services_config.php b/lib/services_config.php new file mode 100644 index 000000000..fc9d56fa1 --- /dev/null +++ b/lib/services_config.php @@ -0,0 +1,29 @@ + diff --git a/lib/remotebuzzer_server.php b/lib/services_start.php similarity index 63% rename from lib/remotebuzzer_server.php rename to lib/services_start.php index 8b65ba6ba..20a7c25fe 100644 --- a/lib/remotebuzzer_server.php +++ b/lib/services_start.php @@ -20,4 +20,18 @@ ?> - + +\n"); + proc_close(proc_open ($config['remotebuzzer_nodebin']." ./sync-to-drive.js 1>>".$logfile." 2>&1 &", array(), $foo)); + } +?> diff --git a/sync-to-drive.js b/sync-to-drive.js index b870774cd..132a90d86 100755 --- a/sync-to-drive.js +++ b/sync-to-drive.js @@ -66,7 +66,7 @@ const getDriveInfos = ({drives}) => { if ( drives.some((drive) => drive === blk.name || drive === blk.kname || drive === blk.path || drive === blk.label) ) { - arr.push(blk); + arr.push(blk); } return arr; @@ -133,7 +133,7 @@ const startSync = ({dataAbsPath, drives}) => { return; } - console.log('Executing:', cmd); + console.log('Sync-To-Drive server [', myPid, ']: Executing command:', cmd); try { const spwndCmd = spawn(cmd, { @@ -188,16 +188,47 @@ const phpConfig = getConfigFromPHP(); if (!phpConfig) { return; -} else if (!phpConfig.sync_script_enabled) { +} else if (!phpConfig.synctodrive_enabled) { console.log('WARN: Sync script was disabled by config! Aborting!'); return; } +/* PARSE PHOTOBOOTH CONFIG */ const parsedConfig = parseConfig(phpConfig); -const driveInfos = getDriveInfos(parsedConfig); -const mountedDrives = mountDrives(driveInfos); -startSync({ - dataAbsPath: parsedConfig.dataAbsPath, - drives: mountedDrives + +/* WRITE PROCESS PID FILE */ +const myPid = process.pid; +console.log('Sync-To-Drive server [', myPid, ']: Starting server for sync to drive'); + +const pidFilename = phpConfig.folders.tmp + '/synctodrive_server.pid'; + +fs.writeFile(pidFilename, myPid, function (err) { + if (err) { + throw new Error('Unable to write PID file [' + pidFilename + '] - ' + err.message); + } + + console.log('Sync-To-Drive server [', myPid, ']: PID file created [', pidFilename, ']'); }); + + +function foreverLoop() { + + console.log('Sync-To-Drive server [', myPid, ']: Starting sync process'); + + const driveInfos = getDriveInfos(parsedConfig); + + driveInfos.forEach((element) => {console.log('Sync-To-Drive server [', myPid, ']: Processing drive ', element.name, " -> ", element.path);}) + + const mountedDrives = mountDrives(driveInfos); + + driveInfos.forEach((element) => {console.log('Sync-To-Drive server [', myPid, ']: Mounted drive ', element.name, " -> ", element.mountpoint);}) + + startSync({ + dataAbsPath: parsedConfig.dataAbsPath, + drives: mountedDrives + }); + + setTimeout(foreverLoop, 30000); +} +foreverLoop() From 336f7b9eccf26632cd3aa656cd5d40ed1605e5c1 Mon Sep 17 00:00:00 2001 From: jacques42 Date: Sat, 10 Oct 2020 23:10:04 +0100 Subject: [PATCH 18/43] Sync to USB Translations, Admin GUI, udisks2, clean-up, etc. Change-Id: I96a4455b90ffeaebea3b5d38dcb8158050887134 --- config/config.inc.php | 10 +- install-raspbian.sh | 10 +- lib/config.php | 7 ++ lib/configsetup.inc.php | 24 +++-- lib/services_config.php | 27 ++++- lib/services_start.php | 4 +- resources/lang/de.json | 11 +- resources/lang/en.json | 13 ++- resources/lang/pl.json | 4 +- sync-to-drive.js => src/js/sync-to-drive.js | 107 +++++++++++--------- 10 files changed, 134 insertions(+), 83 deletions(-) rename sync-to-drive.js => src/js/sync-to-drive.js (63%) diff --git a/config/config.inc.php b/config/config.inc.php index cd44a8829..cab19b383 100644 --- a/config/config.inc.php +++ b/config/config.inc.php @@ -233,6 +233,7 @@ $config['exiftool']['msg'] = null; $config['preview']['cmd'] = null; $config['preview']['killcmd'] = null; +$config['nodebin']['cmd'] = null; // R E M O T E B U Z Z E R @@ -241,7 +242,7 @@ $config['remotebuzzer_collagetime'] = '2'; $config['remotebuzzer_port'] = 14711; $config['remotebuzzer_pin'] = 40; -$config['remotebuzzer_nodebin'] = '/usr/bin/node'; +$config['remotebuzzer_collagetime'] = '2'; // control time to distinguish picture from collage // R E S E T @@ -251,9 +252,6 @@ // B A C K U P / S Y N C S C R I P T -// Currently supported formats are : sdX, /dev/sdX and simply the label of the drive (not the manufacturer) -// In the default config the script will only search for drives with the label photobooth! -//$config['sync_script_enabled'] = false; -$config['sync_script_targets'] = ['/dev/sda1']; //Default targets for the sync sctipt $config['synctodrive_enabled'] = false; -$config['synctodrive_targets'] = '/dev/sda1'; //Default targets for the sync sctipt +$config['synctodrive_targets'] = 'photobooth'; //Default targets for the sync sctipt +$config['synctodrive_interval'] = 300; diff --git a/install-raspbian.sh b/install-raspbian.sh index 70aa9918f..9b32982e1 100755 --- a/install-raspbian.sh +++ b/install-raspbian.sh @@ -54,6 +54,7 @@ COMMON_PACKAGES=( 'php-zip' 'yarn' 'rsync' + 'udisks2' ) apache_webserver() { @@ -346,15 +347,6 @@ ResultInactive=yes ResultActive=yes EOF - -# info "### Creating and chowning log folder for cronjob log" -# mkdir /var/log/photoboothsync/ -# chown www-data:www-data /var/log/photoboothsync/ - -# info "### Adding rsync cronjob for file backup" -# -# sudo -u www-data crontab -l | { cat; echo 'MAILTO=""'; } | sudo -u www-data crontab - -# sudo -u www-data crontab -l | { cat; echo "*/5 * * * * (cd ${INSTALLFOLDERPATH} && ./sync-to-drive.js) >> /var/log/photoboothsync/synclog.txt 2>&1"; } | sudo -u www-data crontab - fi info "### Congratulations you finished the install process." diff --git a/lib/config.php b/lib/config.php index ab1cb5bf9..b2ac1eb36 100644 --- a/lib/config.php +++ b/lib/config.php @@ -22,6 +22,9 @@ 'preview' => [ 'cmd' => '', 'killcmd' => '', + ], + 'nodejs' => [ + 'cmd' => '', ] ], 'linux' => [ @@ -40,6 +43,9 @@ 'preview' => [ 'cmd' => 'gphoto2 --stdout --capture-movie | ffmpeg -i - -vcodec rawvideo -pix_fmt yuv420p -threads 0 -f v4l2 /dev/video0 > /dev/null 2>&1 & echo $!', 'killcmd' => 'killall gphoto2 && sleep 1', + ], + 'nodejs' => [ + 'cmd' => '/usr/bin/node', ] ], ]; @@ -75,6 +81,7 @@ $config['exiftool']['msg'] = $cmds[$os]['exiftool']['msg']; $config['preview']['cmd'] = $cmds[$os]['preview']['cmd']; $config['preview']['killcmd'] = $cmds[$os]['preview']['killcmd']; +$config['nodejs']['cmd'] = $cmds[$os]['nodejs']['cmd']; $config['collage_limit'] = 4; diff --git a/lib/configsetup.inc.php b/lib/configsetup.inc.php index e13303876..3f4045bb9 100644 --- a/lib/configsetup.inc.php +++ b/lib/configsetup.inc.php @@ -1087,6 +1087,12 @@ 'placeholder' => 'preview_killcmd', 'name' => 'preview[killcmd]', 'value' => htmlentities($config['preview']['killcmd']) + ], + 'nodebin_cmd' => [ + 'type' => 'input', + 'placeholder' => 'nodebin_cmd', + 'name' => 'nodebin[cmd]', + 'value' => htmlentities($config['nodebin']['cmd']) ] ], 'remotebuzzer' => [ @@ -1118,12 +1124,6 @@ 'name' => 'remotebuzzer_pin', 'value' => $config['remotebuzzer_pin'] ], - 'nodebin' => [ - 'type' => 'input', - 'placeholder' => $defaultConfig['remotebuzzer_nodebin'], - 'name' => 'remotebuzzer_nodebin', - 'value' => $config['remotebuzzer_nodebin'] - ], 'logfile' => [ 'type' => 'hidden', 'name' => 'remotebuzzer_logfile', @@ -1131,7 +1131,7 @@ ] ], 'synctodrive' => [ - 'enabled' => [ + 'synctodrive_enabled' => [ 'type' => 'checkbox', 'name' => 'synctodrive_enabled', 'value' => $config['synctodrive_enabled'] @@ -1142,6 +1142,16 @@ 'name' => 'synctodrive_targets', 'value' => $config['synctodrive_targets'] ], + 'interval' => [ + 'type' => 'range', + 'placeholder' => $defaultConfig['synctodrive_interval'], + 'name' => 'synctodrive_interval', + 'value' => $config['synctodrive_interval'], + 'range_min' => 10, + 'range_max' => 600, + 'range_step' => 1, + 'unit' => 'seconds' + ], 'logfile' => [ 'type' => 'hidden', 'name' => 'synctodrive_logfile', diff --git a/lib/services_config.php b/lib/services_config.php index fc9d56fa1..37c820fe2 100644 --- a/lib/services_config.php +++ b/lib/services_config.php @@ -1,8 +1,11 @@ ".$procPID."\n"); + fclose($fp); + } + + posix_kill($procPID, 9); + } + + } } -killProcessIfActive('remotebuzzer','../'.$config['folders']['tmp'].'/remotebuzzer_server.pid',$config['remotebuzzer_logfile']); -killProcessIfActive('sync-to-drive','../'.$config['folders']['tmp'].'/synctodrive_server.pid',$config['synctodrive_logfile']); + +killProcessIfActive('remotebuzzer_server.js','../'.$config['folders']['tmp'].'/remotebuzzer_server.pid',$config['remotebuzzer_logfile']); +killProcessIfActive('sync-to-drive.js','../'.$config['folders']['tmp'].'/synctodrive_server.pid',$config['synctodrive_logfile']); ?> diff --git a/lib/services_start.php b/lib/services_start.php index 20a7c25fe..25b2e22f3 100644 --- a/lib/services_start.php +++ b/lib/services_start.php @@ -12,7 +12,7 @@ print ("\t\n"); - proc_close(proc_open ($config['remotebuzzer_nodebin']." resources/js/remotebuzzer_server.js 1>>".$logfile." 2>&1 &", array(), $foo)); + proc_close(proc_open ($config['nodebin']['cmd']." resources/js/remotebuzzer_server.js 1>>".$logfile." 2>&1 &", array(), $foo)); } else { print ("\t\n"); @@ -32,6 +32,6 @@ } print ("\t\n"); - proc_close(proc_open ($config['remotebuzzer_nodebin']." ./sync-to-drive.js 1>>".$logfile." 2>&1 &", array(), $foo)); + proc_close(proc_open ($config['nodebin']['cmd']." resources/js/sync-to-drive.js 1>>".$logfile." 2>&1 &", array(), $foo)); } ?> diff --git a/resources/lang/de.json b/resources/lang/de.json index 57138064f..b667e028b 100644 --- a/resources/lang/de.json +++ b/resources/lang/de.json @@ -17,6 +17,7 @@ "commands": "Befehle", "commands_exiftool_cmd": "EXIFtool-Befehl", "commands_exiftool_msg": "Erfolgsnachricht für das beibehalten der EXIF-Daten", + "commands_nodebin_cmd": "Dateipfad für node.js - Beispiel /usr/bin/node", "commands_print_cmd": "Befehl zum Drucken", "commands_print_msg": "Erfolgsnachricht fürs Drucken", "commands_take_picture_cmd": "Befehl zum Kamera auslösen", @@ -122,6 +123,7 @@ "manual_chroma_keying": "Wenn diese Option aktiviert ist, können Sie für Ihre Bilder über die Galerie auf Chromakeying zugreifen.", "manual_commands_exiftool_cmd": "EXIFtool-Befehlszeile, die nach dem Aufnehmen eines Bildes ausgeführt wird. Die Option \"EXIF-Daten beibehalten\" muss aktiviert sein.", "manual_commands_exiftool_msg": "Aus der EXIFtool-Befehlszeile zurückgegebene Nachricht.", + "manual_commands_nodebin_cmd": "Der Pfad zum Node.js Binary", "manual_commands_print_cmd": "Befehlszeile, die beim Drücken der Taste \"Drucken\" ausgeführt wird.", "manual_commands_print_msg": "Vom Druckbefehl zurückgegebene Nachricht.", "manual_commands_take_picture_cmd": "Befehlszeile, die beim Klicken auf die Schaltfläche \"Bild aufnehmen\" ausgeführt wird. Unter Linux können Sie beispielsweise gphoto2 zum Aufnehmen von Bildern verwenden. Unter Windows können Sie digiCamControl verwenden.", @@ -238,7 +240,6 @@ "manual_remotebuzzer_collagetime": "Wird der Buzzer weniger oder gleich dieser Anzahl an Sekunden betätigt (GPIO mit Masse verbunden), wird ein Bild ausgelöst.Wird der Buzzer länger betätigt, wird eine Collage ausgelöst. Falls Collage in den Settings deaktiviert wird, ist diese Funktion auch deaktiviert.", "manual_remotebuzzer_enabled": "Diese Option aktiviert eine Raspberry GPIO Überwachungs-Server für einen Hardware Buzzer, in Kombination mit einem über WLAN verbundenen Bildschirm (z.B. iPad). Bitte bei Verwendung auch die IP Adresse des Photobooth Web Server im Abschnitt \"Allgemein\" konfigurieren", "manual_remotebuzzer_logfile": "Wenn der Dev-Mode ein ist, schreibt der Buzzer Server Debug-Information in die Logdatei. Die Datei liegt im tmp Verzeichnis und der Dateiname ist io_server.log", - "manual_remotebuzzer_nodebin": "Dateipfad für node.js - Beispiel /usr/bin/node", "manual_remotebuzzer_pin": "Verbinde den Hardware Buzzer mit diesem Raspberry PIN (Nummern 1-40). Der Default ist PIN 40 == GPIO21. Der Wert 0 (Null) deaktiviert die GPIO Überwachung des Servers. Das Raspberry PIN Layout ist hier. Bitte verwende die tatsächliche PIN Nummer (1-40), nicht die GPIO Nummer (1-21). Verbinde den PIN mit Masse um den Trigger auszulösen.", "manual_remotebuzzer_port": "Server TCP Port - Beispiel 14711", "manual_remove_config": "Wenn diese Option aktiviert ist, wird die persönliche Konfiguration beim Zurücksetzen entfernt.", @@ -253,6 +254,9 @@ "manual_show_gallery": "Wenn diese Option aktiviert ist, kann der Benutzer vom Startbildschirm aus auf die Galerie zugreifen.", "manual_slideshow_pictureTime": "Geben Sie in Millisekunden an, wie lange ein Bild während der Standalone-Diashow angezeigt wird.", "manual_slideshow_refreshTime": "Die Standalone-Diashow wird nach eingegebenen Sekunden neu geladen.", + "manual_synctodrive_enabled": "Aktiviert das automatische Übertragen neuer Bilder auf einen USB Stick mittels rsync ", + "manual_synctodrive_interval": "Intervall in Sekunden für automatische Synchronisierung ", + "manual_synctodrive_targets": "Liste der USB Geräte, anzugeben enweder mit Gerätename (e.g. sda1), oder den kompletten Pfad (e.g /dev/sda1) oder den Namen des USB Sticks (e.g. photobooth). Einträge mit Semikolon separieren ", "manual_take_collage_frame": "Wenn diese Option aktiviert ist, wird nach der Aufnahme ein definierter Rahmen auf Ihre Collage angewendet.", "manual_take_collage_frame_always": "Wenn aktiviert, wird der definierte Rahmen auf jedes Bild Ihrer Collage angewendet, anstatt einmal nach der Aufnahme der Collage. \"Foto-Collage mit Rahmen aufnehmen\" muss aktiviert sein.", "manual_take_frame": "Wenn diese Option aktiviert ist, wird nach der Aufnahme ein definierter Rahmen auf Ihr Bild angewendet.", @@ -329,7 +333,6 @@ "remotebuzzer_collagetime": "Sekunden gedrückt halten für Collage", "remotebuzzer_enabled": "Bei Verwendung bitte unbedingt die IP Addresse des Photobooth Web Servers konfigurieren (Abschnitt \"Allgemein\")", "remotebuzzer_logfile": "Logfile", - "remotebuzzer_nodebin": "Node.js Pfad", "remotebuzzer_pin": "Raspberry Pi GPIO Pin (0 zum Abschalten)", "remotebuzzer_port": "Server-Port", "remove_config": "Persönliche Konfiguration löschen (my.config.inc.php)", @@ -359,6 +362,10 @@ "startScreen": "

Photobooth

Webinterface

by André Rinas", "success": "Erfolgreich gespeichert!", "symbol": "Symbol auswählen", + "synctodrive": "Bilder automatisch auf USB übertragen", + "synctodrive_enabled": "Aktivieren", + "synctodrive_interval": "Automatisches Synchronisierungs-Intervall", + "synctodrive_targets": "USB Device (Liste mit Semikolon separiert)", "takeCollage": "Collage erstellen!", "takePhoto": "Foto erstellen!", "take_collage_frame": "Foto-Collage mit Rahmen aufnehmen", diff --git a/resources/lang/en.json b/resources/lang/en.json index 9d9125f2a..e8cfb2cd5 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -17,6 +17,7 @@ "commands": "Commands", "commands_exiftool_cmd": "EXIFtool command", "commands_exiftool_msg": "Success message for EXIF preservation", + "commands_nodebin_cmd": "Node.js Executable Path", "commands_preview_cmd": "Command to generate a live preview", "commands_preview_killcmd": "Command to kill live preview", "commands_print_cmd": "Print command", @@ -125,6 +126,7 @@ "manual_chroma_keying": "If enabled, chromakeying can be accessed from gallery for your pictures.", "manual_commands_exiftool_cmd": "EXIFtool command line which is executed after taking a picture if \"Preserve EXIF data\" is enabled.", "manual_commands_exiftool_msg": "Message returned from EXIFtool commandline.", + "manual_commands_nodebin_cmd": "Path to executable for node.js - example /usr/bin/node", "manual_commands_preview_cmd": "Command line which is executed to generate a live preview.", "manual_commands_preview_killcmd": "Command line which is executed to kill the live preview.", "manual_commands_print_cmd": "Command line which is executed while pressing the \"Print\" button.", @@ -249,7 +251,6 @@ "manual_remotebuzzer_collagetime": "If trigger button is pressed (GPIO pulled down) less or equal number of seconds, a picture is triggered. If button is pressed more seconds a collage is triggered. If collage is disabled in the admin settings, no collage will be triggered at all.", "manual_remotebuzzer_enabled": "This feature enables a GPIO monitoring for a hardware trigger connected to Raspberry GPIO pins in combination with WLAN connected displays and screens (i.e. iPad). Please when using ensure to also configure the IP address of the Photobooth web server in the section \"General\".", "manual_remotebuzzer_logfile": "If Dev-Mode is on, server debugging information will be written to the logfile, located in the tmp folder and defaults to io_server.log.", - "manual_remotebuzzer_nodebin": "Path to executable for node.js - example /usr/bin/node.", "manual_remotebuzzer_pin": "Connect the hardware trigger to this Raspberry Pi PIN number (range 1-40). Defaults to PIN 40 == GPIO21. Set to 0 (zero) for to disable the GPIO harware monitoring. Raspberry PIN layout can be found here. Please use the actual PIN numbers, not the GPIO numners. Pull PIN to ground for to trigger.", "manual_remotebuzzer_port": "Server TCP Port - example 14711.", "manual_remove_config": "If enabled, personal config gets removed on reset.", @@ -264,6 +265,9 @@ "manual_show_gallery": "If enabled, user can access the gallery from start screen.", "manual_slideshow_pictureTime": "Add a value which is used as milliseconds an image is displayed at standalone slideshow.", "manual_slideshow_refreshTime": "Refresh standalone slideshow after entered seconds.", + "manual_synctodrive_enabled": "Enable automatic syncing of new pictures to USB device using rsync", + "manual_synctodrive_interval": "Seconds to trigger syncing as interval", + "manual_synctodrive_targets": "List of USB devices, either by device name (e.g. sda1), or device full path (e.g /dev/sda1) or USB stick name (e.g. photobooth). List separated by semicolon ", "manual_take_collage_frame": "If enabled, defined frame will be applied to your collage after taking it.", "manual_take_collage_frame_always": "If enabled, defined collage frame will be applied to each picture of your collage instead of applying once at the end of the collage. \"Take collage with frame\" needs to be enabled.", "manual_take_frame": "If enabled, defined frame will be applied to your picture after taking it.", @@ -345,9 +349,8 @@ "reload": "Reload Page", "remotebuzzer": "Remote Buzzer Server", "remotebuzzer_collagetime": "Seconds button hold to trigger collage", - "remotebuzzer_enabled": "If enabled please make sure to set the IP address of the Photobooth web server properly (Section \"General\")", + "remotebuzzer_enabled": "Enable - please make sure to set the IP address of the Photobooth web server properly (Section \"General\")", "remotebuzzer_logfile": "Logfile", - "remotebuzzer_nodebin": "Node.js Executable Path", "remotebuzzer_pin": "Raspberry Pi PIN number (0 to disable)", "remotebuzzer_port": "Server Port", "remove_config": "Delete personal configuration (my.config.inc.php)", @@ -377,6 +380,10 @@ "startScreen": "

Photobooth

Webinterface

by André Rinas", "success": "Saved successful!", "symbol": "Choose a symbol", + "synctodrive": "Sync pictures to USB", + "synctodrive_enabled": "Enable", + "synctodrive_interval": "Autmated syncing interval", + "synctodrive_targets": "USB devices (list separated by semicolon)", "takeCollage": "Take Collage!", "takePhoto": "Take Pic!", "take_collage_frame": "Take collage with frame", diff --git a/resources/lang/pl.json b/resources/lang/pl.json index 27e021ef0..5ac5016d7 100644 --- a/resources/lang/pl.json +++ b/resources/lang/pl.json @@ -16,6 +16,7 @@ "close": "Zamknij", "commands": "Polecenia", "commands_exiftool_cmd": "Polecenie EXIFtool", + "commands_nodebin_cmd": "Ścieżka wykonywalna Node.js", "commands_print_cmd": "Polecenie Drukuj", "commands_print_msg": "Wiadomość z powodzeniem wydruku", "commands_take_picture_cmd": "Wiadomość zrób zdjęcie", @@ -116,6 +117,7 @@ "manual_auto_print": "Jeśli włączone, zdjęcie zostanie wydrukowane natychmiast po wykonaniu.", "manual_auto_reload_on_error": "Jeśli wystąpi błąd podczas wykonywania zdjęcia, fotobudka przeładuje się automatycznie po 5 sekundach.", "manual_chroma_keying": "Jeśli włączone chromakeying może być dostępne z galerii dla Twoich zdjęć.", + "manual_commands_nodebin_cmd": "Ścieżka do pliku wykonywalnego dla node.js - przykład /usr/bin/node", "manual_commands_print_cmd": "Wiersz poleceń, który jest wykonywany podczas naciśnięcia przycisku \"Drukuj\".", "manual_commands_print_msg": "Wiadomość zwrotna z polecenia wydruku.", "manual_commands_take_picture_cmd": "Wiersz poleceń, który jest wykonywany podczas naciśnięcia przycisku \"Zrób zdjęcie\". Na Linux możesz na przykład użyć gphoto2 do robienia zdjęć, w systemie Windows możesz użyć digiCamControl.", @@ -220,7 +222,6 @@ "manual_remotebuzzer_collagetime": "Jeśli przycisk wyzwalacza jest wciśnięty (GPIO podciągnięty do GND) mniej lub równą liczbę sekund, wyzwolenie będzie włączone. Jeśli przycisk zostanie wciśnięty przez więcej sekund zostanie uruchomiony kolaż. Jeśli kolaż jest wyłączony w ustawieniach administratora, kolaż nie będzie uruchomiony", "manual_remotebuzzer_enabled": "Funkcja ta umożliwia monitorowanie GPIO dla wyzwalacza sprzętowego podłączonego do pinów GPIO Raspberry w połączeniu z podłączonymi wyświetlaczami i ekranami WLAN (i. . iPad). Prosimy o skonfigurowanie adresu IP serwera WWW Photobooth w sekcji \"Ogólne\"", "manual_remotebuzzer_logfile": "Jeśli tryb debugowania jest włączony, informacja o debugowaniu serwera zostanie zapisana do pliku dziennika, znajdującego się w folderze tmp i domyślnie io_server.log", - "manual_remotebuzzer_nodebin": "Ścieżka do pliku wykonywalnego dla node.js - przykład /usr/bin/node", "manual_remotebuzzer_port": "Port TCP serwera - przykład 14711", "manual_user_interface_font_size": "Wprowadź domyślny rozmiar czcionki używany dla interfejsu Fotobudki.", "milliseconds": "milisekundy", @@ -275,7 +276,6 @@ "remotebuzzer_collagetime": "Przytrzymaj przycisk x sekund, aby wyzwolić kolaż", "remotebuzzer_enabled": "Jeśli włączone, upewnij się, że poprawnie ustawiono adres IP serwera WWW Fotobudki (sekcja \"Ogólne\")", "remotebuzzer_logfile": "Plik dziennika", - "remotebuzzer_nodebin": "Ścieżka wykonywalna Node.js", "remotebuzzer_pin": "Raspberry Pi PIN", "remotebuzzer_port": "Port serwera", "remove_config": "Usuń konfigurację osobistą (my.config.inc.php)", diff --git a/sync-to-drive.js b/src/js/sync-to-drive.js similarity index 63% rename from sync-to-drive.js rename to src/js/sync-to-drive.js index 132a90d86..e4c97eac2 100755 --- a/sync-to-drive.js +++ b/src/js/sync-to-drive.js @@ -4,6 +4,7 @@ const {execSync, spawn} = require('child_process'); const fs = require('fs'); const path = require('path'); +const myPid = process.pid; //This script needs to be run from within the photobooth directory const API_DIR_NAME = 'api'; @@ -15,10 +16,9 @@ const getConfigFromPHP = () => { try { const stdout = execSync(cmd).toString(); - return JSON.parse(stdout.slice(stdout.indexOf('{'), -1)); } catch (err) { - console.log('ERROR: Couldnt get config from PHP', err); + console.log('Sync-To-Drive server [', myPid, ']: ERROR: Couldnt get config from PHP', err); } return null; @@ -29,14 +29,16 @@ const parseConfig = (config) => { return null; } - try { - return { - dataAbsPath: config.foldersAbs.data, - drives: [...config.sync_script_targets] - }; - } catch (err) { - console.log('ERROR: Couldt parse config!', err); - } + console.log("conf " + config.foldersAbs.data) + + try { + return { + dataAbsPath: config.foldersAbs.data, + drives: [...config.synctodrive_targets.split(";")] + }; + } catch (err) { + console.log('Sync-To-Drive server [', myPid, ']: ERROR: Couldt parse config', err); + } return null; }; @@ -50,14 +52,14 @@ const getDriveInfos = ({drives}) => { json = JSON.parse(output); } catch (err) { console.log( - 'ERROR: Could not parse the output of lsblk! Please make sure its installed and that it offers JSON output!' + 'Sync-To-Drive server [', myPid, ']: ERROR: Could not parse the output of lsblk! Please make sure its installed and that it offers JSON output!' ); return null; } if (!json || !json.blockdevices) { - console.log('ERROR: The output of lsblk was malformed!'); + console.log('Sync-To-Drive server [', myPid, ']: ERROR: The output of lsblk was malformed!'); return null; } @@ -87,7 +89,7 @@ const mountDrives = (drives) => { drive.mountpoint = mountPoint; } catch (error) { - console.log(`ERROR: Count mount ${drive.path}`); + console.log('Sync-To-Drive server [', myPid, ']: ERROR: Count mount ${drive.path}'); } } @@ -100,35 +102,39 @@ const mountDrives = (drives) => { }; const startSync = ({dataAbsPath, drives}) => { - if (!fs.existsSync(dataAbsPath)) { - console.log(`ERROR: Folder ${dataAbsPath} doesn't exist!`); - - return; - } + if (!fs.existsSync(dataAbsPath)) { + console.log('Sync-To-Drive server [', myPid, ']: ERROR: Folder ${dataAbsPath} does not exist!'); + return; + } - for (const drive of drives) { - const cmd = (() => { - switch (process.platform) { - case 'win32': - return null; - case 'linux': - return [ - 'rsync', - '-a', - '--delete-before', - '-b', - `--backup-dir=${path.join(drive.mountpoint, 'deleted')}`, - '--ignore-existing', - dataAbsPath, - path.join(drive.mountpoint, 'sync') - ].join(' '); - default: - return null; - } - })(); + console.log('Sync-To-Drive server [', myPid,']: Source data folder [',dataAbsPath,']' ); + + + for (const drive of drives) { + console.log('Sync-To-Drive server [', myPid, ']: Synching to drive [', drive.path,'] -> [',drive.mountpoint,']'); + + const cmd = (() => { + switch (process.platform) { + case 'win32': + return null; + case 'linux': + return [ + 'rsync', + '-a', + '--delete-before', + '-b', + `--backup-dir=${path.join(drive.mountpoint, 'deleted')}`, + '--ignore-existing', + dataAbsPath, + path.join(drive.mountpoint, 'sync') + ].join(' '); + default: + return null; + } + })(); if (!cmd) { - console.log('ERROR: No command for syncing!'); + console.log('Sync-To-Drive server [', myPid, ']: ERROR: No command for syncing!'); return; } @@ -143,7 +149,7 @@ const startSync = ({dataAbsPath, drives}) => { }); spwndCmd.unref(); } catch (err) { - console.log('ERROR! Couldnt start sync!'); + console.log('Sync-To-Drive server [', myPid, ']: ERROR! Couldnt start sync!'); } } }; @@ -157,7 +163,7 @@ const isProcessRunning = (processName) => { case 'darwin': return `ps -ax | grep ${processName}`; case 'linux': - return 'ps -A'; + return `ps -A`; default: return false; } @@ -173,13 +179,13 @@ const isProcessRunning = (processName) => { }; if (PLATFORM === 'win32') { - console.error('Windows is currently not supported!'); + console.error('Sync-To-Drive server [', myPid, ']: Windows is currently not supported!'); return; } if (isProcessRunning('rsync')) { - console.log('WARN: Sync in progress! Aborting!'); + console.log('Sync-To-Drive server [', myPid, ']: WARN: Sync in progress'); return; } @@ -189,18 +195,16 @@ const phpConfig = getConfigFromPHP(); if (!phpConfig) { return; } else if (!phpConfig.synctodrive_enabled) { - console.log('WARN: Sync script was disabled by config! Aborting!'); + console.log('Sync-To-Drive server [', myPid, ']: WARN: Sync script was disabled by config! Aborting!'); return; } /* PARSE PHOTOBOOTH CONFIG */ const parsedConfig = parseConfig(phpConfig); +console.log('Sync-To-Drive server [', myPid, ']: Drive names ', ...parsedConfig.drives); /* WRITE PROCESS PID FILE */ -const myPid = process.pid; -console.log('Sync-To-Drive server [', myPid, ']: Starting server for sync to drive'); - const pidFilename = phpConfig.folders.tmp + '/synctodrive_server.pid'; fs.writeFile(pidFilename, myPid, function (err) { @@ -211,6 +215,11 @@ fs.writeFile(pidFilename, myPid, function (err) { console.log('Sync-To-Drive server [', myPid, ']: PID file created [', pidFilename, ']'); }); +/* START LOOP */ + +console.log('Sync-To-Drive server [', myPid, ']: Starting server for sync to drive'); +console.log('Sync-To-Drive server [', myPid, ']: Interval is [', phpConfig.synctodrive_interval, '] seconds'); + function foreverLoop() { @@ -223,12 +232,12 @@ function foreverLoop() { const mountedDrives = mountDrives(driveInfos); driveInfos.forEach((element) => {console.log('Sync-To-Drive server [', myPid, ']: Mounted drive ', element.name, " -> ", element.mountpoint);}) - + startSync({ dataAbsPath: parsedConfig.dataAbsPath, drives: mountedDrives }); - setTimeout(foreverLoop, 30000); + setTimeout(foreverLoop, phpConfig.synctodrive_interval * 1000); } foreverLoop() From a6e1b98d45694f98cd11fb699bebbde5d9a76d91 Mon Sep 17 00:00:00 2001 From: jacques42 Date: Sat, 10 Oct 2020 23:28:08 +0100 Subject: [PATCH 19/43] Sync to USB small bug fix and clean-up Change-Id: Idfd985ce6ea357d84d5d4bef30b93a075ad2b601 --- lib/config.php | 6 +++--- src/js/sync-to-drive.js | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/config.php b/lib/config.php index b2ac1eb36..7ac87cc47 100644 --- a/lib/config.php +++ b/lib/config.php @@ -23,7 +23,7 @@ 'cmd' => '', 'killcmd' => '', ], - 'nodejs' => [ + 'nodebin' => [ 'cmd' => '', ] ], @@ -44,7 +44,7 @@ 'cmd' => 'gphoto2 --stdout --capture-movie | ffmpeg -i - -vcodec rawvideo -pix_fmt yuv420p -threads 0 -f v4l2 /dev/video0 > /dev/null 2>&1 & echo $!', 'killcmd' => 'killall gphoto2 && sleep 1', ], - 'nodejs' => [ + 'nodebin' => [ 'cmd' => '/usr/bin/node', ] ], @@ -81,7 +81,7 @@ $config['exiftool']['msg'] = $cmds[$os]['exiftool']['msg']; $config['preview']['cmd'] = $cmds[$os]['preview']['cmd']; $config['preview']['killcmd'] = $cmds[$os]['preview']['killcmd']; -$config['nodejs']['cmd'] = $cmds[$os]['nodejs']['cmd']; +$config['nodebin']['cmd'] = $cmds[$os]['nodebin']['cmd']; $config['collage_limit'] = 4; diff --git a/src/js/sync-to-drive.js b/src/js/sync-to-drive.js index e4c97eac2..ec0d7cc39 100755 --- a/src/js/sync-to-drive.js +++ b/src/js/sync-to-drive.js @@ -29,8 +29,6 @@ const parseConfig = (config) => { return null; } - console.log("conf " + config.foldersAbs.data) - try { return { dataAbsPath: config.foldersAbs.data, From 4e9927b5669f2389a8f8489ab1aa210855a8e92d Mon Sep 17 00:00:00 2001 From: jacques42 Date: Sun, 11 Oct 2020 09:57:26 +0100 Subject: [PATCH 20/43] Sync to USB: Done prettier, update script, faq, manual, logging fixes, other small things Change-Id: I4537f1a0956a7e7d6e72f335c3faf23121a7a44b --- api/admin.php | 4 +- faq/faq.md | 34 +- lib/configsetup.inc.php | 1 + ...{services_config.php => services_stop.php} | 6 +- src/js/sync-to-drive.js | 291 +++++++++--------- update-booth.sh | 1 + 6 files changed, 172 insertions(+), 165 deletions(-) rename lib/{services_config.php => services_stop.php} (93%) diff --git a/api/admin.php b/api/admin.php index decd2a9db..9f0f3cfd4 100644 --- a/api/admin.php +++ b/api/admin.php @@ -99,6 +99,7 @@ if ($os === 'windows') { $newConfig['remotebuzzer_enabled'] = false; + $newConfig['synctodrive_enabled'] = false; } $content = " Specifically for device `/dev/sda1` being mounted + > For a device being mounted with the name `photobooth` + > Any device being mounted with the string `sdb` in it's device name ``` -'sync_script_targets' => array ( - 'sdX', -) +Example: + /dev/sda1;photobooth;sdb ``` - -Either with their name (`sdX`), path (`/dev/sdX`) or with the label (`photobooth`). -The default config will look for a drive with the label photobooth. +Pictures will be synced to all devices matched by this list, as long as they are mounted (aka USB stick is plugged in) diff --git a/lib/configsetup.inc.php b/lib/configsetup.inc.php index 3f4045bb9..ec0420e68 100644 --- a/lib/configsetup.inc.php +++ b/lib/configsetup.inc.php @@ -1131,6 +1131,7 @@ ] ], 'synctodrive' => [ + 'platform' => 'linux', 'synctodrive_enabled' => [ 'type' => 'checkbox', 'name' => 'synctodrive_enabled', diff --git a/lib/services_config.php b/lib/services_stop.php similarity index 93% rename from lib/services_config.php rename to lib/services_stop.php index 37c820fe2..9e153ec77 100644 --- a/lib/services_config.php +++ b/lib/services_stop.php @@ -29,8 +29,9 @@ function killProcessIfActive($pName, $pidFile, $logfileName) { exec("pgrep -f ".$pName,$pids); - foreach ($pids as $procPID) { - if ($config['dev']) + if (count($pids) > 1) { + foreach ($pids as $procPID) { + if ($config['dev']) { $logfile = $config['folders']['tmp']."/".$logfileName; $fp = fopen("../".$logfile, 'a');//opens file in append mode. @@ -39,6 +40,7 @@ function killProcessIfActive($pName, $pidFile, $logfileName) } posix_kill($procPID, 9); + } } } diff --git a/src/js/sync-to-drive.js b/src/js/sync-to-drive.js index ec0d7cc39..c28daddf6 100755 --- a/src/js/sync-to-drive.js +++ b/src/js/sync-to-drive.js @@ -12,190 +12,203 @@ const API_FILE_NAME = 'config.php'; const PLATFORM = process.platform; const getConfigFromPHP = () => { - const cmd = `cd ${API_DIR_NAME} && php ./${API_FILE_NAME}`; + const cmd = 'cd ' + API_DIR_NAME + ' && php ./' + API_FILE_NAME; - try { - const stdout = execSync(cmd).toString(); - return JSON.parse(stdout.slice(stdout.indexOf('{'), -1)); - } catch (err) { - console.log('Sync-To-Drive server [', myPid, ']: ERROR: Couldnt get config from PHP', err); - } + try { + const stdout = execSync(cmd).toString(); - return null; + return JSON.parse(stdout.slice(stdout.indexOf('{'), -1)); + } catch (err) { + console.log('Sync-To-Drive server [', myPid, ']: ERROR: Couldnt get config from PHP', err); + } + + return null; }; const parseConfig = (config) => { - if (!config) { - return null; - } + if (!config) { + return null; + } try { - return { - dataAbsPath: config.foldersAbs.data, - drives: [...config.synctodrive_targets.split(";")] - }; + return { + dataAbsPath: config.foldersAbs.data, + drives: [...config.synctodrive_targets.split(';')] + }; } catch (err) { - console.log('Sync-To-Drive server [', myPid, ']: ERROR: Couldt parse config', err); + console.log('Sync-To-Drive server [', myPid, ']: ERROR: Couldt parse config', err); } - return null; + return null; }; const getDriveInfos = ({drives}) => { - let json = null; + let json = null; - try { - //Assuming that the lsblk version supports JSON output! - const output = execSync('export LC_ALL=C; lsblk -ablJO 2>/dev/null; unset LC_ALL').toString(); - json = JSON.parse(output); - } catch (err) { - console.log( - 'Sync-To-Drive server [', myPid, ']: ERROR: Could not parse the output of lsblk! Please make sure its installed and that it offers JSON output!' - ); - - return null; - } + try { + //Assuming that the lsblk version supports JSON output! + const output = execSync('export LC_ALL=C; lsblk -ablJO 2>/dev/null; unset LC_ALL').toString(); + json = JSON.parse(output); + } catch (err) { + console.log( + 'Sync-To-Drive server [', + myPid, + ']: ERROR: Could not parse the output of lsblk! Please make sure its installed and that it offers JSON output!' + ); - if (!json || !json.blockdevices) { - console.log('Sync-To-Drive server [', myPid, ']: ERROR: The output of lsblk was malformed!'); + return null; + } - return null; - } + if (!json || !json.blockdevices) { + console.log('Sync-To-Drive server [', myPid, ']: ERROR: The output of lsblk was malformed!'); - return json.blockdevices.reduce((arr, blk) => { - if ( - drives.some((drive) => drive === blk.name || drive === blk.kname || drive === blk.path || drive === blk.label) - ) { - arr.push(blk); + return null; } - return arr; - }, []); + return json.blockdevices.reduce((arr, blk) => { + if ( + drives.some( + (drive) => drive === blk.name || drive === blk.kname || drive === blk.path || drive === blk.label + ) + ) { + arr.push(blk); + } + + return arr; + }, []); }; const mountDrives = (drives) => { - const result = []; - - for (const drive of drives) { - if (!drive.mountpoint) { - try { - const mountRes = execSync(`export LC_ALL=C; udisksctl mount -b ${drive.path}; unset LC_ALL`).toString(); - const mountPoint = mountRes - .substr(mountRes.indexOf('at') + 3) - .trim() - .replace(/[\n.]/gu, ''); - - drive.mountpoint = mountPoint; - } catch (error) { - console.log('Sync-To-Drive server [', myPid, ']: ERROR: Count mount ${drive.path}'); - } - } + const result = []; - if (drive.mountpoint) { - result.push(drive); + for (const drive of drives) { + if (!drive.mountpoint) { + try { + const mountRes = execSync('export LC_ALL=C; udisksctl mount -b ' + drive.path + '; unset LC_ALL').toString(); + const mountPoint = mountRes + .substr(mountRes.indexOf('at') + 3) + .trim() + .replace(/[\n.]/gu, ''); + + drive.mountpoint = mountPoint; + } catch (error) { + console.log('Sync-To-Drive server [', myPid, ']: ERROR: Count mount ' + drive.path); + } + } + + if (drive.mountpoint) { + result.push(drive); + } } - } - return result; + return result; }; const startSync = ({dataAbsPath, drives}) => { if (!fs.existsSync(dataAbsPath)) { - console.log('Sync-To-Drive server [', myPid, ']: ERROR: Folder ${dataAbsPath} does not exist!'); - return; - } - - console.log('Sync-To-Drive server [', myPid,']: Source data folder [',dataAbsPath,']' ); - - - for (const drive of drives) { - console.log('Sync-To-Drive server [', myPid, ']: Synching to drive [', drive.path,'] -> [',drive.mountpoint,']'); - - const cmd = (() => { - switch (process.platform) { - case 'win32': - return null; - case 'linux': - return [ - 'rsync', - '-a', - '--delete-before', - '-b', - `--backup-dir=${path.join(drive.mountpoint, 'deleted')}`, - '--ignore-existing', - dataAbsPath, - path.join(drive.mountpoint, 'sync') - ].join(' '); - default: - return null; - } - })(); + console.log('Sync-To-Drive server [', myPid, ']: ERROR: Folder ' + dataAbsPath + ' does not exist!'); - if (!cmd) { - console.log('Sync-To-Drive server [', myPid, ']: ERROR: No command for syncing!'); - - return; +return; } - console.log('Sync-To-Drive server [', myPid, ']: Executing command:', cmd); + console.log('Sync-To-Drive server [', myPid, ']: Source data folder [', dataAbsPath, ']'); - try { - const spwndCmd = spawn(cmd, { - detached: true, - shell: true, - stdio: 'ignore' - }); - spwndCmd.unref(); - } catch (err) { - console.log('Sync-To-Drive server [', myPid, ']: ERROR! Couldnt start sync!'); + for (const drive of drives) { + console.log( + 'Sync-To-Drive server [', + myPid, + ']: Synching to drive [', + drive.path, + '] -> [', + drive.mountpoint, + ']' + ); + + const cmd = (() => { + switch (process.platform) { + case 'win32': + return null; + case 'linux': + return [ + 'rsync', + '-a', + '--delete-before', + '-b', + '--backup-dir=' + path.join(drive.mountpoint, 'deleted'), + '--ignore-existing', + dataAbsPath, + path.join(drive.mountpoint, 'sync') + ].join(' '); + default: + return null; + } + })(); + + if (!cmd) { + console.log('Sync-To-Drive server [', myPid, ']: ERROR: No command for syncing!'); + + return; + } + + console.log('Sync-To-Drive server [', myPid, ']: Executing command:', cmd); + + try { + const spwndCmd = spawn(cmd, { + detached: true, + shell: true, + stdio: 'ignore' + }); + spwndCmd.unref(); + } catch (err) { + console.log('Sync-To-Drive server [', myPid, ']: ERROR! Couldnt start sync!'); + } } - } }; // https://stackoverflow.com/a/58844917 const isProcessRunning = (processName) => { - const cmd = (() => { - switch (process.platform) { - case 'win32': - return 'tasklist'; - case 'darwin': - return `ps -ax | grep ${processName}`; - case 'linux': - return `ps -A`; - default: - return false; - } - })(); + const cmd = (() => { + switch (process.platform) { + case 'win32': + return 'tasklist'; + case 'darwin': + return 'ps -ax | grep ' + processName; + case 'linux': + return 'ps -A'; + default: + return false; + } + })(); - try { - const result = execSync(cmd).toString(); + try { + const result = execSync(cmd).toString(); - return result.toLowerCase().indexOf(processName.toLowerCase()) > -1; - } catch (error) { - return null; - } + return result.toLowerCase().indexOf(processName.toLowerCase()) > -1; + } catch (error) { + return null; + } }; if (PLATFORM === 'win32') { - console.error('Sync-To-Drive server [', myPid, ']: Windows is currently not supported!'); + console.error('Sync-To-Drive server [', myPid, ']: Windows is currently not supported!'); - return; + return; } if (isProcessRunning('rsync')) { - console.log('Sync-To-Drive server [', myPid, ']: WARN: Sync in progress'); + console.log('Sync-To-Drive server [', myPid, ']: WARN: Sync in progress'); - return; + return; } const phpConfig = getConfigFromPHP(); if (!phpConfig) { - return; + return; } else if (!phpConfig.synctodrive_enabled) { - console.log('Sync-To-Drive server [', myPid, ']: WARN: Sync script was disabled by config! Aborting!'); + console.log('Sync-To-Drive server [', myPid, ']: WARN: Sync script was disabled by config! Aborting!'); - return; + return; } /* PARSE PHOTOBOOTH CONFIG */ @@ -218,24 +231,26 @@ fs.writeFile(pidFilename, myPid, function (err) { console.log('Sync-To-Drive server [', myPid, ']: Starting server for sync to drive'); console.log('Sync-To-Drive server [', myPid, ']: Interval is [', phpConfig.synctodrive_interval, '] seconds'); - function foreverLoop() { - console.log('Sync-To-Drive server [', myPid, ']: Starting sync process'); const driveInfos = getDriveInfos(parsedConfig); - driveInfos.forEach((element) => {console.log('Sync-To-Drive server [', myPid, ']: Processing drive ', element.name, " -> ", element.path);}) - + driveInfos.forEach((element) => { + console.log('Sync-To-Drive server [', myPid, ']: Processing drive ', element.name, ' -> ', element.path); + }); + const mountedDrives = mountDrives(driveInfos); - driveInfos.forEach((element) => {console.log('Sync-To-Drive server [', myPid, ']: Mounted drive ', element.name, " -> ", element.mountpoint);}) - + driveInfos.forEach((element) => { + console.log('Sync-To-Drive server [', myPid, ']: Mounted drive ', element.name, ' -> ', element.mountpoint); + }); + startSync({ - dataAbsPath: parsedConfig.dataAbsPath, - drives: mountedDrives + dataAbsPath: parsedConfig.dataAbsPath, + drives: mountedDrives }); setTimeout(foreverLoop, phpConfig.synctodrive_interval * 1000); } -foreverLoop() +foreverLoop(); diff --git a/update-booth.sh b/update-booth.sh index 19f29934d..a021da344 100644 --- a/update-booth.sh +++ b/update-booth.sh @@ -94,6 +94,7 @@ COMMON_PACKAGES=( 'php-zip' 'yarn' 'rsync' + 'udisks2' ) if [[ ! -d "${booth_source}" ]]; then From e79d87758508a727009a8fddfe04b2df3f0b0d03 Mon Sep 17 00:00:00 2001 From: jacques42 Date: Sun, 11 Oct 2020 20:08:02 +0100 Subject: [PATCH 21/43] Sync to USB: gulp clean-up Change-Id: Ia2c2da7a90e0374d56ef9ff5e81b5b53a37d6725 --- src/js/remotebuzzer_server.js | 1 - src/js/sync-to-drive.js | 17 ++++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/js/remotebuzzer_server.js b/src/js/remotebuzzer_server.js index 203655ac8..b8533a401 100644 --- a/src/js/remotebuzzer_server.js +++ b/src/js/remotebuzzer_server.js @@ -20,7 +20,6 @@ process.on('uncaughtException', function (err) { console.log('socket.io server [', myPid, ']: Exiting'); /* got to exit now and here - can not recover from error */ - process.exit(); }); diff --git a/src/js/sync-to-drive.js b/src/js/sync-to-drive.js index c28daddf6..d85b5eed7 100755 --- a/src/js/sync-to-drive.js +++ b/src/js/sync-to-drive.js @@ -84,7 +84,9 @@ const mountDrives = (drives) => { for (const drive of drives) { if (!drive.mountpoint) { try { - const mountRes = execSync('export LC_ALL=C; udisksctl mount -b ' + drive.path + '; unset LC_ALL').toString(); + const mountRes = execSync( + 'export LC_ALL=C; udisksctl mount -b ' + drive.path + '; unset LC_ALL' + ).toString(); const mountPoint = mountRes .substr(mountRes.indexOf('at') + 3) .trim() @@ -108,7 +110,7 @@ const startSync = ({dataAbsPath, drives}) => { if (!fs.existsSync(dataAbsPath)) { console.log('Sync-To-Drive server [', myPid, ']: ERROR: Folder ' + dataAbsPath + ' does not exist!'); -return; + return; } console.log('Sync-To-Drive server [', myPid, ']: Source data folder [', dataAbsPath, ']'); @@ -191,24 +193,21 @@ const isProcessRunning = (processName) => { if (PLATFORM === 'win32') { console.error('Sync-To-Drive server [', myPid, ']: Windows is currently not supported!'); - - return; + process.exit(); } if (isProcessRunning('rsync')) { console.log('Sync-To-Drive server [', myPid, ']: WARN: Sync in progress'); - - return; + process.exit(); } const phpConfig = getConfigFromPHP(); if (!phpConfig) { - return; + process.exit(); } else if (!phpConfig.synctodrive_enabled) { console.log('Sync-To-Drive server [', myPid, ']: WARN: Sync script was disabled by config! Aborting!'); - - return; + process.exit(); } /* PARSE PHOTOBOOTH CONFIG */ From abf7627e295da733db099073c0ff715c225f1b3a Mon Sep 17 00:00:00 2001 From: jacques42 Date: Mon, 12 Oct 2020 20:33:52 +0100 Subject: [PATCH 22/43] Sync to USB fixed bug and implemented recommendations --- src/js/sync-to-drive.js | 58 ++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/js/sync-to-drive.js b/src/js/sync-to-drive.js index d85b5eed7..f203fb8fa 100755 --- a/src/js/sync-to-drive.js +++ b/src/js/sync-to-drive.js @@ -4,12 +4,12 @@ const {execSync, spawn} = require('child_process'); const fs = require('fs'); const path = require('path'); -const myPid = process.pid; +const {pid: PID, platform: PLATFORM} = process; //This script needs to be run from within the photobooth directory const API_DIR_NAME = 'api'; const API_FILE_NAME = 'config.php'; -const PLATFORM = process.platform; +//const PLATFORM = process.platform; const getConfigFromPHP = () => { const cmd = 'cd ' + API_DIR_NAME + ' && php ./' + API_FILE_NAME; @@ -19,7 +19,7 @@ const getConfigFromPHP = () => { return JSON.parse(stdout.slice(stdout.indexOf('{'), -1)); } catch (err) { - console.log('Sync-To-Drive server [', myPid, ']: ERROR: Couldnt get config from PHP', err); + console.log('Sync-To-Drive server [', PID, ']: ERROR: Couldnt get config from PHP', err); } return null; @@ -33,10 +33,10 @@ const parseConfig = (config) => { try { return { dataAbsPath: config.foldersAbs.data, - drives: [...config.synctodrive_targets.split(';')] + drives: [config.synctodrive_targets.split(';')] }; } catch (err) { - console.log('Sync-To-Drive server [', myPid, ']: ERROR: Couldt parse config', err); + console.log('Sync-To-Drive server [', PID, ']: ERROR: Couldt parse config', err); } return null; @@ -52,7 +52,7 @@ const getDriveInfos = ({drives}) => { } catch (err) { console.log( 'Sync-To-Drive server [', - myPid, + PID, ']: ERROR: Could not parse the output of lsblk! Please make sure its installed and that it offers JSON output!' ); @@ -60,7 +60,7 @@ const getDriveInfos = ({drives}) => { } if (!json || !json.blockdevices) { - console.log('Sync-To-Drive server [', myPid, ']: ERROR: The output of lsblk was malformed!'); + console.log('Sync-To-Drive server [', PID, ']: ERROR: The output of lsblk was malformed!'); return null; } @@ -94,7 +94,7 @@ const mountDrives = (drives) => { drive.mountpoint = mountPoint; } catch (error) { - console.log('Sync-To-Drive server [', myPid, ']: ERROR: Count mount ' + drive.path); + console.log('Sync-To-Drive server [', PID, ']: ERROR: Count mount ' + drive.path); } } @@ -108,17 +108,17 @@ const mountDrives = (drives) => { const startSync = ({dataAbsPath, drives}) => { if (!fs.existsSync(dataAbsPath)) { - console.log('Sync-To-Drive server [', myPid, ']: ERROR: Folder ' + dataAbsPath + ' does not exist!'); + console.log('Sync-To-Drive server [', PID, ']: ERROR: Folder ' + dataAbsPath + ' does not exist!'); return; } - console.log('Sync-To-Drive server [', myPid, ']: Source data folder [', dataAbsPath, ']'); + console.log('Sync-To-Drive server [', PID, ']: Source data folder [', dataAbsPath, ']'); for (const drive of drives) { console.log( 'Sync-To-Drive server [', - myPid, + PID, ']: Synching to drive [', drive.path, '] -> [', @@ -147,12 +147,12 @@ const startSync = ({dataAbsPath, drives}) => { })(); if (!cmd) { - console.log('Sync-To-Drive server [', myPid, ']: ERROR: No command for syncing!'); + console.log('Sync-To-Drive server [', PID, ']: ERROR: No command for syncing!'); return; } - console.log('Sync-To-Drive server [', myPid, ']: Executing command:', cmd); + console.log('Sync-To-Drive server [', PID, ']: Executing command:', cmd); try { const spwndCmd = spawn(cmd, { @@ -162,7 +162,7 @@ const startSync = ({dataAbsPath, drives}) => { }); spwndCmd.unref(); } catch (err) { - console.log('Sync-To-Drive server [', myPid, ']: ERROR! Couldnt start sync!'); + console.log('Sync-To-Drive server [', PID, ']: ERROR! Couldnt start sync!'); } } }; @@ -192,12 +192,12 @@ const isProcessRunning = (processName) => { }; if (PLATFORM === 'win32') { - console.error('Sync-To-Drive server [', myPid, ']: Windows is currently not supported!'); + console.error('Sync-To-Drive server [', PID, ']: Windows is currently not supported!'); process.exit(); } if (isProcessRunning('rsync')) { - console.log('Sync-To-Drive server [', myPid, ']: WARN: Sync in progress'); + console.log('Sync-To-Drive server [', PID, ']: WARN: Sync in progress'); process.exit(); } @@ -206,43 +206,43 @@ const phpConfig = getConfigFromPHP(); if (!phpConfig) { process.exit(); } else if (!phpConfig.synctodrive_enabled) { - console.log('Sync-To-Drive server [', myPid, ']: WARN: Sync script was disabled by config! Aborting!'); + console.log('Sync-To-Drive server [', PID, ']: WARN: Sync script was disabled by config! Aborting!'); process.exit(); } /* PARSE PHOTOBOOTH CONFIG */ const parsedConfig = parseConfig(phpConfig); -console.log('Sync-To-Drive server [', myPid, ']: Drive names ', ...parsedConfig.drives); +console.log('Sync-To-Drive server [', PID, ']: Drive names ', ...parsedConfig.drives); /* WRITE PROCESS PID FILE */ -const pidFilename = phpConfig.folders.tmp + '/synctodrive_server.pid'; +const pidFilename = path.join(phpConfig.folders.tmp, 'synctodrive_server.pid'); -fs.writeFile(pidFilename, myPid, function (err) { +fs.writeFile(pidFilename, PID, (err) => { if (err) { throw new Error('Unable to write PID file [' + pidFilename + '] - ' + err.message); } - console.log('Sync-To-Drive server [', myPid, ']: PID file created [', pidFilename, ']'); + console.log('Sync-To-Drive server [', PID, ']: PID file created [', pidFilename, ']'); }); /* START LOOP */ -console.log('Sync-To-Drive server [', myPid, ']: Starting server for sync to drive'); -console.log('Sync-To-Drive server [', myPid, ']: Interval is [', phpConfig.synctodrive_interval, '] seconds'); +console.log('Sync-To-Drive server [', PID, ']: Starting server for sync to drive'); +console.log('Sync-To-Drive server [', PID, ']: Interval is [', phpConfig.synctodrive_interval, '] seconds'); -function foreverLoop() { - console.log('Sync-To-Drive server [', myPid, ']: Starting sync process'); +const foreverLoop = () => { + console.log('Sync-To-Drive server [', PID, ']: Starting sync process'); const driveInfos = getDriveInfos(parsedConfig); driveInfos.forEach((element) => { - console.log('Sync-To-Drive server [', myPid, ']: Processing drive ', element.name, ' -> ', element.path); + console.log('Sync-To-Drive server [', PID, ']: Processing drive ', element.name, ' -> ', element.path); }); const mountedDrives = mountDrives(driveInfos); - driveInfos.forEach((element) => { - console.log('Sync-To-Drive server [', myPid, ']: Mounted drive ', element.name, ' -> ', element.mountpoint); + mountedDrives.forEach((element) => { + console.log('Sync-To-Drive server [', PID, ']: Mounted drive ', element.name, ' -> ', element.mountpoint); }); startSync({ @@ -251,5 +251,5 @@ function foreverLoop() { }); setTimeout(foreverLoop, phpConfig.synctodrive_interval * 1000); -} +}; foreverLoop(); From f8e6eb1add831ab7ecc7472dd01859772ad9489c Mon Sep 17 00:00:00 2001 From: jacques42 Date: Mon, 12 Oct 2020 20:35:58 +0100 Subject: [PATCH 23/43] Sync to USB as always, forgot one more thing to change. --- src/js/sync-to-drive.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/js/sync-to-drive.js b/src/js/sync-to-drive.js index f203fb8fa..f7b380b84 100755 --- a/src/js/sync-to-drive.js +++ b/src/js/sync-to-drive.js @@ -9,7 +9,6 @@ const {pid: PID, platform: PLATFORM} = process; //This script needs to be run from within the photobooth directory const API_DIR_NAME = 'api'; const API_FILE_NAME = 'config.php'; -//const PLATFORM = process.platform; const getConfigFromPHP = () => { const cmd = 'cd ' + API_DIR_NAME + ' && php ./' + API_FILE_NAME; From 64266bb819f2c5c38227e964d88493ce22b75147 Mon Sep 17 00:00:00 2001 From: justsomedude Date: Mon, 12 Oct 2020 23:14:48 +0200 Subject: [PATCH 24/43] reverted back to interpolation and some small fixes, new logging function --- src/js/sync-to-drive.js | 71 +++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 41 deletions(-) diff --git a/src/js/sync-to-drive.js b/src/js/sync-to-drive.js index f7b380b84..5086e1957 100755 --- a/src/js/sync-to-drive.js +++ b/src/js/sync-to-drive.js @@ -4,21 +4,23 @@ const {execSync, spawn} = require('child_process'); const fs = require('fs'); const path = require('path'); -const {pid: PID, platform: PLATFORM} = process; //This script needs to be run from within the photobooth directory const API_DIR_NAME = 'api'; const API_FILE_NAME = 'config.php'; +const {pid: PID, platform: PLATFORM} = process; + +const customLog = (...optionalParams) => console.log(`Sync-To-Drive server [${PID}]:`, ...optionalParams); const getConfigFromPHP = () => { - const cmd = 'cd ' + API_DIR_NAME + ' && php ./' + API_FILE_NAME; + const cmd = `cd ${API_DIR_NAME} && php ./${API_FILE_NAME}`; try { const stdout = execSync(cmd).toString(); return JSON.parse(stdout.slice(stdout.indexOf('{'), -1)); } catch (err) { - console.log('Sync-To-Drive server [', PID, ']: ERROR: Couldnt get config from PHP', err); + customLog('ERROR: Couldnt get config from PHP', err); } return null; @@ -32,10 +34,10 @@ const parseConfig = (config) => { try { return { dataAbsPath: config.foldersAbs.data, - drives: [config.synctodrive_targets.split(';')] + drives: config.synctodrive_targets.split(';') }; } catch (err) { - console.log('Sync-To-Drive server [', PID, ']: ERROR: Couldt parse config', err); + customLog('ERROR: Couldt parse config', err); } return null; @@ -49,17 +51,15 @@ const getDriveInfos = ({drives}) => { const output = execSync('export LC_ALL=C; lsblk -ablJO 2>/dev/null; unset LC_ALL').toString(); json = JSON.parse(output); } catch (err) { - console.log( - 'Sync-To-Drive server [', - PID, - ']: ERROR: Could not parse the output of lsblk! Please make sure its installed and that it offers JSON output!' + customLog( + 'ERROR: Could not parse the output of lsblk! Please make sure its installed and that it offers JSON output!' ); return null; } if (!json || !json.blockdevices) { - console.log('Sync-To-Drive server [', PID, ']: ERROR: The output of lsblk was malformed!'); + customLog('ERROR: The output of lsblk was malformed!'); return null; } @@ -83,9 +83,7 @@ const mountDrives = (drives) => { for (const drive of drives) { if (!drive.mountpoint) { try { - const mountRes = execSync( - 'export LC_ALL=C; udisksctl mount -b ' + drive.path + '; unset LC_ALL' - ).toString(); + const mountRes = execSync(`export LC_ALL=C; udisksctl mount -b ${drive.path}; unset LC_ALL`).toString(); const mountPoint = mountRes .substr(mountRes.indexOf('at') + 3) .trim() @@ -93,7 +91,7 @@ const mountDrives = (drives) => { drive.mountpoint = mountPoint; } catch (error) { - console.log('Sync-To-Drive server [', PID, ']: ERROR: Count mount ' + drive.path); + customLog('ERROR: Coudnt mount', drive.path); } } @@ -107,23 +105,15 @@ const mountDrives = (drives) => { const startSync = ({dataAbsPath, drives}) => { if (!fs.existsSync(dataAbsPath)) { - console.log('Sync-To-Drive server [', PID, ']: ERROR: Folder ' + dataAbsPath + ' does not exist!'); + customLog(`ERROR: Folder [${dataAbsPath}] does not exist!`); return; } - console.log('Sync-To-Drive server [', PID, ']: Source data folder [', dataAbsPath, ']'); + customLog(`Source data folder [${dataAbsPath}]`); for (const drive of drives) { - console.log( - 'Sync-To-Drive server [', - PID, - ']: Synching to drive [', - drive.path, - '] -> [', - drive.mountpoint, - ']' - ); + customLog(`Synching to drive [${drive.path}] -> [${drive.mountpoint}]`); const cmd = (() => { switch (process.platform) { @@ -135,7 +125,7 @@ const startSync = ({dataAbsPath, drives}) => { '-a', '--delete-before', '-b', - '--backup-dir=' + path.join(drive.mountpoint, 'deleted'), + `--backup-dir=${path.join(drive.mountpoint, 'deleted')}`, '--ignore-existing', dataAbsPath, path.join(drive.mountpoint, 'sync') @@ -146,12 +136,12 @@ const startSync = ({dataAbsPath, drives}) => { })(); if (!cmd) { - console.log('Sync-To-Drive server [', PID, ']: ERROR: No command for syncing!'); + customLog('ERROR: No command for syncing!'); return; } - console.log('Sync-To-Drive server [', PID, ']: Executing command:', cmd); + customLog('Executing command:', cmd); try { const spwndCmd = spawn(cmd, { @@ -161,7 +151,7 @@ const startSync = ({dataAbsPath, drives}) => { }); spwndCmd.unref(); } catch (err) { - console.log('Sync-To-Drive server [', PID, ']: ERROR! Couldnt start sync!'); + customLog('ERROR! Couldnt start sync!'); } } }; @@ -173,7 +163,7 @@ const isProcessRunning = (processName) => { case 'win32': return 'tasklist'; case 'darwin': - return 'ps -ax | grep ' + processName; + return `ps -ax | grep ${processName}`; case 'linux': return 'ps -A'; default: @@ -191,12 +181,12 @@ const isProcessRunning = (processName) => { }; if (PLATFORM === 'win32') { - console.error('Sync-To-Drive server [', PID, ']: Windows is currently not supported!'); + customLog('Windows is currently not supported!'); process.exit(); } if (isProcessRunning('rsync')) { - console.log('Sync-To-Drive server [', PID, ']: WARN: Sync in progress'); + customLog('WARN: Sync in progress'); process.exit(); } @@ -205,13 +195,13 @@ const phpConfig = getConfigFromPHP(); if (!phpConfig) { process.exit(); } else if (!phpConfig.synctodrive_enabled) { - console.log('Sync-To-Drive server [', PID, ']: WARN: Sync script was disabled by config! Aborting!'); + customLog('WARN: Sync script was disabled by config! Aborting!'); process.exit(); } /* PARSE PHOTOBOOTH CONFIG */ const parsedConfig = parseConfig(phpConfig); -console.log('Sync-To-Drive server [', PID, ']: Drive names ', ...parsedConfig.drives); +customLog('Drive names', ...parsedConfig.drives); /* WRITE PROCESS PID FILE */ const pidFilename = path.join(phpConfig.folders.tmp, 'synctodrive_server.pid'); @@ -221,27 +211,26 @@ fs.writeFile(pidFilename, PID, (err) => { throw new Error('Unable to write PID file [' + pidFilename + '] - ' + err.message); } - console.log('Sync-To-Drive server [', PID, ']: PID file created [', pidFilename, ']'); + customLog(`PID file created [${pidFilename}]`); }); /* START LOOP */ - -console.log('Sync-To-Drive server [', PID, ']: Starting server for sync to drive'); -console.log('Sync-To-Drive server [', PID, ']: Interval is [', phpConfig.synctodrive_interval, '] seconds'); +customLog('Starting server for sync to drive'); +customLog(`Interval is [${phpConfig.synctodrive_interval}] seconds`); const foreverLoop = () => { - console.log('Sync-To-Drive server [', PID, ']: Starting sync process'); + customLog('Starting sync process'); const driveInfos = getDriveInfos(parsedConfig); driveInfos.forEach((element) => { - console.log('Sync-To-Drive server [', PID, ']: Processing drive ', element.name, ' -> ', element.path); + customLog(`Processing drive ${element.name} -> ${element.path}`); }); const mountedDrives = mountDrives(driveInfos); mountedDrives.forEach((element) => { - console.log('Sync-To-Drive server [', PID, ']: Mounted drive ', element.name, ' -> ', element.mountpoint); + customLog(`Mounted drive ${element.name} -> ${element.mountpoint}`); }); startSync({ From 23781f55e5ab924d69d56bd27b36f2fe3537fdb6 Mon Sep 17 00:00:00 2001 From: justsomedude Date: Mon, 12 Oct 2020 23:24:03 +0200 Subject: [PATCH 25/43] dang me always that one typo --- src/js/sync-to-drive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/sync-to-drive.js b/src/js/sync-to-drive.js index 5086e1957..2de3ecff1 100755 --- a/src/js/sync-to-drive.js +++ b/src/js/sync-to-drive.js @@ -91,7 +91,7 @@ const mountDrives = (drives) => { drive.mountpoint = mountPoint; } catch (error) { - customLog('ERROR: Coudnt mount', drive.path); + customLog('ERROR: Couldnt mount', drive.path); } } From aa683ca94c9fc80899f0dc8ca5bf40b332833896 Mon Sep 17 00:00:00 2001 From: justsomedude Date: Wed, 14 Oct 2020 17:02:27 +0200 Subject: [PATCH 26/43] missed interpolation here --- src/js/sync-to-drive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/sync-to-drive.js b/src/js/sync-to-drive.js index 2de3ecff1..4b1389ec2 100755 --- a/src/js/sync-to-drive.js +++ b/src/js/sync-to-drive.js @@ -208,7 +208,7 @@ const pidFilename = path.join(phpConfig.folders.tmp, 'synctodrive_server.pid'); fs.writeFile(pidFilename, PID, (err) => { if (err) { - throw new Error('Unable to write PID file [' + pidFilename + '] - ' + err.message); + throw new Error(`Unable to write PID file [${pidFilename}] - ${err.message}`); } customLog(`PID file created [${pidFilename}]`); From bc9cd72aa476905740ec94c4f76695b5f95e2271 Mon Sep 17 00:00:00 2001 From: justsomedude Date: Wed, 14 Oct 2020 17:03:29 +0200 Subject: [PATCH 27/43] changed name of log function --- src/js/sync-to-drive.js | 44 ++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/js/sync-to-drive.js b/src/js/sync-to-drive.js index 4b1389ec2..ae537ce99 100755 --- a/src/js/sync-to-drive.js +++ b/src/js/sync-to-drive.js @@ -10,7 +10,7 @@ const API_DIR_NAME = 'api'; const API_FILE_NAME = 'config.php'; const {pid: PID, platform: PLATFORM} = process; -const customLog = (...optionalParams) => console.log(`Sync-To-Drive server [${PID}]:`, ...optionalParams); +const log = (...optionalParams) => console.log(`Sync-To-Drive server [${PID}]:`, ...optionalParams); const getConfigFromPHP = () => { const cmd = `cd ${API_DIR_NAME} && php ./${API_FILE_NAME}`; @@ -20,7 +20,7 @@ const getConfigFromPHP = () => { return JSON.parse(stdout.slice(stdout.indexOf('{'), -1)); } catch (err) { - customLog('ERROR: Couldnt get config from PHP', err); + log('ERROR: Couldnt get config from PHP', err); } return null; @@ -37,7 +37,7 @@ const parseConfig = (config) => { drives: config.synctodrive_targets.split(';') }; } catch (err) { - customLog('ERROR: Couldt parse config', err); + log('ERROR: Couldt parse config', err); } return null; @@ -51,7 +51,7 @@ const getDriveInfos = ({drives}) => { const output = execSync('export LC_ALL=C; lsblk -ablJO 2>/dev/null; unset LC_ALL').toString(); json = JSON.parse(output); } catch (err) { - customLog( + log( 'ERROR: Could not parse the output of lsblk! Please make sure its installed and that it offers JSON output!' ); @@ -59,7 +59,7 @@ const getDriveInfos = ({drives}) => { } if (!json || !json.blockdevices) { - customLog('ERROR: The output of lsblk was malformed!'); + log('ERROR: The output of lsblk was malformed!'); return null; } @@ -91,7 +91,7 @@ const mountDrives = (drives) => { drive.mountpoint = mountPoint; } catch (error) { - customLog('ERROR: Couldnt mount', drive.path); + log('ERROR: Couldnt mount', drive.path); } } @@ -105,15 +105,15 @@ const mountDrives = (drives) => { const startSync = ({dataAbsPath, drives}) => { if (!fs.existsSync(dataAbsPath)) { - customLog(`ERROR: Folder [${dataAbsPath}] does not exist!`); + log(`ERROR: Folder [${dataAbsPath}] does not exist!`); return; } - customLog(`Source data folder [${dataAbsPath}]`); + log(`Source data folder [${dataAbsPath}]`); for (const drive of drives) { - customLog(`Synching to drive [${drive.path}] -> [${drive.mountpoint}]`); + log(`Synching to drive [${drive.path}] -> [${drive.mountpoint}]`); const cmd = (() => { switch (process.platform) { @@ -136,12 +136,12 @@ const startSync = ({dataAbsPath, drives}) => { })(); if (!cmd) { - customLog('ERROR: No command for syncing!'); + log('ERROR: No command for syncing!'); return; } - customLog('Executing command:', cmd); + log('Executing command:', cmd); try { const spwndCmd = spawn(cmd, { @@ -151,7 +151,7 @@ const startSync = ({dataAbsPath, drives}) => { }); spwndCmd.unref(); } catch (err) { - customLog('ERROR! Couldnt start sync!'); + log('ERROR! Couldnt start sync!'); } } }; @@ -181,12 +181,12 @@ const isProcessRunning = (processName) => { }; if (PLATFORM === 'win32') { - customLog('Windows is currently not supported!'); + log('Windows is currently not supported!'); process.exit(); } if (isProcessRunning('rsync')) { - customLog('WARN: Sync in progress'); + log('WARN: Sync in progress'); process.exit(); } @@ -195,13 +195,13 @@ const phpConfig = getConfigFromPHP(); if (!phpConfig) { process.exit(); } else if (!phpConfig.synctodrive_enabled) { - customLog('WARN: Sync script was disabled by config! Aborting!'); + log('WARN: Sync script was disabled by config! Aborting!'); process.exit(); } /* PARSE PHOTOBOOTH CONFIG */ const parsedConfig = parseConfig(phpConfig); -customLog('Drive names', ...parsedConfig.drives); +log('Drive names', ...parsedConfig.drives); /* WRITE PROCESS PID FILE */ const pidFilename = path.join(phpConfig.folders.tmp, 'synctodrive_server.pid'); @@ -211,26 +211,26 @@ fs.writeFile(pidFilename, PID, (err) => { throw new Error(`Unable to write PID file [${pidFilename}] - ${err.message}`); } - customLog(`PID file created [${pidFilename}]`); + log(`PID file created [${pidFilename}]`); }); /* START LOOP */ -customLog('Starting server for sync to drive'); -customLog(`Interval is [${phpConfig.synctodrive_interval}] seconds`); +log('Starting server for sync to drive'); +log(`Interval is [${phpConfig.synctodrive_interval}] seconds`); const foreverLoop = () => { - customLog('Starting sync process'); + log('Starting sync process'); const driveInfos = getDriveInfos(parsedConfig); driveInfos.forEach((element) => { - customLog(`Processing drive ${element.name} -> ${element.path}`); + log(`Processing drive ${element.name} -> ${element.path}`); }); const mountedDrives = mountDrives(driveInfos); mountedDrives.forEach((element) => { - customLog(`Mounted drive ${element.name} -> ${element.mountpoint}`); + log(`Mounted drive ${element.name} -> ${element.mountpoint}`); }); startSync({ From 3fe6ecb3806e02b32ac200dbadd1e89b7d9727e0 Mon Sep 17 00:00:00 2001 From: justsomedude Date: Wed, 14 Oct 2020 17:17:31 +0200 Subject: [PATCH 28/43] fixed inf node bug, check rsync every loop, loop with interval --- lib/services_start.php | 14 ++++++-------- src/js/sync-to-drive.js | 38 ++++++++++++++++++++------------------ 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/lib/services_start.php b/lib/services_start.php index 25b2e22f3..b889b6bed 100644 --- a/lib/services_start.php +++ b/lib/services_start.php @@ -23,15 +23,13 @@ \n"); - proc_close(proc_open ($config['nodebin']['cmd']." resources/js/sync-to-drive.js 1>>".$logfile." 2>&1 &", array(), $foo)); + } + else { $logfile = "/dev/null"; } + + print ("\t\n"); + proc_close(proc_open ($config['nodebin']['cmd']." resources/js/sync-to-drive.js 1>>".$logfile." 2>&1 &", array(), $foo)); } ?> diff --git a/src/js/sync-to-drive.js b/src/js/sync-to-drive.js index ae537ce99..62d10611c 100755 --- a/src/js/sync-to-drive.js +++ b/src/js/sync-to-drive.js @@ -180,16 +180,21 @@ const isProcessRunning = (processName) => { } }; +const writePIDFile = (filename) => { + fs.writeFileSync(filename, PID, (err) => { + if (err) { + throw new Error(`Unable to write PID file [${filename}] - ${err.message}`); + } + + log(`PID file created [${filename}]`); + }); +}; + if (PLATFORM === 'win32') { log('Windows is currently not supported!'); process.exit(); } -if (isProcessRunning('rsync')) { - log('WARN: Sync in progress'); - process.exit(); -} - const phpConfig = getConfigFromPHP(); if (!phpConfig) { @@ -204,23 +209,21 @@ const parsedConfig = parseConfig(phpConfig); log('Drive names', ...parsedConfig.drives); /* WRITE PROCESS PID FILE */ -const pidFilename = path.join(phpConfig.folders.tmp, 'synctodrive_server.pid'); - -fs.writeFile(pidFilename, PID, (err) => { - if (err) { - throw new Error(`Unable to write PID file [${pidFilename}] - ${err.message}`); - } - - log(`PID file created [${pidFilename}]`); -}); +writePIDFile(path.join(phpConfig.folders.tmp, 'synctodrive_server.pid')); /* START LOOP */ log('Starting server for sync to drive'); log(`Interval is [${phpConfig.synctodrive_interval}] seconds`); -const foreverLoop = () => { +const syncLoop = () => { log('Starting sync process'); + if (isProcessRunning('rsync')) { + log('WARN: Sync in progress, waiting for next sync call!'); + + return; + } + const driveInfos = getDriveInfos(parsedConfig); driveInfos.forEach((element) => { @@ -237,7 +240,6 @@ const foreverLoop = () => { dataAbsPath: parsedConfig.dataAbsPath, drives: mountedDrives }); - - setTimeout(foreverLoop, phpConfig.synctodrive_interval * 1000); }; -foreverLoop(); + +setInterval(syncLoop, phpConfig.synctodrive_interval * 1000); From e5bd25080c1c2c7b3cbcfba41c60c62439b2d957 Mon Sep 17 00:00:00 2001 From: justsomedude Date: Wed, 14 Oct 2020 17:51:28 +0200 Subject: [PATCH 29/43] writeFileSync has no callback --- src/js/sync-to-drive.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/js/sync-to-drive.js b/src/js/sync-to-drive.js index 62d10611c..9a21da844 100755 --- a/src/js/sync-to-drive.js +++ b/src/js/sync-to-drive.js @@ -181,13 +181,12 @@ const isProcessRunning = (processName) => { }; const writePIDFile = (filename) => { - fs.writeFileSync(filename, PID, (err) => { - if (err) { - throw new Error(`Unable to write PID file [${filename}] - ${err.message}`); - } - + try { + fs.writeFileSync(filename, PID, {mode: 'wx'}); log(`PID file created [${filename}]`); - }); + } catch (err) { + throw new Error(`Unable to write PID file [${filename}] - ${err.message}`); + } }; if (PLATFORM === 'win32') { From c1b4bf890ea82e5355dbe399c43807a97e8b228b Mon Sep 17 00:00:00 2001 From: jacques42 Date: Wed, 14 Oct 2020 22:21:43 +0100 Subject: [PATCH 30/43] USB sync to drive: enhanced services_start / services_stop scripts to be more robust --- lib/services_start.php | 82 ++++++++++++++++++++++++++--------------- lib/services_stop.php | 61 +++++++++++------------------- src/js/sync-to-drive.js | 2 +- 3 files changed, 75 insertions(+), 70 deletions(-) diff --git a/lib/services_start.php b/lib/services_start.php index b889b6bed..8421239cf 100644 --- a/lib/services_start.php +++ b/lib/services_start.php @@ -1,35 +1,59 @@ -\n"); +function processIsRunning ($pName, $pidFile) { + if (file_exists($pidFile)) + { + exec("pgrep -F ".$pidFile, $output, $return); + if ($return == 0) { return true; } // process is active + unlink ($pidFile); // remove stale PID file + } - proc_close(proc_open ($config['nodebin']['cmd']." resources/js/remotebuzzer_server.js 1>>".$logfile." 2>&1 &", array(), $foo)); + exec("pgrep -a -f ".$pName, $output, $return); + return count($output)-1 ? true : false ; // true if process is active +} - } else { - print ("\t\n"); - } -?> - - -\n"); - proc_close(proc_open ($config['nodebin']['cmd']." resources/js/sync-to-drive.js 1>>".$logfile." 2>&1 &", array(), $foo)); - } +if ($config['remotebuzzer_enabled']) { + $connection = @fsockopen('127.0.0.1', $config['remotebuzzer_port']); + + if (! is_resource($connection)) + { + if ($config['dev']) + { + $logfile = $config['foldersAbs']['tmp']."/".$config['remotebuzzer_logfile']; + } + else + { $logfile = "/dev/null"; } + + print ("\t\n"); + + proc_close(proc_open ($config['nodebin']['cmd']." resources/js/remotebuzzer_server.js 1>>".$logfile." 2>&1 &", array(), $foo)); + + } else { + print ("\t\n"); + } + + print("\t\n"); +} + +if ($config['synctodrive_enabled']) { + if ($config['dev']) { + $logfile = $config['foldersAbs']['tmp']."/".$config['synctodrive_logfile']; + } + else { + $logfile = "/dev/null"; + } + + if ( processIsRunning("sync-to-drive.js",$config['foldersAbs']['tmp'].'/synctodrive_server.pid')) + { + print ("\t\n"); + } + else + { + print ("\t\n"); + proc_close(proc_open ($config['nodebin']['cmd']." resources/js/sync-to-drive.js 1>>".$logfile." 2>&1 &", array(), $foo)); + } +} ?> diff --git a/lib/services_stop.php b/lib/services_stop.php index 9e153ec77..739aa8399 100644 --- a/lib/services_stop.php +++ b/lib/services_stop.php @@ -5,48 +5,29 @@ function killProcessIfActive($pName, $pidFile, $logfileName) { - global $config; - - if (file_exists($pidFile)) - { - $myfile = fopen($pidFile, "r"); - $procPID = fread($myfile,100); - fclose($myfile); - - posix_kill($procPID, 9); - - unlink ($pidFile); - - if ($config['dev']) - { - $logfile = $config['folders']['tmp']."/".$logfileName; - $fp = fopen("../".$logfile, 'a');//opens file in append mode. - fwrite($fp, "Service Control [ config ]: Photobooth config has changed, kill existing $pName process (PID ".$procPID.") and remove PID file\n"); - fclose($fp); - } - } - else - { - exec("pgrep -f ".$pName,$pids); - - if (count($pids) > 1) { - foreach ($pids as $procPID) { - if ($config['dev']) - { - $logfile = $config['folders']['tmp']."/".$logfileName; - $fp = fopen("../".$logfile, 'a');//opens file in append mode. - fwrite($fp, "Service Control [ config ]: Photobooth config has changed, killed processes by name ".$pName." -> ".$procPID."\n"); - fclose($fp); - } - - posix_kill($procPID, 9); - } - } - - } -} + global $config; + + exec("pgrep -f ".$pName,$pids); + + if (count($pids) > 1) { + foreach ($pids as $procPID) { + if ($config['dev']) + { + $logfile = $config['foldersAbs']['tmp']."/".$logfileName; + $fp = fopen($logfile, 'a');//opens file in append mode. + fwrite($fp, "Service Control [ config ]: Photobooth config has changed, killed processes by name ".$pName." -> PID ".$procPID."\n"); + fclose($fp); + } + + posix_kill($procPID, 9); + } + } + if (file_exists($pidFile)) { unlink ($pidFile); } + +} killProcessIfActive('remotebuzzer_server.js','../'.$config['folders']['tmp'].'/remotebuzzer_server.pid',$config['remotebuzzer_logfile']); killProcessIfActive('sync-to-drive.js','../'.$config['folders']['tmp'].'/synctodrive_server.pid',$config['synctodrive_logfile']); + ?> diff --git a/src/js/sync-to-drive.js b/src/js/sync-to-drive.js index 9a21da844..a7116f088 100755 --- a/src/js/sync-to-drive.js +++ b/src/js/sync-to-drive.js @@ -182,7 +182,7 @@ const isProcessRunning = (processName) => { const writePIDFile = (filename) => { try { - fs.writeFileSync(filename, PID, {mode: 'wx'}); + fs.writeFileSync(filename, PID, {flag: 'w'}); log(`PID file created [${filename}]`); } catch (err) { throw new Error(`Unable to write PID file [${filename}] - ${err.message}`); From 2d8b131b538c9d3b8440492a76b79980af968799 Mon Sep 17 00:00:00 2001 From: Andreas Blaesius Date: Sat, 24 Oct 2020 12:23:51 +0200 Subject: [PATCH 31/43] lib: formatting Change-Id: Ib0cf489c7a5fd72d4bb1a91164f80cda942a24ae --- lib/config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/config.php b/lib/config.php index 7ac87cc47..301234aff 100644 --- a/lib/config.php +++ b/lib/config.php @@ -23,7 +23,7 @@ 'cmd' => '', 'killcmd' => '', ], - 'nodebin' => [ + 'nodebin' => [ 'cmd' => '', ] ], From 21df2171d1fb92339541518205b5a0836fef4523 Mon Sep 17 00:00:00 2001 From: jacques42 Date: Tue, 27 Oct 2020 21:16:15 +0000 Subject: [PATCH 32/43] Added basic inter-process communication using events, to track child process termination. Using semaphore approach to ensure one child process instance at a time. Installed signal handler to gracefully terminate process upn signals --- lib/services_stop.php | 2 +- src/js/sync-to-drive.js | 104 ++++++++++++++++++++++++++-------------- 2 files changed, 68 insertions(+), 38 deletions(-) diff --git a/lib/services_stop.php b/lib/services_stop.php index 739aa8399..c502b8c54 100644 --- a/lib/services_stop.php +++ b/lib/services_stop.php @@ -19,7 +19,7 @@ function killProcessIfActive($pName, $pidFile, $logfileName) fclose($fp); } - posix_kill($procPID, 9); + posix_kill($procPID, 15); } } diff --git a/src/js/sync-to-drive.js b/src/js/sync-to-drive.js index a7116f088..dfd9e4e97 100755 --- a/src/js/sync-to-drive.js +++ b/src/js/sync-to-drive.js @@ -4,6 +4,10 @@ const {execSync, spawn} = require('child_process'); const fs = require('fs'); const path = require('path'); +const events = require('events'); +const myEmitter = new events.EventEmitter(); +var rsyncSemaphore = false; +var rsyncStartTime = 0; //This script needs to be run from within the photobooth directory const API_DIR_NAME = 'api'; @@ -114,13 +118,14 @@ const startSync = ({dataAbsPath, drives}) => { for (const drive of drives) { log(`Synching to drive [${drive.path}] -> [${drive.mountpoint}]`); - + const cmd = (() => { switch (process.platform) { - case 'win32': - return null; - case 'linux': - return [ + case 'win32': + return null; + case 'linux': + return 'sleep 25'; + return [ 'rsync', '-a', '--delete-before', @@ -141,45 +146,29 @@ const startSync = ({dataAbsPath, drives}) => { return; } - log('Executing command:', cmd); + log('Executing command: <', cmd, '>'); try { - const spwndCmd = spawn(cmd, { - detached: true, + rsyncSemaphore = spawn(cmd, { shell: true, stdio: 'ignore' }); - spwndCmd.unref(); } catch (err) { - log('ERROR! Couldnt start sync!'); + log('ERROR: Could not start rsync:', err.toString()); + return; } - } -}; - -// https://stackoverflow.com/a/58844917 -const isProcessRunning = (processName) => { - const cmd = (() => { - switch (process.platform) { - case 'win32': - return 'tasklist'; - case 'darwin': - return `ps -ax | grep ${processName}`; - case 'linux': - return 'ps -A'; - default: - return false; - } - })(); - - try { - const result = execSync(cmd).toString(); - return result.toLowerCase().indexOf(processName.toLowerCase()) > -1; - } catch (error) { - return null; + log('Rsync child process PID: ',rsyncSemaphore.pid); + + rsyncSemaphore.on('exit', () => { + myEmitter.emit('rsync-completed', rsyncSemaphore.pid); + }); + + rsyncStartTime = new Date(); } }; + const writePIDFile = (filename) => { try { fs.writeFileSync(filename, PID, {flag: 'w'}); @@ -210,19 +199,60 @@ log('Drive names', ...parsedConfig.drives); /* WRITE PROCESS PID FILE */ writePIDFile(path.join(phpConfig.folders.tmp, 'synctodrive_server.pid')); +/* INSTALL HANDLER TO MONITOR CHILD PROCESS EXITS */ +myEmitter.on('rsync-completed', (childPID) => { + log('Rsync child process PID', childPID, 'finished after',(new Date() - rsyncStartTime),'ms'); + rsyncSemaphore = false; +}); + +/* INSTALL HANDLER ON PROCESS SIGHUP SIGTERM, SIGINT */ +const signalHandler = async (signal) => { + log('SignalHandler: received signal', signal, '- wait for possible rsync to exit, umount USB stick and gracefully terminate'); + + if (rsyncSemaphore) + { + log ('SignalHandler: rsync in progress - waiting for termination for max 60 seconds'); + + setTimeout( () => { + log ('SignalHandler: rsync seems stale, terminate child process (SIGKILL)'); + rsyncSemaphore.kill('SIGKILL'); + rsyncSemaphore = false; + }, 60000); + + const Sleep = (milliseconds) => { + return new Promise(resolve => setTimeout(resolve, milliseconds)); + } + + while (rsyncSemaphore) + { + await Sleep(1000); + } + } + + /* umount drives here - eventually have to mountedDrives a global variable */ + log ('SignalHandler: FIXME - umount USB drives'); + + log ('SignalHandler: Gracefully terminating now - bye bye'); + process.exit(); +} + +process.on('SIGTERM', signalHandler); +process.on('SIGHUP', signalHandler); +process.on('SIGINT', signalHandler); + /* START LOOP */ log('Starting server for sync to drive'); log(`Interval is [${phpConfig.synctodrive_interval}] seconds`); const syncLoop = () => { - log('Starting sync process'); - - if (isProcessRunning('rsync')) { - log('WARN: Sync in progress, waiting for next sync call!'); + if (rsyncSemaphore) { + log(`WARN: Sync in progress, waiting for [${phpConfig.synctodrive_interval}] seconds`); return; } + log('Starting sync process'); + const driveInfos = getDriveInfos(parsedConfig); driveInfos.forEach((element) => { From dda71fc82fee21ca929cffeb2b033f0f8721e0cd Mon Sep 17 00:00:00 2001 From: justsomedude Date: Wed, 28 Oct 2020 18:39:52 +0100 Subject: [PATCH 33/43] make kill signal configurable --- lib/services_stop.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/services_stop.php b/lib/services_stop.php index c502b8c54..6f347ec69 100644 --- a/lib/services_stop.php +++ b/lib/services_stop.php @@ -3,7 +3,7 @@ require_once(__DIR__ . '/config.php'); -function killProcessIfActive($pName, $pidFile, $logfileName) +function killProcessIfActive($pName, $pidFile, $logfileName, $killSig) { global $config; @@ -19,7 +19,7 @@ function killProcessIfActive($pName, $pidFile, $logfileName) fclose($fp); } - posix_kill($procPID, 15); + posix_kill($procPID, $killSig); } } @@ -27,7 +27,7 @@ function killProcessIfActive($pName, $pidFile, $logfileName) } -killProcessIfActive('remotebuzzer_server.js','../'.$config['folders']['tmp'].'/remotebuzzer_server.pid',$config['remotebuzzer_logfile']); -killProcessIfActive('sync-to-drive.js','../'.$config['folders']['tmp'].'/synctodrive_server.pid',$config['synctodrive_logfile']); +killProcessIfActive('remotebuzzer_server.js','../'.$config['folders']['tmp'].'/remotebuzzer_server.pid',$config['remotebuzzer_logfile'], 9); +killProcessIfActive('sync-to-drive.js','../'.$config['folders']['tmp'].'/synctodrive_server.pid',$config['synctodrive_logfile'], 15); ?> From 15814151dec2b0c538a0418ce67e814ff51e36a4 Mon Sep 17 00:00:00 2001 From: justsomedude Date: Wed, 28 Oct 2020 19:22:22 +0100 Subject: [PATCH 34/43] fixed/ignored all eslint errors, added unmount function, fixed indent (untested) --- src/js/sync-to-drive.js | 112 +++++++++++++++++++++++----------------- 1 file changed, 64 insertions(+), 48 deletions(-) diff --git a/src/js/sync-to-drive.js b/src/js/sync-to-drive.js index dfd9e4e97..038458ba5 100755 --- a/src/js/sync-to-drive.js +++ b/src/js/sync-to-drive.js @@ -1,18 +1,20 @@ -#!/usr/bin/env node -/* eslint-disable node/shebang */ +/* This script needs to be run from within the photobooth directory */ +/* Imports */ const {execSync, spawn} = require('child_process'); const fs = require('fs'); const path = require('path'); const events = require('events'); -const myEmitter = new events.EventEmitter(); -var rsyncSemaphore = false; -var rsyncStartTime = 0; -//This script needs to be run from within the photobooth directory +/* Variables */ const API_DIR_NAME = 'api'; const API_FILE_NAME = 'config.php'; const {pid: PID, platform: PLATFORM} = process; +const myEmitter = new events.EventEmitter(); +let rsyncSemaphore = null; +let rsyncStartTime = 0; + +/* Variables */ const log = (...optionalParams) => console.log(`Sync-To-Drive server [${PID}]:`, ...optionalParams); @@ -118,14 +120,13 @@ const startSync = ({dataAbsPath, drives}) => { for (const drive of drives) { log(`Synching to drive [${drive.path}] -> [${drive.mountpoint}]`); - + const cmd = (() => { switch (process.platform) { - case 'win32': - return null; - case 'linux': - return 'sleep 25'; - return [ + case 'win32': + return null; + case 'linux': + return [ 'rsync', '-a', '--delete-before', @@ -155,20 +156,21 @@ const startSync = ({dataAbsPath, drives}) => { }); } catch (err) { log('ERROR: Could not start rsync:', err.toString()); - return; + + return; } - log('Rsync child process PID: ',rsyncSemaphore.pid); - + log('rsync child process PID: ', rsyncSemaphore.pid); + + // eslint-disable-next-line no-loop-func rsyncSemaphore.on('exit', () => { myEmitter.emit('rsync-completed', rsyncSemaphore.pid); }); - - rsyncStartTime = new Date(); + + rsyncStartTime = new Date(); } }; - const writePIDFile = (filename) => { try { fs.writeFileSync(filename, PID, {flag: 'w'}); @@ -178,6 +180,19 @@ const writePIDFile = (filename) => { } }; +const unmountDrives = (drives) => { + for (const drive of drives) { + try { + execSync(`export LC_ALL=C; udisksctl unmount -b ${drive.path}; unset LC_ALL`).toString(); + log('INFO: Unmounted drive', drive.path); + } catch (error) { + log('ERROR: Couldnt unmount drive', drive.path); + } + } +}; + +/* Execution starts here */ + if (PLATFORM === 'win32') { log('Windows is currently not supported!'); process.exit(); @@ -201,53 +216,54 @@ writePIDFile(path.join(phpConfig.folders.tmp, 'synctodrive_server.pid')); /* INSTALL HANDLER TO MONITOR CHILD PROCESS EXITS */ myEmitter.on('rsync-completed', (childPID) => { - log('Rsync child process PID', childPID, 'finished after',(new Date() - rsyncStartTime),'ms'); + log('Rsync child process PID', childPID, 'finished after', new Date() - rsyncStartTime, 'ms'); rsyncSemaphore = false; }); /* INSTALL HANDLER ON PROCESS SIGHUP SIGTERM, SIGINT */ const signalHandler = async (signal) => { - log('SignalHandler: received signal', signal, '- wait for possible rsync to exit, umount USB stick and gracefully terminate'); - - if (rsyncSemaphore) - { - log ('SignalHandler: rsync in progress - waiting for termination for max 60 seconds'); - - setTimeout( () => { - log ('SignalHandler: rsync seems stale, terminate child process (SIGKILL)'); - rsyncSemaphore.kill('SIGKILL'); - rsyncSemaphore = false; - }, 60000); - - const Sleep = (milliseconds) => { - return new Promise(resolve => setTimeout(resolve, milliseconds)); - } - - while (rsyncSemaphore) - { - await Sleep(1000); - } - } + log( + 'SignalHandler: received signal', + signal, + '- wait for possible rsync to exit, umount USB stick and gracefully terminate' + ); + + if (rsyncSemaphore) { + log('SignalHandler: rsync in progress - waiting for termination for max 60 seconds'); + + setTimeout(() => { + log('SignalHandler: rsync seems stale, terminate child process (SIGKILL)'); + rsyncSemaphore.kill('SIGKILL'); + rsyncSemaphore = false; + }, 60000); + + const sleep = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)); + + // eslint-disable-next-line no-unmodified-loop-condition + while (rsyncSemaphore) { + // eslint-disable-next-line no-await-in-loop + await sleep(1000); + } + } /* umount drives here - eventually have to mountedDrives a global variable */ - log ('SignalHandler: FIXME - umount USB drives'); - - log ('SignalHandler: Gracefully terminating now - bye bye'); + log('SignalHandler: FIXME - umount USB drives'); + unmountDrives([]); + + log('SignalHandler: Gracefully terminating now - bye bye'); process.exit(); -} +}; -process.on('SIGTERM', signalHandler); -process.on('SIGHUP', signalHandler); -process.on('SIGINT', signalHandler); +['SIGTERM', 'SIGHUP', 'SIGINT'].forEach((term) => process.on(term, signalHandler.bind(null, term))); /* START LOOP */ log('Starting server for sync to drive'); log(`Interval is [${phpConfig.synctodrive_interval}] seconds`); const syncLoop = () => { - if (rsyncSemaphore) { log(`WARN: Sync in progress, waiting for [${phpConfig.synctodrive_interval}] seconds`); + return; } From a49360348d9f821441bb56af632632cfd7b76057 Mon Sep 17 00:00:00 2001 From: justsomedude Date: Wed, 28 Oct 2020 19:30:42 +0100 Subject: [PATCH 35/43] copy paste i tell ya --- src/js/sync-to-drive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/sync-to-drive.js b/src/js/sync-to-drive.js index 038458ba5..468cfcd9e 100755 --- a/src/js/sync-to-drive.js +++ b/src/js/sync-to-drive.js @@ -14,7 +14,7 @@ const myEmitter = new events.EventEmitter(); let rsyncSemaphore = null; let rsyncStartTime = 0; -/* Variables */ +/* Functions */ const log = (...optionalParams) => console.log(`Sync-To-Drive server [${PID}]:`, ...optionalParams); From 854036c0e610567ad45c49dfb6cac70fbcbf75c7 Mon Sep 17 00:00:00 2001 From: jacques42 Date: Sat, 19 Dec 2020 15:48:24 +0100 Subject: [PATCH 36/43] Finalize code for initial version to be included in next PB release Change-Id: Ia189c1c8a2b764cc776e7587e35466dfcf2b0814 --- config/config.inc.php | 4 +- lib/configsetup.inc.php | 8 +- manual/index.php | 4 + resources/lang/de.json | 6 +- resources/lang/en.json | 6 +- src/js/sync-to-drive.js | 228 ++++++++++++++++++++-------------------- 6 files changed, 130 insertions(+), 126 deletions(-) diff --git a/config/config.inc.php b/config/config.inc.php index cab19b383..13234c612 100644 --- a/config/config.inc.php +++ b/config/config.inc.php @@ -251,7 +251,7 @@ $config['reset_remove_config'] = true; -// B A C K U P / S Y N C S C R I P T + // S Y N C T O U S B S T I C K $config['synctodrive_enabled'] = false; -$config['synctodrive_targets'] = 'photobooth'; //Default targets for the sync sctipt +$config['synctodrive_target'] = 'photobooth'; //Default target for the sync script $config['synctodrive_interval'] = 300; diff --git a/lib/configsetup.inc.php b/lib/configsetup.inc.php index ec0420e68..69ad3216e 100644 --- a/lib/configsetup.inc.php +++ b/lib/configsetup.inc.php @@ -1137,11 +1137,11 @@ 'name' => 'synctodrive_enabled', 'value' => $config['synctodrive_enabled'] ], - 'targets' => [ + 'target' => [ 'type' => 'input', - 'placeholder' => $defaultConfig['synctodrive_targets'], - 'name' => 'synctodrive_targets', - 'value' => $config['synctodrive_targets'] + 'placeholder' => $defaultConfig['synctodrive_target'], + 'name' => 'synctodrive_target', + 'value' => $config['synctodrive_target'] ], 'interval' => [ 'type' => 'range', diff --git a/manual/index.php b/manual/index.php index 51f49074f..50f1fdaba 100644 --- a/manual/index.php +++ b/manual/index.php @@ -49,6 +49,10 @@ echo '
'; foreach($fields as $key => $field) { + if ($key == 'platform') { + continue; + }; + echo '
'; switch($field['type']) { case 'checkbox': diff --git a/resources/lang/de.json b/resources/lang/de.json index b667e028b..04d222d3c 100644 --- a/resources/lang/de.json +++ b/resources/lang/de.json @@ -256,7 +256,7 @@ "manual_slideshow_refreshTime": "Die Standalone-Diashow wird nach eingegebenen Sekunden neu geladen.", "manual_synctodrive_enabled": "Aktiviert das automatische Übertragen neuer Bilder auf einen USB Stick mittels rsync ", "manual_synctodrive_interval": "Intervall in Sekunden für automatische Synchronisierung ", - "manual_synctodrive_targets": "Liste der USB Geräte, anzugeben enweder mit Gerätename (e.g. sda1), oder den kompletten Pfad (e.g /dev/sda1) oder den Namen des USB Sticks (e.g. photobooth). Einträge mit Semikolon separieren ", + "manual_synctodrive_target": "Identifiziert das USB Gerät entweder durch den Namen des USB Sticks (e.g. photobooth), den kompletten Geräte-Pfad (e.g /dev/sda1) oder das subsystem (e.g. sda).", "manual_take_collage_frame": "Wenn diese Option aktiviert ist, wird nach der Aufnahme ein definierter Rahmen auf Ihre Collage angewendet.", "manual_take_collage_frame_always": "Wenn aktiviert, wird der definierte Rahmen auf jedes Bild Ihrer Collage angewendet, anstatt einmal nach der Aufnahme der Collage. \"Foto-Collage mit Rahmen aufnehmen\" muss aktiviert sein.", "manual_take_frame": "Wenn diese Option aktiviert ist, wird nach der Aufnahme ein definierter Rahmen auf Ihr Bild angewendet.", @@ -362,10 +362,10 @@ "startScreen": "

Photobooth

Webinterface

by André Rinas", "success": "Erfolgreich gespeichert!", "symbol": "Symbol auswählen", - "synctodrive": "Bilder automatisch auf USB übertragen", + "synctodrive": "Bilder automatisch auf USB Stick übertragen", "synctodrive_enabled": "Aktivieren", "synctodrive_interval": "Automatisches Synchronisierungs-Intervall", - "synctodrive_targets": "USB Device (Liste mit Semikolon separiert)", + "synctodrive_target": "USB Gerät", "takeCollage": "Collage erstellen!", "takePhoto": "Foto erstellen!", "take_collage_frame": "Foto-Collage mit Rahmen aufnehmen", diff --git a/resources/lang/en.json b/resources/lang/en.json index e8cfb2cd5..d70f2e84a 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -267,7 +267,7 @@ "manual_slideshow_refreshTime": "Refresh standalone slideshow after entered seconds.", "manual_synctodrive_enabled": "Enable automatic syncing of new pictures to USB device using rsync", "manual_synctodrive_interval": "Seconds to trigger syncing as interval", - "manual_synctodrive_targets": "List of USB devices, either by device name (e.g. sda1), or device full path (e.g /dev/sda1) or USB stick name (e.g. photobooth). List separated by semicolon ", + "manual_synctodrive_target": " Pattern to identify USB device either by USB stick name (e.g. photobooth), device full path (e.g /dev/sda1) or device subsystem (e.g. sda).", "manual_take_collage_frame": "If enabled, defined frame will be applied to your collage after taking it.", "manual_take_collage_frame_always": "If enabled, defined collage frame will be applied to each picture of your collage instead of applying once at the end of the collage. \"Take collage with frame\" needs to be enabled.", "manual_take_frame": "If enabled, defined frame will be applied to your picture after taking it.", @@ -380,10 +380,10 @@ "startScreen": "

Photobooth

Webinterface

by André Rinas", "success": "Saved successful!", "symbol": "Choose a symbol", - "synctodrive": "Sync pictures to USB", + "synctodrive": "Sync pictures to USB stick", "synctodrive_enabled": "Enable", "synctodrive_interval": "Autmated syncing interval", - "synctodrive_targets": "USB devices (list separated by semicolon)", + "synctodrive_target": "USB device identifier", "takeCollage": "Take Collage!", "takePhoto": "Take Pic!", "take_collage_frame": "Take collage with frame", diff --git a/src/js/sync-to-drive.js b/src/js/sync-to-drive.js index 468cfcd9e..938e3464c 100755 --- a/src/js/sync-to-drive.js +++ b/src/js/sync-to-drive.js @@ -9,6 +9,7 @@ const events = require('events'); /* Variables */ const API_DIR_NAME = 'api'; const API_FILE_NAME = 'config.php'; +const SYNC_DESTINATION_DIR = 'photobooth-pic-sync'; const {pid: PID, platform: PLATFORM} = process; const myEmitter = new events.EventEmitter(); let rsyncSemaphore = null; @@ -26,7 +27,7 @@ const getConfigFromPHP = () => { return JSON.parse(stdout.slice(stdout.indexOf('{'), -1)); } catch (err) { - log('ERROR: Couldnt get config from PHP', err); + log('ERROR: Unable to load photobooth config', err); } return null; @@ -40,18 +41,20 @@ const parseConfig = (config) => { try { return { dataAbsPath: config.foldersAbs.data, - drives: config.synctodrive_targets.split(';') + drive: config.synctodrive_target }; } catch (err) { - log('ERROR: Couldt parse config', err); + log('ERROR: unable to parse sync-to-drive config', err); } return null; }; -const getDriveInfos = ({drives}) => { +const getDriveInfo = ({drive}) => { let json = null; + drive = drive.toLowerCase(); + try { //Assuming that the lsblk version supports JSON output! const output = execSync('export LC_ALL=C; lsblk -ablJO 2>/dev/null; unset LC_ALL').toString(); @@ -70,105 +73,96 @@ const getDriveInfos = ({drives}) => { return null; } - return json.blockdevices.reduce((arr, blk) => { - if ( - drives.some( - (drive) => drive === blk.name || drive === blk.kname || drive === blk.path || drive === blk.label - ) - ) { - arr.push(blk); - } - - return arr; - }, []); + return json.blockdevices.find( + (blk) => blk.subsystems.includes('usb') && + ((blk.name && drive === blk.name.toLowerCase()) || + (blk.kname && drive === blk.kname.toLowerCase()) || + (blk.path && drive === blk.path.toLowerCase()) || + (blk.label && drive === blk.label.toLowerCase())) + ); }; -const mountDrives = (drives) => { - const result = []; - - for (const drive of drives) { - if (!drive.mountpoint) { - try { - const mountRes = execSync(`export LC_ALL=C; udisksctl mount -b ${drive.path}; unset LC_ALL`).toString(); - const mountPoint = mountRes - .substr(mountRes.indexOf('at') + 3) - .trim() - .replace(/[\n.]/gu, ''); - - drive.mountpoint = mountPoint; - } catch (error) { - log('ERROR: Couldnt mount', drive.path); - } - } +const mountDrive = (drive) => { + if (!drive.mountpoint) { + try { + const mountRes = execSync(`export LC_ALL=C; udisksctl mount -b ${drive.path}; unset LC_ALL`).toString(); + const mountPoint = mountRes + .substr(mountRes.indexOf('at') + 3) + .trim() + .replace(/[\n.]/gu, ''); - if (drive.mountpoint) { - result.push(drive); + drive.mountpoint = mountPoint; + } catch (error) { + log('ERROR: unable to mount drive', drive.path); + drive = null; } } - return result; + return drive; }; -const startSync = ({dataAbsPath, drives}) => { +const startSync = ({dataAbsPath, drive}) => { if (!fs.existsSync(dataAbsPath)) { log(`ERROR: Folder [${dataAbsPath}] does not exist!`); return; } + log('Starting sync to USB drive ...'); log(`Source data folder [${dataAbsPath}]`); - - for (const drive of drives) { - log(`Synching to drive [${drive.path}] -> [${drive.mountpoint}]`); - - const cmd = (() => { - switch (process.platform) { - case 'win32': - return null; - case 'linux': - return [ - 'rsync', - '-a', - '--delete-before', - '-b', - `--backup-dir=${path.join(drive.mountpoint, 'deleted')}`, - '--ignore-existing', - dataAbsPath, - path.join(drive.mountpoint, 'sync') - ].join(' '); - default: - return null; - } - })(); - - if (!cmd) { - log('ERROR: No command for syncing!'); - - return; + log(`Syncing to drive [${drive.path}] -> [${drive.mountpoint}]`); + + const cmd = (() => { + switch (process.platform) { + case 'win32': + return null; + case 'linux': + return [ + 'rsync', + '-a', + '--delete-before', + '-b', + `--backup-dir=${path.join(drive.mountpoint, 'deleted')}`, + '--ignore-existing', + '--include=\'*.jpg\'', + '--include=\'*/\'', + '--exclude=\'*\'', + '--prune-empty-dirs', + dataAbsPath, + path.join(drive.mountpoint, SYNC_DESTINATION_DIR) + ].join(' '); + default: + return null; } + })(); - log('Executing command: <', cmd, '>'); + if (!cmd) { + log('ERROR: No command for syncing!'); - try { - rsyncSemaphore = spawn(cmd, { - shell: true, - stdio: 'ignore' - }); - } catch (err) { - log('ERROR: Could not start rsync:', err.toString()); - - return; - } + return; + } - log('rsync child process PID: ', rsyncSemaphore.pid); + log('Executing command: <', cmd, '>'); - // eslint-disable-next-line no-loop-func - rsyncSemaphore.on('exit', () => { - myEmitter.emit('rsync-completed', rsyncSemaphore.pid); + try { + rsyncSemaphore = spawn(cmd, { + shell: true, + stdio: 'ignore' }); + } catch (err) { + log('ERROR: Could not start rsync:', err.toString()); - rsyncStartTime = new Date(); + return; } + + log('Rsync child process PID:', rsyncSemaphore.pid, 'started'); + + // eslint-disable-next-line no-loop-func + rsyncSemaphore.on('exit', () => { + myEmitter.emit('rsync-completed', rsyncSemaphore.pid); + }); + + rsyncStartTime = new Date(); }; const writePIDFile = (filename) => { @@ -180,14 +174,15 @@ const writePIDFile = (filename) => { } }; -const unmountDrives = (drives) => { - for (const drive of drives) { - try { - execSync(`export LC_ALL=C; udisksctl unmount -b ${drive.path}; unset LC_ALL`).toString(); - log('INFO: Unmounted drive', drive.path); - } catch (error) { - log('ERROR: Couldnt unmount drive', drive.path); - } +const unmountDrive = () => { + const driveInfo = getDriveInfo(parsedConfig); + const mountedDrive = mountDrive(driveInfo); + + try { + execSync(`export LC_ALL=C; udisksctl unmount -b ${mountedDrive.path}; unset LC_ALL`).toString(); + log('Unmounted drive', mountedDrive.path); + } catch (error) { + log('ERROR: unable to unmount drive', mountedDrive.path); } }; @@ -198,34 +193,36 @@ if (PLATFORM === 'win32') { process.exit(); } +/* GET PHOTOBOOTH CONFIG */ const phpConfig = getConfigFromPHP(); if (!phpConfig) { process.exit(); } else if (!phpConfig.synctodrive_enabled) { - log('WARN: Sync script was disabled by config! Aborting!'); + log('WARN: Sync script disabled by config - exiting'); process.exit(); } /* PARSE PHOTOBOOTH CONFIG */ const parsedConfig = parseConfig(phpConfig); -log('Drive names', ...parsedConfig.drives); +log('USB target ', ...parsedConfig.drive); /* WRITE PROCESS PID FILE */ writePIDFile(path.join(phpConfig.folders.tmp, 'synctodrive_server.pid')); /* INSTALL HANDLER TO MONITOR CHILD PROCESS EXITS */ myEmitter.on('rsync-completed', (childPID) => { - log('Rsync child process PID', childPID, 'finished after', new Date() - rsyncStartTime, 'ms'); + log('Rsync child process PID:', childPID, 'finished after', new Date() - rsyncStartTime, 'ms'); rsyncSemaphore = false; + log('... finished sync, going back to sleep'); }); -/* INSTALL HANDLER ON PROCESS SIGHUP SIGTERM, SIGINT */ +/* INSTALL HANDLER ON SERVER PROCESS SIGHUP SIGTERM, SIGINT */ const signalHandler = async (signal) => { log( 'SignalHandler: received signal', signal, - '- wait for possible rsync to exit, umount USB stick and gracefully terminate' + '- wait for possible rsync to complete, umount USB stick and gracefully terminate' ); if (rsyncSemaphore) { @@ -246,18 +243,17 @@ const signalHandler = async (signal) => { } } - /* umount drives here - eventually have to mountedDrives a global variable */ - log('SignalHandler: FIXME - umount USB drives'); - unmountDrives([]); + /* umount drives here */ + unmountDrive(); - log('SignalHandler: Gracefully terminating now - bye bye'); + log('SignalHandler: gracefully terminating now - bye bye'); process.exit(); }; ['SIGTERM', 'SIGHUP', 'SIGINT'].forEach((term) => process.on(term, signalHandler.bind(null, term))); -/* START LOOP */ -log('Starting server for sync to drive'); +/* START FOREVER LOOP */ +log('Starting server process'); log(`Interval is [${phpConfig.synctodrive_interval}] seconds`); const syncLoop = () => { @@ -267,24 +263,28 @@ const syncLoop = () => { return; } - log('Starting sync process'); + log('Checking for USB drive'); - const driveInfos = getDriveInfos(parsedConfig); - - driveInfos.forEach((element) => { - log(`Processing drive ${element.name} -> ${element.path}`); - }); - - const mountedDrives = mountDrives(driveInfos); + const driveInfo = getDriveInfo(parsedConfig); + try { + log(`Processing drive ${driveInfo.label} -> ${driveInfo.path}`); + } catch (error) { + return; + } - mountedDrives.forEach((element) => { - log(`Mounted drive ${element.name} -> ${element.mountpoint}`); - }); + const mountedDrive = mountDrive(driveInfo); + try { + log(`Mounted drive ${mountedDrive.name} -> ${mountedDrive.mountpoint}`); + } catch (error) { + return; + } - startSync({ - dataAbsPath: parsedConfig.dataAbsPath, - drives: mountedDrives - }); + if (mountedDrive) { + startSync({ + dataAbsPath: parsedConfig.dataAbsPath, + drive: mountedDrive + }); + } }; setInterval(syncLoop, phpConfig.synctodrive_interval * 1000); From 4bb66a64994c6bcf0ca3814a5116dbaa4a986ffd Mon Sep 17 00:00:00 2001 From: jacques42 Date: Sat, 19 Dec 2020 20:34:58 +0100 Subject: [PATCH 37/43] Final adjustments, prettier, eslint --- api/admin.php | 2 +- config/config.inc.php | 2 +- faq/faq.md | 20 ++++++-------------- install-raspbian.sh | 5 +++-- lib/configsetup.inc.php | 6 +++--- src/js/sync-to-drive.js | 1 - 6 files changed, 14 insertions(+), 22 deletions(-) diff --git a/api/admin.php b/api/admin.php index 9f0f3cfd4..5331b9e1e 100644 --- a/api/admin.php +++ b/api/admin.php @@ -100,7 +100,7 @@ if ($os === 'windows') { $newConfig['remotebuzzer_enabled'] = false; $newConfig['synctodrive_enabled'] = false; - } + } $content = " Specifically for device `/dev/sda1` being mounted - > For a device being mounted with the name `photobooth` - > Any device being mounted with the string `sdb` in it's device name - -``` -Example: - /dev/sda1;photobooth;sdb -``` -Pictures will be synced to all devices matched by this list, as long as they are mounted (aka USB stick is plugged in) +Pictures will be synced to the USB stick matched by the pattern, as long as it is mounted (aka USB stick is plugged in) +Debugging: switch on dev settings for server logs to be written to the `data/tmp` directory of the photobooth installation (i.e. `data/tmp/synctodrive_server.log`). diff --git a/install-raspbian.sh b/install-raspbian.sh index 9b32982e1..a38def5e4 100755 --- a/install-raspbian.sh +++ b/install-raspbian.sh @@ -319,8 +319,9 @@ cat >> /boot/config.txt << EOF dtoverlay=gpio-no-irq EOF -echo -e "\033[0;33m### You'd probably like to use the file backup feature." -read -p "### Would you like to enable the file backup? [y/N] " -n 1 -r +echo -e "\033[0;33m### Sync to USB - this feature will automatically copy (sync) new pictures to a USB stick." +echo -e "### The actual configuration will be done in the admin panel but we need to setup Raspberry Pi OS first" +read -p "### Would you like to enable the USB sync file backup? [y/N] " -n 1 -r echo -e "\033[0m" if [[ $REPLY =~ ^[Yy]$ ]] then diff --git a/lib/configsetup.inc.php b/lib/configsetup.inc.php index 69ad3216e..8486985f8 100644 --- a/lib/configsetup.inc.php +++ b/lib/configsetup.inc.php @@ -1129,9 +1129,9 @@ 'name' => 'remotebuzzer_logfile', 'value' => $config['remotebuzzer_logfile'] ] - ], + ], 'synctodrive' => [ - 'platform' => 'linux', + 'platform' => 'linux', 'synctodrive_enabled' => [ 'type' => 'checkbox', 'name' => 'synctodrive_enabled', @@ -1158,7 +1158,7 @@ 'name' => 'synctodrive_logfile', 'value' => $config['synctodrive_logfile'] ] - ], + ], 'reset' => [ 'remove_images' => [ 'type' => 'checkbox', diff --git a/src/js/sync-to-drive.js b/src/js/sync-to-drive.js index 938e3464c..32b4679c3 100755 --- a/src/js/sync-to-drive.js +++ b/src/js/sync-to-drive.js @@ -157,7 +157,6 @@ const startSync = ({dataAbsPath, drive}) => { log('Rsync child process PID:', rsyncSemaphore.pid, 'started'); - // eslint-disable-next-line no-loop-func rsyncSemaphore.on('exit', () => { myEmitter.emit('rsync-completed', rsyncSemaphore.pid); }); From 67562c56040500a73402986eecaacb9a9ec77514 Mon Sep 17 00:00:00 2001 From: Andreas Blaesius Date: Sat, 19 Dec 2020 21:14:45 +0100 Subject: [PATCH 38/43] docs (README): add thatonedude3470 to contributors Change-Id: I03bc9e7ee79a762c03fbb84c74788c8834f670d4 --- README.md | 1 + src/js/sync-to-drive.js | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 17d3526ef..b12b8bc44 100644 --- a/README.md +++ b/README.md @@ -207,3 +207,4 @@ If you like my work and like to keep me motivated you can buy me a coconut water - [joiyco](https://github.com/joiyco) - [EccoB](https://github.com/EccoB) - [couz74](https://github.com/couz74) +- [thatonedude3470](https://github.com/thatonedude3470) diff --git a/src/js/sync-to-drive.js b/src/js/sync-to-drive.js index 32b4679c3..fe50189b9 100755 --- a/src/js/sync-to-drive.js +++ b/src/js/sync-to-drive.js @@ -74,7 +74,8 @@ const getDriveInfo = ({drive}) => { } return json.blockdevices.find( - (blk) => blk.subsystems.includes('usb') && + (blk) => + blk.subsystems.includes('usb') && ((blk.name && drive === blk.name.toLowerCase()) || (blk.kname && drive === blk.kname.toLowerCase()) || (blk.path && drive === blk.path.toLowerCase()) || @@ -124,9 +125,9 @@ const startSync = ({dataAbsPath, drive}) => { '-b', `--backup-dir=${path.join(drive.mountpoint, 'deleted')}`, '--ignore-existing', - '--include=\'*.jpg\'', - '--include=\'*/\'', - '--exclude=\'*\'', + "--include='*.jpg'", + "--include='*/'", + "--exclude='*'", '--prune-empty-dirs', dataAbsPath, path.join(drive.mountpoint, SYNC_DESTINATION_DIR) From 0577b5befdb4264e0a9ca75d40a3d04d59e47235 Mon Sep 17 00:00:00 2001 From: Andreas Blaesius Date: Sat, 19 Dec 2020 21:18:48 +0100 Subject: [PATCH 39/43] fix (js): fix lint issues Change-Id: I0becbd9f1bbb7f3788c30572f2eb6a58979354c6 --- src/js/sync-to-drive.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/js/sync-to-drive.js b/src/js/sync-to-drive.js index fe50189b9..32b4679c3 100755 --- a/src/js/sync-to-drive.js +++ b/src/js/sync-to-drive.js @@ -74,8 +74,7 @@ const getDriveInfo = ({drive}) => { } return json.blockdevices.find( - (blk) => - blk.subsystems.includes('usb') && + (blk) => blk.subsystems.includes('usb') && ((blk.name && drive === blk.name.toLowerCase()) || (blk.kname && drive === blk.kname.toLowerCase()) || (blk.path && drive === blk.path.toLowerCase()) || @@ -125,9 +124,9 @@ const startSync = ({dataAbsPath, drive}) => { '-b', `--backup-dir=${path.join(drive.mountpoint, 'deleted')}`, '--ignore-existing', - "--include='*.jpg'", - "--include='*/'", - "--exclude='*'", + '--include=\'*.jpg\'', + '--include=\'*/\'', + '--exclude=\'*\'', '--prune-empty-dirs', dataAbsPath, path.join(drive.mountpoint, SYNC_DESTINATION_DIR) From 1fbdaef5c0aa83f164a1418d4dad8ffc1bcf4b35 Mon Sep 17 00:00:00 2001 From: jacques42 Date: Mon, 21 Dec 2020 21:03:24 +0100 Subject: [PATCH 40/43] Added babel polyfills for Promises --- package.json | 2 ++ src/js/sync-to-drive.js | 2 ++ 2 files changed, 4 insertions(+) diff --git a/package.json b/package.json index 07d085ebe..fac668289 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ ], "dependencies": { "@andreasremdt/simple-translator": "^2.0.3", + "core-js": "^3.8.1", "font-awesome": "^4.7.0", "gh-markdown-cli": "^0.2.0", "github-markdown-css": "^4.0.0", @@ -48,6 +49,7 @@ "marvinj": "^1.0.0", "normalize.css": "^8.0.1", "npm-run-all": "^4.1.5", + "regenerator-runtime": "^0.13.7", "rpio": "^2.1.1", "socket.io": "^2.3.0", "socket.io-client": "^2.3.1", diff --git a/src/js/sync-to-drive.js b/src/js/sync-to-drive.js index 32b4679c3..c6a808ff5 100755 --- a/src/js/sync-to-drive.js +++ b/src/js/sync-to-drive.js @@ -1,4 +1,6 @@ /* This script needs to be run from within the photobooth directory */ +require('core-js/stable'); +require('regenerator-runtime/runtime'); /* Imports */ const {execSync, spawn} = require('child_process'); From 19339a8d621952b30d3a12e1bc2cc42442515645 Mon Sep 17 00:00:00 2001 From: jacques42 Date: Mon, 21 Dec 2020 22:19:21 +0100 Subject: [PATCH 41/43] Updated pack-build.js --- scripts/pack-build.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/pack-build.js b/scripts/pack-build.js index 3b77ddb16..6dc66f7dd 100644 --- a/scripts/pack-build.js +++ b/scripts/pack-build.js @@ -98,6 +98,7 @@ function createArchive(fileName, archive) { archive.directory('node_modules/component-emitter'); archive.directory('node_modules/component-inherit'); archive.directory('node_modules/cookie'); + archive.directory('node_modules/core-js'); archive.directory('node_modules/debug'); archive.directory('node_modules/engine.io'); archive.directory('node_modules/engine.io-client'); @@ -114,6 +115,7 @@ function createArchive(fileName, archive) { archive.directory('node_modules/object-component'); archive.directory('node_modules/parseqs'); archive.directory('node_modules/parseuri'); + archive.directory('node_modules/regenerator-runtime'); archive.directory('node_modules/rpio'); archive.directory('node_modules/socket.io'); archive.directory('node_modules/socket.io-adapter'); From 947bfd86ea78ea51dcc311570876e61c4ad42217 Mon Sep 17 00:00:00 2001 From: jacques42 Date: Tue, 22 Dec 2020 17:39:03 +0100 Subject: [PATCH 42/43] Excluded server scripts from babel transpiler modifications, this is not required. Removed polyfills, related modules and updated pack-script --- gulpfile.js | 4 ++-- package.json | 2 -- scripts/pack-build.js | 2 -- src/js/sync-to-drive.js | 2 -- 4 files changed, 2 insertions(+), 8 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 8d27b630f..a2293890e 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -14,7 +14,7 @@ gulp.task('sass', function () { gulp.task('js', function () { return gulp .src('./src/js/**/*.js') - .pipe(babel({ presets: ['@babel/env'] })) + .pipe(babel({ presets: ['@babel/env'], ignore: [ "src/js/sync-to-drive.js", "src/js/remotebuzzer_server.js" ] })) .pipe(gulp.dest('./resources/js')); }); @@ -23,4 +23,4 @@ gulp.task('watch', function () { gulp.watch('./src/js/*.js', gulp.series('js')); }); -gulp.task('default', gulp.parallel('sass', 'js')); \ No newline at end of file +gulp.task('default', gulp.parallel('sass', 'js')); diff --git a/package.json b/package.json index fac668289..07d085ebe 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,6 @@ ], "dependencies": { "@andreasremdt/simple-translator": "^2.0.3", - "core-js": "^3.8.1", "font-awesome": "^4.7.0", "gh-markdown-cli": "^0.2.0", "github-markdown-css": "^4.0.0", @@ -49,7 +48,6 @@ "marvinj": "^1.0.0", "normalize.css": "^8.0.1", "npm-run-all": "^4.1.5", - "regenerator-runtime": "^0.13.7", "rpio": "^2.1.1", "socket.io": "^2.3.0", "socket.io-client": "^2.3.1", diff --git a/scripts/pack-build.js b/scripts/pack-build.js index 6dc66f7dd..3b77ddb16 100644 --- a/scripts/pack-build.js +++ b/scripts/pack-build.js @@ -98,7 +98,6 @@ function createArchive(fileName, archive) { archive.directory('node_modules/component-emitter'); archive.directory('node_modules/component-inherit'); archive.directory('node_modules/cookie'); - archive.directory('node_modules/core-js'); archive.directory('node_modules/debug'); archive.directory('node_modules/engine.io'); archive.directory('node_modules/engine.io-client'); @@ -115,7 +114,6 @@ function createArchive(fileName, archive) { archive.directory('node_modules/object-component'); archive.directory('node_modules/parseqs'); archive.directory('node_modules/parseuri'); - archive.directory('node_modules/regenerator-runtime'); archive.directory('node_modules/rpio'); archive.directory('node_modules/socket.io'); archive.directory('node_modules/socket.io-adapter'); diff --git a/src/js/sync-to-drive.js b/src/js/sync-to-drive.js index c6a808ff5..32b4679c3 100755 --- a/src/js/sync-to-drive.js +++ b/src/js/sync-to-drive.js @@ -1,6 +1,4 @@ /* This script needs to be run from within the photobooth directory */ -require('core-js/stable'); -require('regenerator-runtime/runtime'); /* Imports */ const {execSync, spawn} = require('child_process'); From 7a4fb578adb70300228141e8bac1cef6b5d52110 Mon Sep 17 00:00:00 2001 From: jacques42 Date: Tue, 22 Dec 2020 22:09:52 +0100 Subject: [PATCH 43/43] Fixed prettier / eslint conflict --- src/js/sync-to-drive.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/js/sync-to-drive.js b/src/js/sync-to-drive.js index 32b4679c3..917d02cd2 100755 --- a/src/js/sync-to-drive.js +++ b/src/js/sync-to-drive.js @@ -74,7 +74,9 @@ const getDriveInfo = ({drive}) => { } return json.blockdevices.find( - (blk) => blk.subsystems.includes('usb') && + (blk) => + // eslint-disable-next-line implicit-arrow-linebreak + blk.subsystems.includes('usb') && ((blk.name && drive === blk.name.toLowerCase()) || (blk.kname && drive === blk.kname.toLowerCase()) || (blk.path && drive === blk.path.toLowerCase()) || @@ -117,6 +119,7 @@ const startSync = ({dataAbsPath, drive}) => { case 'win32': return null; case 'linux': + // prettier-ignore return [ 'rsync', '-a',