diff --git a/.github/ISSUE_TEMPLATE/add_localization.md b/.github/ISSUE_TEMPLATE/add_localization.md deleted file mode 100644 index 177cc2dfc..000000000 --- a/.github/ISSUE_TEMPLATE/add_localization.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -name: "\U0001F5FA Add new language" -about: Create a localization request -title: '' -labels: '' -assignees: '' - ---- - - - -**Missing language** - - -**Additional context** - diff --git a/.github/ISSUE_TEMPLATE/add_localization.yml b/.github/ISSUE_TEMPLATE/add_localization.yml new file mode 100644 index 000000000..8b2c27627 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/add_localization.yml @@ -0,0 +1,25 @@ +name: "\U0001F5FA Request a new language" +description: Your language isn't supported? Create a localization request here! +title: "[LOCALIZATION]: " +labels: localization +body: +- type: markdown + attributes: + value: | + 1. Please speak English, this is the language all maintainers can speak and write. + 2. Please ask questions or configuration/deploy problems in our Telegram group (https://t.me/PhotoboothGroup). + 3. Please take a moment to check that your language hasn't already been suggested. + 4. Request as translator on (https://crowdin.com/project/photobooth). +- type: textarea + id: description + attributes: + label: Request a language + placeholder: | + Please tell which language you are missing in Photobooth. + validations: + required: true +- type: textarea + id: additional + attributes: + label: Additional context + description: Add any other context here. diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index b91466299..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: "\U0001F41E Bug report" -about: Create a report to help us improve -title: '' -labels: 'bug' -assignees: '' - ---- - - - -**Describe the bug** - - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** - - -**Screenshots** - - -**Environment (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] - -**Photobooth** -- Did it work before? [yes/no] -- Which version of Photobooth are you using? - -**Additional context** - diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000..1c25e2fc0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,86 @@ +name: "\U0001F41E Bug report" +description: Create a report to help us improve +title: "[BUG]: " +labels: bug +body: +- type: markdown + attributes: + value: | + 1. Please speak English, this is the language all maintainers can speak and write. + 2. Please ask questions or configuration/deploy problems in our Telegram group (https://t.me/PhotoboothGroup). + 3. Please take a moment to check that your bug hasn't already been reported. + 4. Make sure it's not mentioned in the [FAQ](https://github.com/andi34/photobooth/blob/wiki/FAQ.md). + 5. Please give all relevant information below for bug reports, because incomplete details will be handled as an invalid report! + +- type: textarea + id: description + attributes: + label: Describe the bug + description: | + A clear and concise description of what the bug is. + validations: + required: true + +- type: textarea + id: reproduce + attributes: + label: Steps to reproduce the behavior + description: | + 1. Go to ... + 2. Click on .... + 3. Scroll down to .... + 4. See error + validations: + required: true + +- type: textarea + id: expection + attributes: + label: Expected behavior + placeholder: | + A clear and concise description of what you expected to happen + validations: + required: true + +- type: input + id: photobooth-ver + attributes: + label: Photobooth Version + description: Photobooth Version (or commit reference) your instance is running + validations: + required: true + +- type: input + id: browser-ver + attributes: + label: Browser and browser version + description: Browser and version of the browser you are accessing Photobooth from + +- type: input + id: os-ver + attributes: + label: Operating System + description: The operating system you are using to run Photobooth, e.g Raspberry Pi OS with desktop + + +- type: dropdown + id: workbefore + attributes: + label: Did it work before? + options: + - "Yes" + - "No" + validations: + required: true +- type: input + id: workbefore-description + attributes: + label: Latest working version + description: | + If it worked before, please tell the latest working version. + +- type: textarea + id: additional + attributes: + label: Additional context + description: Add any other context or screenshots about the bug here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index a0949f389..000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: "\U0001F680 Feature request" -about: Suggest an idea for this project -title: '' -labels: enhancement -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** - - -**Describe the solution you'd like** - - -**Describe alternatives you've considered** - - -**Additional context** - diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 000000000..62869d44a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,53 @@ +name: "\U0001F680 Feature request" +description: Suggest an idea for this project +title: "[FEATURE]: " +labels: enhancement +body: +- type: markdown + attributes: + value: | + 1. Please speak English, this is the language all maintainers can speak and write. + 2. Please ask questions or configuration/deploy problems in our Telegram group (https://t.me/PhotoboothGroup). + 3. Please take a moment to check that your feature hasn't already been suggested. + +- type: dropdown + id: problem-related + attributes: + label: Is your feature request related to a problem? + description: | + If so, please please describe in the Description field. + options: + - "Yes" + - "No" + validations: + required: true +- type: textarea + id: problem-description + attributes: + label: Description + description: | + A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]. + +- type: textarea + id: description + attributes: + label: Describe the solution you'd like + placeholder: | + A clear and concise description of what you want to happen. + validations: + required: true + +- type: textarea + id: alternatives + attributes: + label: Describe alternatives you've considered + placeholder: | + A clear and concise description of any alternative solutions or features you've considered. + validations: + required: true + +- type: textarea + id: additional + attributes: + label: Additional context + description: Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..c0daaffd5 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,52 @@ +name: Build + +on: + push: + branches: + - dev + pull_request: + branches: + - dev + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repository and submodules + uses: actions/checkout@v2 + with: + submodules: true + - name: Use Node v12 + uses: actions/setup-node@v2 + with: + node-version: '12' + - name: Install modules + run: yarn install + - name: Build + run: yarn build + - name: Get git status + run: git status + - name: Pack zip + run: yarn pack:build + - name: Publish zip + uses: actions/upload-artifact@v2 + if: github.actor != 'dependabot[bot]' && github.actor != 'dependabot-preview[bot]' + with: + name: photobooth + path: archives/photobooth-*.zip + buildmacos: + runs-on: macos-11 + steps: + - name: Checkout repository and submodules + uses: actions/checkout@v2 + with: + submodules: true + - name: Use Node v12 + uses: actions/setup-node@v2 + with: + node-version: '12' + - name: Install modules + run: yarn install + - name: Build + run: yarn build diff --git a/.github/workflows/gulp_sass.yml b/.github/workflows/gulp_sass.yml index 96c9f85fe..8f47ccf7a 100644 --- a/.github/workflows/gulp_sass.yml +++ b/.github/workflows/gulp_sass.yml @@ -4,14 +4,12 @@ on: push: branches: - dev - - stable2 - stable3 paths: - '**.scss' pull_request: branches: - dev - - stable2 - stable3 paths: - '**.scss' @@ -22,6 +20,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: '12' - name: Install modules run: yarn install - name: Running gulp-sass diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 4814c002a..21d33a648 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -4,14 +4,12 @@ on: push: branches: - dev - - stable2 - stable3 paths: - '**.js' pull_request: branches: - dev - - stable2 - stable3 paths: - '**.js' @@ -22,6 +20,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: '12' - name: Install modules run: yarn install - name: Lint diff --git a/.gitignore b/.gitignore index f92976bc6..365a080b7 100644 --- a/.gitignore +++ b/.gitignore @@ -4,10 +4,12 @@ config/* !config/config.inc.php data/ digicamcontrol/ +HEAD manual/faq.* node_modules/ package-lock.json -private/ +private/* +!private/README.md resources/css/* !resources/css/README.md resources/js/* @@ -23,4 +25,5 @@ vendor/simple-translator/ /.htaccess .htpasswd *.patch +.skip_welcome .vscode diff --git a/.gitmodules b/.gitmodules index 0af580df9..1004addb0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "vendor/Seriously"] path = vendor/Seriously url = https://github.com/brianchirls/Seriously.js +[submodule "vendor/phpqrcode"] + path = vendor/phpqrcode + url = https://github.com/andi34/phpqrcode diff --git a/.npmrc b/.npmrc index 5fca0d518..d84128b58 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1,2 @@ scripts-prepend-node-path=true +engine-strict=true diff --git a/LICENSE_NOTICE b/LICENSE_NOTICE new file mode 100644 index 000000000..529689f8a --- /dev/null +++ b/LICENSE_NOTICE @@ -0,0 +1,6 @@ +LICENSE NOTE + +Once build, Photobooth incorporates several parts and optimizations that are covered by a different license which could apply to Photobooth as well. + +All dependencies include their respective LICENSE files. + diff --git a/README.md b/README.md index 8e93ddc76..ae7ef8e95 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Photobooth v3 -A Photobooth web interface for Raspberry Pi and Windows. +A Photobooth web interface for Linux and Windows. -Photobooth was initially developped by Andre Rinas, you can find his source [here](https://github.com/andreknieriem/photobooth). +Photobooth was initially developped by Andre Rinas to use on a Raspberry Pi, you can find his source [here](https://github.com/andreknieriem/photobooth). **This is my personal Photobooth fork with a lot of extras and improvements.** _(The full changelog can be found inside [the Photobooth Wiki](https://github.com/andi34/photobooth/wiki/changelog).)_ @@ -107,14 +107,17 @@ If you're having trouble or questions please take a look at our [FAQ](https://github.com/andi34/photobooth/wiki#faq---frequently-asked-questions) before opening a new issue. -## :globe_with_meridians: Browser support - -[Click here](https://github.com/andi34/photobooth/wiki#browser-support) to find out if your Browser is supported. - ### :mag: Changelog Please take a look at the changelog in our [Photobooth Wiki](https://github.com/andi34/photobooth/wiki/changelog). +### :copyright: License + +Photobooth source is licensed under the MIT license. + +Once build, Photobooth incorporates several parts and optimizations that are covered by a different license which could apply to Photobooth as well. +All dependencies include their respective LICENSE files. + ### :tada: Donation If you like my work and like to keep me motivated you can buy me a coconut water: @@ -123,7 +126,9 @@ If you like my work and like to keep me motivated you can buy me a coconut water ### :mortar_board: Tutorial -[Raspberry Pi Weddingphotobooth (german)](https://www.andrerinas.de/tutorials/raspberry-pi-einen-dslr-weddingphotobooth-erstellen.html) +[Raspberry Pi Weddingphotobooth (german)](https://www.andrerinas.de/tutorials/raspberry-pi-einen-dslr-weddingphotobooth-erstellen.html) +[Raspberry Pi Fotobox für Hochzeiten und Geburtstage (German)](https://www.dennis-henss.de/2020/01/25/raspberry-pi-fotobox-fuer-hochzeiten-und-geburtstage) +[Raspberry Pi Photobooth in a classic vintage plate camera](https://florianmuller.com/raspberry-pi-photobooth-in-a-classic-vintage-plate-camera) ### :clap: Contributors and thanks to @@ -165,4 +170,5 @@ If you like my work and like to keep me motivated you can buy me a coconut water - [Christian Tarne](https://github.com/Metropo) - [DeNeD1](https://github.com/DeNeD1) - [DIY89](https://github.com/DIY89) - +- [mhellmeier](https://github.com/mhellmeier) +- [Uwe Pieper](https://github.com/up-87) diff --git a/admin/debugpanel.php b/admin/debugpanel.php index bb5878a33..cfc71f3c0 100644 --- a/admin/debugpanel.php +++ b/admin/debugpanel.php @@ -39,6 +39,9 @@ + + + @@ -89,9 +92,10 @@ function html_src_indent($num) echo '
  • myconfig
  • '; echo '
  • remotebuzzer
  • '; echo '
  • synctodrive
  • '; - echo '
  • cameralog
  • '; + echo '
  • devlog
  • '; echo '
  • serverprocesses
  • '; echo '
  • bootconfig
  • '; + echo '
  • githead
  • '; html_src_indent(--$indent); echo ''; @@ -107,10 +111,11 @@ function html_src_indent($num) + - + diff --git a/admin/diskusage.php b/admin/diskusage.php index 6f7f5b4e5..bd10f7d59 100644 --- a/admin/diskusage.php +++ b/admin/diskusage.php @@ -46,6 +46,9 @@ + + + @@ -81,11 +84,12 @@ + - + diff --git a/admin/index.php b/admin/index.php index 48968c316..064731a1e 100644 --- a/admin/index.php +++ b/admin/index.php @@ -34,6 +34,9 @@ + + + @@ -286,9 +289,10 @@ function isElementHidden($element_class, $setting) + - + diff --git a/allow-shutdown.sh b/allow-shutdown.sh new file mode 100644 index 000000000..4c13348b4 --- /dev/null +++ b/allow-shutdown.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# Stop on the first sign of trouble +set -e + +function info { + echo -e "\033[0;36m${1}\033[0m" +} + +function error { + echo -e "\033[0;31m${1}\033[0m" +} + +if [ $UID != 0 ]; then + error "ERROR: Only root is allowed to execute the installer. Forgot sudo?" + exit 1 +fi + +cat > /etc/sudoers.d/020_www-data-shutdown << EOF +# Photobooth Remotebuzzer shutdown button for www-data to shutdown the system +www-data ALL=(ALL) NOPASSWD: /sbin/shutdown +EOF + diff --git a/api/admin.php b/api/admin.php index 0acf40917..8dba3891c 100644 --- a/api/admin.php +++ b/api/admin.php @@ -20,15 +20,12 @@ foreach ($files as $file) { // iterate files if (is_file($file)) { - unlink($file); // delete file + // delete file + unlink($file); } } } } - $logFile = $config['foldersAbs']['tmp'] . DIRECTORY_SEPARATOR . $config['take_picture']['logfile']; - if (is_file($logFile)) { - unlink($logFile); - } } if ($config['reset']['remove_mailtxt']) { @@ -44,9 +41,19 @@ } } + $logFiles = glob($config['foldersAbs']['tmp'] . '/*.log'); + foreach ($logFiles as $logFile) { + // iterate files + if (is_file($logFile)) { + // delete file + unlink($logFile); + } + } + // delete db.txt if (is_file(DB_FILE)) { - unlink(DB_FILE); // delete file + // delete file + unlink(DB_FILE); } die(json_encode('success')); @@ -114,7 +121,7 @@ copy('../resources/css/modern_style.css', '../resources/css/custom_style.css'); } if (!file_exists('../resources/css/custom_chromakeying.css')) { - copy('../resources/css/modern_chromakeying.css.css', '../resources/css/custom_chromakeying.css'); + copy('../resources/css/modern_chromakeying.css', '../resources/css/custom_chromakeying.css'); } if (!file_exists('../resources/css/custom_live_chromakeying.css')) { copy('../resources/css/modern_live_chromakeying.css', '../resources/css/custom_live_chromakeying.css'); @@ -127,10 +134,23 @@ $newConfig['synctodrive']['enabled'] = false; } + if (isset($newConfig['database']['file']) && empty($newConfig['database']['file'])) { + $newConfig['database']['file'] = 'db'; + } + + if (isset($newConfig['mail']['file']) && empty($newConfig['mail']['file'])) { + $newConfig['mail']['file'] = 'mail-adresses'; + } + if (isset($newConfig['remotebuzzer']['port']) && empty($newConfig['remotebuzzer']['port'])) { $newConfig['remotebuzzer']['port'] = 14711; } + if (isset($newConfig['get_request']['server']) && empty($newConfig['get_request']['server'])) { + $newConfig['get_request']['countdown'] = false; + $newConfig['get_request']['processed'] = false; + } + if ($newConfig['collage']['layout'] === '1+2') { $newConfig['collage']['limit'] = 3; } else { diff --git a/api/applyEffects.php b/api/applyEffects.php index 8869d1916..d67240b42 100644 --- a/api/applyEffects.php +++ b/api/applyEffects.php @@ -11,22 +11,17 @@ require_once '../lib/log.php'; if (!extension_loaded('gd')) { - $errormsg = 'GD library not loaded! Please enable GD!'; + $errormsg = basename($_SERVER['PHP_SELF']) . ': GD library not loaded! Please enable GD!'; logErrorAndDie($errormsg); } if (empty($_POST['file'])) { - $errormsg = 'No file provided'; + $errormsg = basename($_SERVER['PHP_SELF']) . ': No file provided'; logErrorAndDie($errormsg); } $file = $_POST['file']; -$filename_photo = $config['foldersAbs']['images'] . DIRECTORY_SEPARATOR . $file; -$filename_keying = $config['foldersAbs']['keying'] . DIRECTORY_SEPARATOR . $file; -$filename_tmp = $config['foldersAbs']['tmp'] . DIRECTORY_SEPARATOR . $file; -$filename_thumb = $config['foldersAbs']['thumbs'] . DIRECTORY_SEPARATOR . $file; -$picture_frame = realpath(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . $config['picture']['frame']); $picture_permissions = $config['picture']['permissions']; $thumb_size = substr($config['picture']['thumb_size'], 0, -2); $chroma_size = substr($config['keying']['size'], 0, -2); @@ -48,13 +43,16 @@ $image_filter = false; if (!isset($_POST['style'])) { - $errormsg = 'No style provided'; + $errormsg = basename($_SERVER['PHP_SELF']) . ': No style provided'; logErrorAndDie($errormsg); } if (!isset($_POST['filter'])) { - $errormsg = 'No filter provided'; - logErrorAndDie($errormsg); + $ErrorData = [ + 'warning' => 'No filter provided! Using plain image filter!', + ]; + logError($ErrorData); + $image_filter = 'plain'; } if (!empty($_POST['filter']) && $_POST['filter'] !== 'plain') { @@ -63,196 +61,172 @@ // Check collage configuration if ($_POST['style'] === 'collage') { - if ($config['collage']['take_frame'] !== 'off') { - if (is_dir(COLLAGE_FRAME)) { - $errormsg = 'Frame not set! ' . COLLAGE_FRAME . ' is a path but needs to be a png!'; - logErrorAndDie($errormsg); - } - - if (!file_exists(COLLAGE_FRAME)) { - $errormsg = 'Frame ' . COLLAGE_FRAME . ' does not exist!'; - logErrorAndDie($errormsg); - } - } - if ($config['textoncollage']['enabled']) { - if (is_dir(realpath(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . TEXTONCOLLAGE_FONT))) { - $errormsg = 'Font not set! ' . TEXTONCOLLAGE_FONT . ' is a path but needs to be a ttf!'; - logErrorAndDie($errormsg); - } - - if (!file_exists(realpath(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . TEXTONCOLLAGE_FONT))) { - $errormsg = 'Font ' . TEXTONCOLLAGE_FONT . ' does not exist!'; - logErrorAndDie($errormsg); - } - } -} else { - // Check picture configuration - if (!file_exists($filename_tmp)) { - $errormsg = 'File ' . $filename_tmp . ' does not exist'; - logErrorAndDie($errormsg); - } - - if ($config['picture']['take_frame']) { - if (is_dir($picture_frame)) { - $errormsg = 'Frame not set! ' . $picture_frame . ' is a path but needs to be a png!'; - logErrorAndDie($errormsg); - } - - if (!file_exists($picture_frame)) { - $errormsg = 'Frame ' . $picture_frame . ' does not exist!'; - logErrorAndDie($errormsg); - } + testFile($config['textoncollage']['font']); } +} - if ($config['textonpicture']['enabled']) { - if (is_dir(realpath(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . $fontpath))) { - $errormsg = 'Font not set! ' . $fontpath . ' is a path but needs to be a ttf!'; - logErrorAndDie($errormsg); - } +$srcImages = []; +$srcImages[] = $file; - if (!file_exists(realpath(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . $fontpath))) { - $errormsg = 'Font ' . $fontpath . ' does not exist!'; - logErrorAndDie($errormsg); - } - } -} +$filename_tmp = $config['foldersAbs']['tmp'] . DIRECTORY_SEPARATOR . $file; // Process Collage if ($_POST['style'] === 'collage') { $collageBasename = substr($filename_tmp, 0, -4); + $singleImageBase = substr($file, 0, -4); + $collageSrcImagePaths = []; for ($i = 0; $i < $config['collage']['limit']; $i++) { $collageSrcImagePaths[] = $collageBasename . '-' . $i . '.jpg'; + if ($config['collage']['keep_single_images']) { + $srcImages[] = $singleImageBase . '-' . $i . '.jpg'; + } } if (!createCollage($collageSrcImagePaths, $filename_tmp, $image_filter)) { - $errormsg = 'Could not create collage'; + $errormsg = basename($_SERVER['PHP_SELF']) . ': Could not create collage'; logErrorAndDie($errormsg); } - - if (!$config['picture']['keep_original']) { - foreach ($collageSrcImagePaths as $tmp) { - unlink($tmp); - } - } } -$imageResource = imagecreatefromjpeg($filename_tmp); +foreach ($srcImages as $image) { + $filename_photo = $config['foldersAbs']['images'] . DIRECTORY_SEPARATOR . $image; + $filename_keying = $config['foldersAbs']['keying'] . DIRECTORY_SEPARATOR . $image; + $filename_tmp = $config['foldersAbs']['tmp'] . DIRECTORY_SEPARATOR . $image; + $filename_thumb = $config['foldersAbs']['thumbs'] . DIRECTORY_SEPARATOR . $image; -if ($_POST['style'] !== 'collage') { - // Only jpg/jpeg are supported - if (!$imageResource) { - $errormsg = 'Could not read jpeg file. Are you taking raws?'; + if (!file_exists($filename_tmp)) { + $errormsg = basename($_SERVER['PHP_SELF']) . ': File ' . $filename_tmp . ' does not exist'; logErrorAndDie($errormsg); } - if ($config['picture']['flip'] !== 'off') { - if ($config['picture']['flip'] === 'horizontal') { - imageflip($imageResource, IMG_FLIP_HORIZONTAL); - } elseif ($config['picture']['flip'] === 'vertical') { - imageflip($imageResource, IMG_FLIP_VERTICAL); - } elseif ($config['picture']['flip'] === 'both') { - imageflip($imageResource, IMG_FLIP_BOTH); - } - $imageModified = true; + $imageResource = imagecreatefromjpeg($filename_tmp); + + if ($_POST['style'] === 'collage' && $file != $image) { + $editSingleCollage = true; + $picture_frame = realpath(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . $config['collage']['frame']); + } else { + $editSingleCollage = false; + $picture_frame = realpath(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . $config['picture']['frame']); } - // apply filter - if ($image_filter) { - applyFilter($image_filter, $imageResource); - $imageModified = true; + if ($_POST['style'] !== 'collage' || $editSingleCollage) { + // Only jpg/jpeg are supported + if (!$imageResource) { + $errormsg = basename($_SERVER['PHP_SELF']) . ': Could not read jpeg file. Are you taking raws?'; + logErrorAndDie($errormsg); + } + + if ($config['picture']['flip'] !== 'off') { + if ($config['picture']['flip'] === 'horizontal') { + imageflip($imageResource, IMG_FLIP_HORIZONTAL); + } elseif ($config['picture']['flip'] === 'vertical') { + imageflip($imageResource, IMG_FLIP_VERTICAL); + } elseif ($config['picture']['flip'] === 'both') { + imageflip($imageResource, IMG_FLIP_BOTH); + } + $imageModified = true; + } + + // apply filter + if ($image_filter) { + applyFilter($image_filter, $imageResource); + $imageModified = true; + } + + if ($config['picture']['polaroid_effect'] && $_POST['style'] !== 'collage') { + $polaroid_rotation = $config['picture']['polaroid_rotation']; + $imageResource = effectPolaroid($imageResource, $polaroid_rotation, 200, 200, 200); + $imageModified = true; + } + + if ( + ($config['picture']['take_frame'] && $_POST['style'] !== 'collage' && testFile($config['picture']['frame'])) || + ($editSingleCollage && $config['collage']['take_frame'] === 'always' && testFile($config['collage']['frame'])) + ) { + $imageResource = applyFrame($imageResource, $picture_frame); + $imageModified = true; + } + + if ($config['picture']['rotation'] !== '0') { + $imageResource = rotateResizeImage($imageResource, $config['picture']['rotation']); + $imageModified = true; + } } - if ($config['picture']['rotation'] !== '0') { - $rotatedImg = imagerotate($imageResource, $config['picture']['rotation'], 0); - $imageResource = $rotatedImg; - $imageModified = true; + if ($config['keying']['enabled'] || $_POST['style'] === 'chroma') { + $chromaCopyResource = resizeImage($imageResource, $chroma_size, $chroma_size); + imagejpeg($chromaCopyResource, $filename_keying, $config['jpeg_quality']['chroma']); + imagedestroy($chromaCopyResource); } - if ($config['picture']['polaroid_effect'] && $_POST['style'] !== 'collage') { - $polaroid_rotation = $config['picture']['polaroid_rotation']; - $imageResource = effectPolaroid($imageResource, $polaroid_rotation, 200, 200, 200); + if ($config['textonpicture']['enabled'] && testFile($config['textonpicture']['font']) && $_POST['style'] !== 'collage') { + $imageResource = applyText($imageResource, $fontsize, $fontrot, $fontlocx, $fontlocy, $fontcolor, $fontpath, $line1text, $line2text, $line3text, $linespacing); $imageModified = true; } - if ($config['picture']['take_frame']) { - $frame = imagecreatefrompng($picture_frame); - $frame = resizePngImage($frame, imagesx($imageResource), imagesy($imageResource)); - $x = imagesx($imageResource) / 2 - imagesx($frame) / 2; - $y = imagesy($imageResource) / 2 - imagesy($frame) / 2; - imagecopy($imageResource, $frame, $x, $y, 0, 0, imagesx($frame), imagesy($frame)); - $imageModified = true; + // image scale, create thumbnail + $thumbResource = resizeImage($imageResource, $thumb_size, $thumb_size); + + imagejpeg($thumbResource, $filename_thumb, $config['jpeg_quality']['thumb']); + imagedestroy($thumbResource); + + if ($imageModified || ($config['jpeg_quality']['image'] >= 0 && $config['jpeg_quality']['image'] < 100)) { + imagejpeg($imageResource, $filename_photo, $config['jpeg_quality']['image']); + // preserve jpeg meta data + if ($config['picture']['preserve_exif_data'] && $config['exiftool']['cmd']) { + $cmd = sprintf($config['exiftool']['cmd'], $filename_tmp, $filename_photo); + $cmd .= ' 2>&1'; //Redirect stderr to stdout, otherwise error messages get lost. + + exec($cmd, $output, $returnValue); + + if ($returnValue) { + $ErrorData = [ + 'error' => 'exiftool returned with an error code', + 'cmd' => $cmd, + 'returnValue' => $returnValue, + 'output' => $output, + ]; + $ErrorString = json_encode($ErrorData); + logError($ErrorData); + die($ErrorString); + } + } + } else { + copy($filename_tmp, $filename_photo); } -} -if ($config['keying']['enabled'] || $_POST['style'] === 'chroma') { - $chromaCopyResource = resizeImage($imageResource, $chroma_size, $chroma_size); - imagejpeg($chromaCopyResource, $filename_keying, $config['jpeg_quality']['chroma']); - imagedestroy($chromaCopyResource); -} + if (!$config['picture']['keep_original']) { + unlink($filename_tmp); + } -if ($config['textonpicture']['enabled'] && $_POST['style'] !== 'collage') { - imagejpeg($imageResource, $filename_photo, $config['jpeg_quality']['image']); imagedestroy($imageResource); - ApplyText($filename_photo, $fontsize, $fontrot, $fontlocx, $fontlocy, $fontcolor, $fontpath, $line1text, $line2text, $line3text, $linespacing); - $imageModified = true; - $imageResource = imagecreatefromjpeg($filename_photo); -} -// image scale, create thumbnail -$thumbResource = resizeImage($imageResource, $thumb_size, $thumb_size); - -imagejpeg($thumbResource, $filename_thumb, $config['jpeg_quality']['thumb']); -imagedestroy($thumbResource); - -if ($imageModified || ($config['jpeg_quality']['image'] >= 0 && $config['jpeg_quality']['image'] < 100)) { - imagejpeg($imageResource, $filename_photo, $config['jpeg_quality']['image']); - // preserve jpeg meta data - if ($config['picture']['preserve_exif_data'] && $config['exiftool']['cmd']) { - $cmd = sprintf($config['exiftool']['cmd'], $filename_tmp, $filename_photo); - $cmd .= ' 2>&1'; //Redirect stderr to stdout, otherwise error messages get lost. - - exec($cmd, $output, $returnValue); - - if ($returnValue) { - $ErrorData = [ - 'error' => 'exiftool returned with an error code', - 'cmd' => $cmd, - 'returnValue' => $returnValue, - 'output' => $output, - ]; - $ErrorString = json_encode($ErrorData); - logError($ErrorData); - die($ErrorString); + // insert into database + if ($config['database']['enabled']) { + if ($_POST['style'] !== 'chroma' || ($_POST['style'] === 'chroma' && $config['live_keying']['show_all'] === true)) { + appendImageToDB($image); } } -} else { - copy($filename_tmp, $filename_photo); -} - -if (!$config['picture']['keep_original']) { - unlink($filename_tmp); -} -imagedestroy($imageResource); - -// insert into database -if ($config['database']['enabled']) { - if ($_POST['style'] !== 'chroma' || ($_POST['style'] === 'chroma' && $config['live_keying']['show_all'] === true)) { - appendImageToDB($file); - } + // Change permissions + chmod($filename_photo, octdec($picture_permissions)); } -// Change permissions -chmod($filename_photo, octdec($picture_permissions)); - if ($_POST['style'] === 'chroma' && $config['live_keying']['show_all'] === false) { unlink($filename_photo); unlink($filename_thumb); } -echo json_encode([ +$LogData = [ 'file' => $file, -]); + 'images' => $srcImages, + 'php' => basename($_SERVER['PHP_SELF']), +]; +$LogString = json_encode($LogData); +if ($config['dev']['enabled'] && $config['dev']['advanced_log']) { + logError($LogData); +} +echo $LogString; diff --git a/api/checkConnection.php b/api/checkConnection.php new file mode 100644 index 000000000..9b667d7f5 --- /dev/null +++ b/api/checkConnection.php @@ -0,0 +1,24 @@ + true, + ]); +} else { + echo json_encode([ + 'connected' => false, + ]); +} diff --git a/api/checkOS.php b/api/checkOS.php new file mode 100644 index 000000000..204c8b600 --- /dev/null +++ b/api/checkOS.php @@ -0,0 +1,8 @@ + $operating_system, +]); diff --git a/api/checkVersion.php b/api/checkVersion.php index d58a242e9..a4053236a 100644 --- a/api/checkVersion.php +++ b/api/checkVersion.php @@ -2,6 +2,7 @@ header('Content-Type: application/json'); require_once '../lib/config.php'; +require_once '../lib/log.php'; $url = 'https://api.github.com/repos/' . $config['ui']['github'] . '/photobooth/releases/latest'; $gh = $config['ui']['github']; @@ -21,8 +22,14 @@ $package = json_decode($packageContent, true); $localVersion = $package['version']; -echo json_encode([ +$LogData = [ 'updateAvailable' => $remoteVersion !== $localVersion, 'currentVersion' => $localVersion, 'availableVersion' => $remoteVersion, -]); + 'php' => basename($_SERVER['PHP_SELF']), +]; +$LogString = json_encode($LogData); +if ($config['dev']['enabled']) { + logError($LogData); +} +die($LogString); diff --git a/api/deletePhoto.php b/api/deletePhoto.php index 5c49f181e..28d9aecff 100644 --- a/api/deletePhoto.php +++ b/api/deletePhoto.php @@ -3,13 +3,11 @@ require_once '../lib/db.php'; require_once '../lib/config.php'; +require_once '../lib/log.php'; if (empty($_POST['file'])) { - die( - json_encode([ - 'error' => 'No file provided', - ]) - ); + $errormsg = basename($_SERVER['PHP_SELF']) . ': No file provided'; + logErrorAndDie($errormsg); } $file = $_POST['file']; @@ -19,31 +17,22 @@ $filePathTmp = $config['foldersAbs']['tmp'] . DIRECTORY_SEPARATOR . $file; if (!unlink($filePath) || !unlink($filePathThumb)) { - die( - json_encode([ - 'error' => 'Could not delete file', - ]) - ); + $errormsg = basename($_SERVER['PHP_SELF']) . ': Could not delete file'; + logErrorAndDie($errormsg); } if (is_readable($filePathKeying)) { if (!unlink($filePathKeying)) { - die( - json_encode([ - 'error' => 'Could not delete keying file', - ]) - ); + $errormsg = basename($_SERVER['PHP_SELF']) . ': Could not delete keying file'; + logErrorAndDie($errormsg); } } if (!$config['picture']['keep_original']) { if (is_readable($filePathTmp)) { if (!unlink($filePathTmp)) { - die( - json_encode([ - 'error' => 'Could not delete tmp file', - ]) - ); + $errormsg = basename($_SERVER['PHP_SELF']) . ': Could not delete tmp file'; + logErrorAndDie($errormsg); } } } diff --git a/api/deleteTmpPhoto.php b/api/deleteTmpPhoto.php index 3c2ced31a..7efc39324 100644 --- a/api/deleteTmpPhoto.php +++ b/api/deleteTmpPhoto.php @@ -2,13 +2,11 @@ header('Content-Type: application/json'); require_once '../lib/config.php'; +require_once '../lib/log.php'; if (empty($_POST['file'])) { - die( - json_encode([ - 'error' => 'No file provided', - ]) - ); + $errormsg = basename($_SERVER['PHP_SELF']) . ': No file provided'; + logErrorAndDie($errormsg); } $file = $_POST['file']; @@ -16,11 +14,8 @@ if (is_readable($filePathTmp)) { if (!unlink($filePathTmp)) { - die( - json_encode([ - 'error' => 'Could not delete tmp file', - ]) - ); + $errormsg = basename($_SERVER['PHP_SELF']) . ': Could not delete tmp file'; + logErrorAndDie($errormsg); } } diff --git a/api/print.php b/api/print.php index 023e16c0f..aecb7e37f 100644 --- a/api/print.php +++ b/api/print.php @@ -6,47 +6,33 @@ require_once '../lib/resize.php'; require_once '../lib/applyFrame.php'; require_once '../lib/applyText.php'; +require_once '../lib/log.php'; if (empty($_GET['filename'])) { - die( - json_encode([ - 'error' => 'No file provided', - ]) - ); + $errormsg = basename($_SERVER['PHP_SELF']) . ': No file provided!'; + logErrorAndDie($errormsg); } $filename = $_GET['filename']; $filename_source = $config['foldersAbs']['images'] . DIRECTORY_SEPARATOR . $filename; $filename_print = $config['foldersAbs']['print'] . DIRECTORY_SEPARATOR . $filename; $filename_codes = $config['foldersAbs']['qrcodes'] . DIRECTORY_SEPARATOR . $filename; +$print_frame = realpath(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . $config['print']['frame']); $quality = 100; $status = false; // exit with error if file does not exist if (!file_exists($filename_source)) { - die( - json_encode([ - 'error' => "File $filename not found", - ]) - ); + $errormsg = "File $filename not found"; + logErrorAndDie($errormsg); } // Only jpg/jpeg are supported $imginfo = getimagesize($filename_source); $mimetype = $imginfo['mime']; if ($mimetype != 'image/jpg' && $mimetype != 'image/jpeg') { - die( - json_encode([ - 'error' => 'The source file type ' . $mimetype . ' is not supported', - ]) - ); -} - -// QR -if (!isset($config['webserver']['ip'])) { - $SERVER_IP = $_SERVER['HTTP_HOST']; -} else { - $SERVER_IP = $config['webserver']['ip']; + $errormsg = basename($_SERVER['PHP_SELF']) . ': The source file type ' . $mimetype . ' is not supported'; + logErrorAndDie($errormsg); } // text on print variables @@ -61,45 +47,6 @@ $line2text = $config['textonprint']['line2']; $line3text = $config['textonprint']['line3']; -// print frame -$print_frame = realpath(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . $config['print']['frame']); - -if ($config['print']['print_frame'] && !$config['picture']['take_frame']) { - if (is_dir($print_frame)) { - die( - json_encode([ - 'error' => 'Frame not set! ' . $print_frame . ' is a path but needs to be a png!', - ]) - ); - } - - if (!file_exists($print_frame)) { - die( - json_encode([ - 'error' => 'Frame ' . $print_frame . ' does not exist!', - ]) - ); - } -} - -if ($config['textonprint']['enabled']) { - if (is_dir(realpath(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . $fontpath))) { - die( - json_encode([ - 'error' => 'Font not set! ' . $fontpath . ' is a path but needs to be a ttf!', - ]) - ); - } - - if (!file_exists(realpath(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . $fontpath))) { - die( - json_encode([ - 'error' => 'Font ' . $fontpath . ' does not exist!', - ]) - ); - } -} - if (!file_exists($filename_print)) { // rotate image if needed list($width, $height) = getimagesize($filename_source); @@ -116,23 +63,28 @@ list($width, $height) = getimagesize($filename_print); } - if ($config['print']['qrcode']) { + $source = imagecreatefromjpeg($filename_print); + + if ($config['print']['qrcode'] && file_exists('../vendor/phpqrcode/lib/full/qrlib.php')) { // create qr code if (!file_exists($filename_codes)) { - include '../vendor/phpqrcode/qrlib.php'; - $url = 'http://' . $SERVER_IP . '/api/download.php?image='; - QRcode::png($url . $filename, $filename_codes, QR_ECLEVEL_H, 10); + if ($config['qr']['append_filename']) { + $url = $config['qr']['url'] . $filename; + } else { + $url = $config['qr']['url']; + } + include '../vendor/phpqrcode/lib/full/qrlib.php'; + QRcode::png($url, $filename_codes, QR_ECLEVEL_H, 10); } // merge source and code $newwidth = $width + $height / 2; $newheight = $height; - if ($config['print']['print_frame'] && !$config['picture']['take_frame']) { - ApplyFrame($filename_print, $filename_print, $print_frame); + if ($config['print']['print_frame'] && testFile($config['print']['frame'])) { + $source = applyFrame($source, $print_frame); } - $source = imagecreatefromjpeg($filename_print); $code = imagecreatefrompng($filename_codes); $print = imagecreatetruecolor($newwidth, $newheight); @@ -144,30 +96,39 @@ imagedestroy($source); imagedestroy($print); } else { - if ($config['print']['print_frame'] && !$config['picture']['take_frame']) { - ApplyFrame($filename_print, $filename_print, $print_frame); + if ($config['print']['print_frame'] && testFile($config['print']['frame'])) { + $source = applyFrame($source, $print_frame); } } - if ($config['textonprint']['enabled']) { - $print = imagecreatefromjpeg($filename_print); - ApplyText($filename_print, $fontsize, $fontrot, $fontlocx, $fontlocy, $fontcolor, $fontpath, $line1text, $line2text, $line3text, $linespacing); - imagedestroy($print); + if ($config['textonprint']['enabled'] && testFile($config['textonprint']['font'])) { + $source = applyText($source, $fontsize, $fontrot, $fontlocx, $fontlocy, $fontcolor, $fontpath, $line1text, $line2text, $line3text, $linespacing); } if ($config['print']['crop']) { $crop_width = $config['print']['crop_width']; $crop_height = $config['print']['crop_height']; - ResizeCropImage($crop_width, $crop_height, $filename_print, $filename_print); + $source = resizeCropImage($crop_width, $crop_height, $source); } + + imagejpeg($source, $filename_print, $quality); } // print image -$printimage = shell_exec(sprintf($config['print']['cmd'], $filename_print)); - -die( - json_encode([ - 'status' => 'ok', - 'msg' => $printimage || '', - ]) -); +$cmd = sprintf($config['print']['cmd'], $filename_print); +$cmd .= ' 2>&1'; //Redirect stderr to stdout, otherwise error messages get lost. + +exec($cmd, $output, $returnValue); + +$LogData = [ + 'status' => 'ok', + 'msg' => $cmd, + 'returnValue' => $returnValue, + 'output' => $output, + 'php' => basename($_SERVER['PHP_SELF']), +]; +$LogString = json_encode($LogData); +if ($config['dev']['enabled'] && $config['dev']['advanced_log']) { + logError($LogData); +} +die($LogString); diff --git a/api/qrcode.php b/api/qrcode.php index d27a457ec..e91d2233c 100644 --- a/api/qrcode.php +++ b/api/qrcode.php @@ -1,14 +1,14 @@ false, - 'error' => 'E-Mail address invalid', - ]) - ); + $LogData = [ + 'success' => false, + 'error' => 'E-Mail address invalid', + 'php' => basename($_SERVER['PHP_SELF']), + ]; + $LogString = json_encode($LogData); + if ($config['dev']['enabled']) { + logError($LogData); + } + die($LogString); } $postImage = basename($_POST['image']); if (!isImageInDB($postImage)) { - die( - json_encode([ - 'success' => false, - 'error' => 'Image not found in database', - ]) - ); + $LogData = [ + 'success' => false, + 'error' => 'Image not found in database', + 'php' => basename($_SERVER['PHP_SELF']), + ]; + $LogString = json_encode($LogData); + if ($config['dev']['enabled']) { + logError($LogData); + } + die($LogString); } $mail = new PHPMailer(); @@ -43,12 +52,16 @@ $mail->setFrom($config['mail']['fromAddress'], $config['mail']['fromName']); if (!$mail->addAddress($_POST['sendTo'])) { - die( - json_encode([ - 'success' => false, - 'error' => 'E-Mail address not valid / error', - ]) - ); + $LogData = [ + 'success' => false, + 'error' => 'E-Mail address not valid / error', + 'php' => basename($_SERVER['PHP_SELF']), + ]; + $LogString = json_encode($LogData); + if ($config['dev']['enabled']) { + logError($LogData); + } + die($LogString); } // Email subject @@ -71,12 +84,16 @@ $path = $config['foldersAbs']['images'] . DIRECTORY_SEPARATOR; if (!$mail->addAttachment($path . $postImage)) { - die( - json_encode([ - 'success' => false, - 'error' => 'file error:' . $path . $postImage, - ]) - ); + $LogData = [ + 'success' => false, + 'error' => 'File error:' . $path . $postImage, + 'php' => basename($_SERVER['PHP_SELF']), + ]; + $LogString = json_encode($LogData); + if ($config['dev']['enabled']) { + logError($LogData); + } + die($LogString); } if (isset($_POST['send-link']) && $_POST['send-link'] === 'yes') { @@ -108,9 +125,13 @@ ); } -die( - json_encode([ - 'success' => false, - 'error' => $mail->ErrorInfo, - ]) -); +$LogData = [ + 'success' => false, + 'error' => $mail->ErrorInfo, + 'php' => basename($_SERVER['PHP_SELF']), +]; +$LogString = json_encode($LogData); +if ($config['dev']['enabled']) { + logError($LogData); +} +die($LogString); diff --git a/api/serverInfo.php b/api/serverInfo.php index b8075e04e..721681646 100644 --- a/api/serverInfo.php +++ b/api/serverInfo.php @@ -28,8 +28,24 @@ echo dumpfile('/boot/config.txt', null); break; - case 'nav-cameralog': - echo dumpfile($config['foldersAbs']['tmp'] . '/' . $config['take_picture']['logfile'], null); + case 'nav-devlog': + echo dumpfile($config['foldersAbs']['tmp'] . '/' . $config['dev']['logfile'], null); + break; + + case 'nav-githead': + $get_head = shell_exec('git rev-parse --is-inside-work-tree 2>/dev/null && git log --format="%h %s" -n 20 || false'); + $file_path = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'HEAD'; + $head_file = realpath($file_path); + + if (is_file($head_file)) { + echo 'Latest commits:' . "\r\n"; + echo dumpfile($head_file, null); + } elseif ($get_head) { + echo 'Latest commits:' . "\r\n"; + echo $get_head; + } else { + echo 'Can not get latest commits of this Photobooth installation.'; + } break; default: diff --git a/api/shellCommand.php b/api/shellCommand.php index 7592502c4..9f728afd0 100644 --- a/api/shellCommand.php +++ b/api/shellCommand.php @@ -12,6 +12,12 @@ case 'post-command': $cmd = sprintf($config['post_photo']['cmd']); break; + case 'reboot': + $cmd = 'sudo ' . sprintf($config['reboot']['cmd']); + break; + case 'shutdown': + $cmd = 'sudo ' . sprintf($config['shutdown']['cmd']); + break; default: $cmd = 'echo "Error for mode ' . $mode . ' - command not defined in configuration"'; break; @@ -33,15 +39,27 @@ break; } - echo json_encode([ + $LogData = [ 'success' => $success, 'output' => $output, 'retval' => $retval, 'command' => $cmd, - ]); + 'php' => basename($_SERVER['PHP_SELF']), + ]; + $LogString = json_encode($LogData); + if ($config['dev']['enabled'] && $config['dev']['advanced_log']) { + logError($LogData); + } + echo $LogString; } else { - echo json_encode([ + $LogData = [ 'success' => 'false', 'command' => $cmd, - ]); + 'php' => basename($_SERVER['PHP_SELF']), + ]; + $LogString = json_encode($LogData); + if ($config['dev']['enabled']) { + logError($LogData); + } + echo $LogString; } diff --git a/api/takePic.php b/api/takePic.php index ee20c7d77..39b8007b2 100644 --- a/api/takePic.php +++ b/api/takePic.php @@ -8,7 +8,7 @@ function takePicture($filename) { global $config; - if ($config['dev']['enabled']) { + if ($config['dev']['demo_images']) { $demoFolder = __DIR__ . '/../resources/img/demo/'; $devImg = array_diff(scandir($demoFolder), ['.', '..']); copy($demoFolder . $devImg[array_rand($devImg)], $filename); @@ -40,6 +40,7 @@ function takePicture($filename) { 'cmd' => $cmd, 'returnValue' => $returnValue, 'output' => $output, + 'php' => basename($_SERVER['PHP_SELF']), ]; $ErrorString = json_encode($ErrorData); logError($ErrorData); @@ -50,6 +51,7 @@ function takePicture($filename) { 'cmd' => $cmd, 'returnValue' => $returnValue, 'output' => $output, + 'php' => basename($_SERVER['PHP_SELF']), ]; $ErrorString = json_encode($ErrorData); logError($ErrorData); @@ -84,32 +86,23 @@ function takePicture($filename) { $filename_tmp = $config['foldersAbs']['tmp'] . DIRECTORY_SEPARATOR . $file; if (!isset($_POST['style'])) { - die( - json_encode([ - 'error' => 'No style provided', - ]) - ); + $errormsg = basename($_SERVER['PHP_SELF']) . ': No style provided'; + logErrorAndDie($errormsg); } if ($_POST['style'] === 'photo') { takePicture($filename_tmp); } elseif ($_POST['style'] === 'collage') { if (!is_numeric($_POST['collageNumber'])) { - die( - json_encode([ - 'error' => 'No or invalid collage number provided', - ]) - ); + $errormsg = basename($_SERVER['PHP_SELF']) . ': No or invalid collage number provided'; + logErrorAndDie($errormsg); } $number = $_POST['collageNumber'] + 0; if ($number > $config['collage']['limit']) { - die( - json_encode([ - 'error' => 'Collage consists only of ' . $config['collage']['limit'] . ' pictures', - ]) - ); + $errormsg = basename($_SERVER['PHP_SELF']) . ': Collage consists only of ' . $config['collage']['limit'] . ' pictures'; + logErrorAndDie($errormsg); } $basecollage = substr($file, 0, -4); @@ -120,33 +113,44 @@ function takePicture($filename) { takePicture($filename); - die( - json_encode([ - 'success' => 'collage', - 'file' => $file, - 'collage_file' => $collage_name, - 'current' => $number, - 'limit' => $config['collage']['limit'], - ]) - ); + $LogData = [ + 'success' => 'collage', + 'file' => $file, + 'collage_file' => $collage_name, + 'current' => $number, + 'limit' => $config['collage']['limit'], + 'php' => basename($_SERVER['PHP_SELF']), + ]; + $LogString = json_encode($LogData); + if ($config['dev']['enabled']) { + logError($LogData); + } + die($LogString); } elseif ($_POST['style'] === 'chroma') { takePicture($filename_tmp); - die( - json_encode([ - 'success' => 'chroma', - 'file' => $file, - ]) - ); + $LogData = [ + 'success' => 'chroma', + 'file' => $file, + 'php' => basename($_SERVER['PHP_SELF']), + ]; + $LogString = json_encode($LogData); + if ($config['dev']['enabled'] && $config['dev']['advanced_log']) { + logError($LogData); + } + die($LogString); } else { - die( - json_encode([ - 'error' => 'Invalid photo style provided', - ]) - ); + $errormsg = basename($_SERVER['PHP_SELF']) . ': Invalid photo style provided'; + logErrorAndDie($errormsg); } // send imagename to frontend -echo json_encode([ +$LogData = [ 'success' => 'image', 'file' => $file, -]); + 'php' => basename($_SERVER['PHP_SELF']), +]; +$LogString = json_encode($LogData); +if ($config['dev']['enabled'] && $config['dev']['advanced_log']) { + logError($LogData); +} +die($LogString); diff --git a/api/takeVideo.php b/api/takeVideo.php index ed34f3428..c801a1b83 100644 --- a/api/takeVideo.php +++ b/api/takeVideo.php @@ -2,6 +2,7 @@ header('Content-Type: application/json'); require_once '../lib/config.php'; +require_once '../lib/log.php'; function isRunning($pid) { try { @@ -20,19 +21,28 @@ function isRunning($pid) { $cmd = sprintf($config['preview']['cmd']); $pid = exec($cmd, $out); sleep(3); - die( - json_encode([ - 'isRunning' => isRunning($pid), - 'pid' => $pid - 1, - ]) - ); + $LogData = [ + 'isRunning' => isRunning($pid), + 'pid' => $pid - 1, + 'php' => basename($_SERVER['PHP_SELF']), + ]; + $LogString = json_encode($LogData); + if ($config['dev']['enabled'] && $config['dev']['advanced_log']) { + logError($LogData); + } + die($LogString); } elseif ($_POST['play'] === 'false') { $killcmd = sprintf($config['preview']['killcmd']); exec($killcmd); - die( - json_encode([ - 'isRunning' => isRunning($_POST['pid']), - 'pid' => $_POST['pid'], - ]) - ); + + $LogData = [ + 'isRunning' => isRunning($_POST['pid']), + 'pid' => $_POST['pid'], + 'php' => basename($_SERVER['PHP_SELF']), + ]; + $LogString = json_encode($LogData); + if ($config['dev']['enabled'] && $config['dev']['advanced_log']) { + logError($LogData); + } + die($LogString); } diff --git a/api/update.php b/api/update.php new file mode 100644 index 000000000..c884417f7 --- /dev/null +++ b/api/update.php @@ -0,0 +1,76 @@ + $success, + 'output' => $output, + 'retval' => $retval, + 'command' => $cmd, + 'php' => basename($_SERVER['PHP_SELF']), + ]; + $LogString = json_encode($LogData); + if ($config['dev']['enabled'] && $config['dev']['advanced_log']) { + logError($LogData); + } + echo $LogString; +} else { + $LogData = [ + 'success' => 'false', + 'command' => $cmd, + 'php' => basename($_SERVER['PHP_SELF']), + ]; + $LogString = json_encode($LogData); + if ($config['dev']['enabled'] && $config['dev']['advanced_log']) { + logError($LogData); + } + echo $LogString; +} diff --git a/chromakeying.php b/chromakeying.php index 7c9843ff4..750aeb654 100644 --- a/chromakeying.php +++ b/chromakeying.php @@ -57,6 +57,9 @@ + + +
    @@ -109,6 +112,7 @@ + diff --git a/config/config.inc.php b/config/config.inc.php index 3057236d1..b40af776d 100644 --- a/config/config.inc.php +++ b/config/config.inc.php @@ -10,7 +10,9 @@ // possible language values: de, el, en, es, fr, pl, it $config['ui']['language'] = 'en'; $config['adminpanel']['view'] = 'basic'; -$config['dev']['enabled'] = false; +$config['dev']['enabled'] = true; +$config['dev']['demo_images'] = false; +$config['dev']['advanced_log'] = false; $config['start_screen']['title'] = 'Photobooth'; $config['start_screen']['title_visible'] = true; $config['start_screen']['subtitle'] = NULL; @@ -19,14 +21,15 @@ $config['picture']['thumb_size'] = '540px'; $config['dev']['error_messages'] = true; $config['dev']['reload_on_error'] = true; -$config['qr']['enabled'] = true; -$config['webserver']['ip'] = '127.0.0.1'; +$config['webserver']['ip'] = NULL; $config['webserver']['ssid'] = 'Photobooth'; $config['download']['enabled'] = true; $config['download']['thumbs'] = false; -// control time in milliseconds until Photobooth reloads automatically -$config['picture']['time_to_live'] = '90000'; +// control time in seconds until Photobooth reloads automatically +$config['picture']['time_to_live'] = '90'; $config['picture']['preview_before_processing'] = false; +$config['picture']['retry_on_error'] = '0'; +$config['picture']['retry_timeout'] = '2'; $config['delete']['no_request'] = false; $config['database']['enabled'] = true; $config['database']['file'] = 'db'; @@ -34,6 +37,7 @@ // F R O N T P A G E $config['ui']['show_fork'] = true; +$config['ui']['skip_welcome'] = false; $config['event']['enabled'] = true; $config['event']['textLeft'] = 'We'; // possible event symbol values: 'fa-camera-retro', 'fa-birthday-cake', 'fa-gift', 'fa-tree', 'fa-snowflake-o', 'fa-heart-o', @@ -78,8 +82,7 @@ $config['textonpicture']['locationy'] = '80'; $config['textonpicture']['rotation'] = '0'; $config['textonpicture']['font'] = 'resources/fonts/GreatVibes-Regular.ttf'; -// possible font_color values: 'white', 'grey', 'black' -$config['textonpicture']['font_color'] = 'white'; +$config['textonpicture']['font_color'] = '#ffffff'; $config['textonpicture']['font_size'] = '80'; $config['textonpicture']['linespace'] = '90'; @@ -93,9 +96,12 @@ $config['collage']['continuous_time'] = '5'; // possible layout values: '2x2', '2x2-2', '2x4', '2x4-2', '1+3', '1+3-2', '3+1', '1+2' $config['collage']['layout'] = '2x2-2'; +$config['collage']['dashedline_color'] = '#000000'; +$config['collage']['keep_single_images'] = false; // specify key id (e.g. 13 is the enter key) to use that key to take a collage (collage key) // use for example https://keycode.info to get the key code $config['collage']['key'] = null; +$config['collage']['background_color'] = '#ffffff'; // possible take_frame values: 'off', 'always', 'once' $config['collage']['take_frame'] = 'off'; $config['collage']['frame'] = 'resources/img/frames/frame.png'; @@ -107,8 +113,7 @@ $config['textoncollage']['locationy'] = '250'; $config['textoncollage']['rotation'] = '0'; $config['textoncollage']['font'] = 'resources/fonts/GreatVibes-Regular.ttf'; -// possible font_color values: 'white', 'grey', 'black' -$config['textoncollage']['font_color'] = 'black'; +$config['textoncollage']['font_color'] = '#000000'; $config['textoncollage']['font_size'] = '50'; $config['textoncollage']['linespace'] = '90'; // DO NOT CHANGE limit here @@ -196,12 +201,19 @@ $config['textonprint']['locationy'] = '1050'; $config['textonprint']['rotation'] = '40'; $config['textonprint']['font'] = 'resources/fonts/GreatVibes-Regular.ttf'; -// possible font_color values: 'white', 'grey', 'black' -$config['textonprint']['font_color'] = 'black'; +$config['textonprint']['font_color'] = '#ffffff'; $config['textonprint']['font_size'] = '100'; $config['textonprint']['linespace'] = '100'; +// Q R - C O D E +$config['qr']['enabled'] = true; +$config['qr']['url'] = NULL; +$config['qr']['append_filename'] = true; +$config['qr']['custom_text'] = false; +$config['qr']['text'] = NULL; + + // E - M A I L // Please read https://github.com/andi34/photobooth/wiki/FAQ#ive-trouble-setting-up-e-mail-config-how-do-i-solve-my-problem // @@ -233,6 +245,9 @@ $config['remotebuzzer']['usebuttons'] = false; $config['remotebuzzer']['userotary'] = false; $config['remotebuzzer']['enable_standalonegallery'] = false; +$config['remotebuzzer']['usegpio'] = true; +$config['remotebuzzer']['usehid'] = false; +$config['remotebuzzer']['usesoftbtn'] = false; $config['remotebuzzer']['rotaryclkgpio'] = 27; $config['remotebuzzer']['rotarydtgpio'] = 17; $config['remotebuzzer']['rotarybtngpio'] = 22; @@ -248,6 +263,7 @@ $config['remotebuzzer']['shutdowngpio'] = 16; $config['remotebuzzer']['shutdownholdtime'] = '5'; $config['remotebuzzer']['port'] = 14711; +$config['remotebuzzer']['debounce'] = 30; // S Y N C T O U S B S T I C K @@ -256,14 +272,25 @@ $config['synctodrive']['interval'] = 300; +// G E T R E Q U E S T +$config['get_request']['countdown'] = false; +$config['get_request']['processed'] = false; +$config['get_request']['server'] = NULL; +$config['get_request']['picture'] = 'CNTDWNPHOTO'; +$config['get_request']['collage'] = 'CNTDWNCOLLAGE'; + + // A U T H E N T I C A T I O N $config['login']['enabled'] = false; $config['login']['username'] = 'Photo'; $config['login']['password'] = NULL; $config['protect']['admin'] = true; $config['protect']['localhost_admin'] = true; +$config['protect']['update'] = true; +$config['protect']['localhost_update'] = true; $config['protect']['index'] = false; $config['protect']['localhost_index'] = false; +$config['protect']['index_redirect'] = 'login'; $config['protect']['manual'] = false; $config['protect']['localhost_manual'] = false; @@ -273,6 +300,7 @@ $config['ui']['style'] = 'modern'; $config['button']['show_fs'] = false; $config['button']['homescreen'] = true; +$config['ui']['result_buttons'] = true; $config['ui']['font_size'] = '16px'; $config['colors']['countdown'] = '#ffffff'; $config['colors']['background_countdown'] = '#214852'; @@ -312,7 +340,8 @@ $config['nodebin']['cmd'] = null; $config['pre_photo']['cmd'] = null; $config['post_photo']['cmd'] = null; - +$config['reboot']['cmd'] = null; +$config['shutdown']['cmd'] = null; // F O L D E R S $config['folders']['config'] = 'config'; diff --git a/dependencies.php b/dependencies.php new file mode 100644 index 000000000..6417aab53 --- /dev/null +++ b/dependencies.php @@ -0,0 +1,77 @@ + + + + + + + + + + + <?=$config['ui']['branding']?> + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    dependencies check

    +

    +
    + +
    + + + +
    + + + + + + + + + + + + diff --git a/enable-usb-sync.sh b/enable-usb-sync.sh new file mode 100644 index 000000000..dd00b4bfa --- /dev/null +++ b/enable-usb-sync.sh @@ -0,0 +1,76 @@ +#!/bin/bash + +# Stop on the first sign of trouble +set -e + +SILENT_INSTALL=false + +if [ "silent" = "$1" ]; then + SILENT_INSTALL=true + info "Performing silent install" +fi + +function info { + echo -e "\033[0;36m${1}\033[0m" +} + +function error { + echo -e "\033[0;31m${1}\033[0m" +} + +#Param 1: Question / Param 2: Default / silent answer +function ask_yes_no { + if [ "$SILENT_INSTALL" = false ]; then + read -p "${1}: " -n 1 -r + else + REPLY=${2} + fi +} + +if [ $UID != 0 ]; then + error "ERROR: Only root is allowed to execute the installer. Forgot sudo?" + exit 1 +fi + +if [ ! -f /proc/device-tree/model ]; then + error "ERROR: This installer is only intended to run on a Raspberry Pi." + exit 2 +fi + +PI_MODEL=$(tr -d '\0' > /home/pi/.config/pcmanfm/LXDE-pi/pcmanfm.conf <> /etc/polkit-1/localauthority/50-local.d/udisks2.pkla < @@ -62,6 +66,7 @@ Open `http://localhost/admin` in your Webbrowser and change the configuration fo ### How to change the look of my Photobooth? Photobooth can be easylie styled for your personal needs via admin panel, open [localhost/admin](http://localhost/admin) in your browser and take a look at the `User Interface` options. To use a private custom index you need to create the following files: + - `resources/css/custom_style.css` - Optional: `src/sass/custom_style.scss` (`yarn build` will create the `resources/css/custom_style.css` out of it) - `resources/css/custom_chromakeying.css` @@ -112,10 +117,29 @@ Follow the steps mentioned here: [How to Fix NGINX 413 Request Entity Too Large
    -### Can I use Hardware Button on my Raspberry Pi, to take a Picture? -Yes, the **Hardware Button** feature enables to control Photobooth through hardware buttons connected to Raspberry GPIO pins . This works for directly connected screens and as well for WLAN connected screen (i.e. iPad). Configuration takes place in the admin settings - Hardware Button section. +### Can I use Hardware Button to take a Picture? +Yes, there's different ways! + +#### Key code using connected HID devices +An HID device connected to your hardware can trigger different actions on your device. The HID device must be connected to the device you're accessing Photobooth from! +For example use https://keycode.info to find out the key id of the button you like to use. + +- Related configuration: + **PICTURE section**: + - Key code which triggers a picture: **define** + + **COLLAGE section**: + - Key code which triggers a collage: **define** + + **PRINT section**: + - Key code which triggers printing: **define** + +#### Remotebuzzer Hardware Button feature using GPIO connected hardware (Raspberry Pi only) +The **Hardware Button** feature enables to control Photobooth through hardware buttons connected to Raspberry GPIO pins. This works for directly connected screens and as well for WLAN connected screen (i.e. iPad). Configuration takes place in the admin settings - Hardware Button section. +Using the Remotebuzzer feature makes the button action taking effect at the same time on all devices accessing Photobooth! The Hardware Button functionality supports two separate modes of operation (select via admin panel): + - **Buttons**: Distinct hardware buttons can be connected to distinct GPIOs. Each button will trigger a separate functionality (i.e. take photo). - **Rotary Encoder**: A rotary encoder connected to GPIOs will drive the input on the screen. This enables to use the rotary to scroll through the Photobooth UI buttons, and click to select actions. @@ -126,19 +150,20 @@ Photobooth will watch GPIOs for a PIN_DOWN event - so the hardware button needs Troubleshooting / Debugging: - **Important: For WLAN connected screens you must make sure to set the IP address of the Photobooth web server in the admin settings - section "General"**. The loopback IP (127.0.0.1) does not work, it has to be the exact IP address of the Photobooth web server, to which the remote display connects to. -- Switch Photobooth to DEV mode. (admin screen -> expert view -> general section) -- Reload the Photobooth homepage -- Check the browser developer console for error logs -- Check the server logs for errors (file `data/tmp/remotebuzzer_server.log`). -- If there is no errors logged but hardware buttons still do not trigger - - GPIO interrupts might be disabled. Check file `/boot/config.txt` and remove / disable the following overlay `dtoverlay=gpio-no-irq` to enable interrupts for GPIOs. - - GPIOs may not be configured as PULLUP. The configuration for this is done in fie `/boot/config.txt` by adding the GPIO numbers in use as follows - you **must reboot** the Raspberry Pi in order to activate changes in this setting. +- Having trouble? + - Switch Photobooth to DEV mode. (admin screen -> expert view -> general section) + - Reload the Photobooth homepage + - Check the browser developer console for error logs + - Check the server logs for errors at the Debug panel: [http://localhost/admin/debugpanel.php](http://localhost/admin/debugpanel.php) + - If there is no errors logged but hardware buttons still do not trigger: + - GPIO interrupts might be disabled. Check file `/boot/config.txt` and remove / disable the following overlay `dtoverlay=gpio-no-irq` to enable interrupts for GPIOs. + - GPIOs may not be configured as PULLUP. The configuration for this is done in fie `/boot/config.txt` by adding the GPIO numbers in use as follows - you **must reboot** the Raspberry Pi in order to activate changes in this setting. ``` gpio=16,17,20,21,22,26,27=pu ``` -- For the Shutdown button to work, `www-data` needs to have the necessary sudo permissions. This is done by the `install-raspian.sh` script or can be manually added as + - For the Shutdown button to work, `www-data` needs to have the necessary sudo permissions. This is done by the `install-raspian.sh` script or can be manually added as ``` cat >> /etc/sudoers.d/020_www-data-shutdown << EOF @@ -161,6 +186,7 @@ The server supports up to four connected hardware buttons for the following func - Long button press (default > 2 sec) will trigger a collage in Photobooth Note: + - If collage is configured with interruption, next button presses will trigger the next collage pictures. - If collage is disabled in the admin settings, long button press also triggers a single picture - If the collage button is activated (see next), the picture button will never trigger a collage, regardless @@ -171,6 +197,7 @@ Note: - Button press will trigger a collage in Photobooth. Note: + - If collage is configured with interruption, next button presses will trigger the next collage pictures. - If collage is disabled in the admin settings (Collage section), this button will do nothing. @@ -180,6 +207,7 @@ Note: - This button will initate a safe system shutdown and halt (`shutdown -h now`). Note: + - Hold the button for a defined time to initiate the shut down (defaults to 5 seconds). This can be adjusted in the admin settings. - The shutdown button will only trigger if there is currently no action in progress in Photobooth (picture, collage). @@ -223,9 +251,11 @@ GND --- GND ``` Known limitations: + - Delete Picture: in order to be able to access the Delete button through rotary control, please activate admin setting General -> "Delete images without confirm request" The following elements are currently not supported and not accessible through rotary control navigation + - Full Screen Mode button: Looks like modern browser only allow to change to full screen mode upon user gesture. It seems not possible to change to full-screen using Javascript. - Photoswipe download button: Not needed for Rotary Control. (well, if you can come up with a decent use-case, let us know). @@ -252,20 +282,21 @@ and add the following lines: @xset s off @xset -dpms @xset s noblank -@chromium-browser --incognito --kiosk http://localhost/ +@chromium-browser --noerrdialogs --disable-infobars --disable-features=Translate --no-first-run --check-for-update-interval=31536000 --kiosk http://127.0.0.1 --touch-events=enabled ``` **NOTE:** If you're using QR-Code replace `http://localhost/` with your local IP-Adress (e.g. `http://192.168.4.1`), else QR-Code does not work.
    #### Enable touch events -If touch is not working on your Raspberry Pi edit the LXDE Autostart Script again +If touch is not working on your Raspberry Pi make sure `--touch-events=enabled` was added to your Autostart Script. +Edit the LXDE Autostart Script again ``` sudo nano /etc/xdg/lxsession/LXDE-pi/autostart ``` and add `--touch-events=enabled` for Chromium: ``` -@chromium-browser --incognito --kiosk http://localhost/ --touch-events=enabled +@chromium-browser --kiosk http://localhost/ --touch-events=enabled ```
    @@ -300,45 +331,55 @@ You can follow the instructions [here](https://www.geeks3d.com/hacklab/20160108/ ### How to use a live stream as background at countdown? There's different ways depending on your needs and personal setup: -1. If you access Photobooth on your Raspberry Pi you could use a Raspberry Pi Camera. Raspberry Pi Camera will be detected as "device cam". - - Admin panel config "Preview mode": `from device cam` - - **Note:** - - Preview `"from device cam"` will always use the camera of the device where Photobooth get opened in a Browser (e.g. on a tablet it will always show the tablet camera while on a smartphone it will always show the smartphone camera instead)! - - Secure origin or exception required! - - [Prefer Secure Origins For Powerful New Features](https://medium.com/@Carmichaelize/enabling-the-microphone-camera-in-chrome-for-local-unsecure-origins-9c90c3149339) - - [Enabling the Microphone/Camera in Chrome for (Local) Unsecure Origins](https://www.chromium.org/Home/chromium-security/prefer-secure-origins-for-powerful-new-features) - - Admin panel config *"Device cam takes picture"* can be used to take a picture from this preview instead using gphoto / digicamcontrol / raspistill. - -2. If you like to have the same preview independent of the device you access Photobooth from: - Make sure to have a stream available you can use (e.g. from your Webcam, Smartphone Camera or Raspberry Pi Camera) - - Admin panel config *"Preview mode"*: `from URL` - - Admin panel config *"Preview-URL"* example (add needed IP address instead): `url(http://127.0.0.1:8081)` - - **Note** - - Do NOT enable *"Device cam takes picture"* in admin panel config! - - Capture pictures via `raspistill` won't work if motion is installed! - - Requires Photobooth v2.2.1 or later! - -3. A preview can also be done using the video mode of your DSLR (Linux only), but only works if you access Photobooth via [http://localhost](http://localhost) or [http://127.0.0.1](http://localhost): - - install all dependencies `sudo apt install ffmpeg v4l2loopback-dkms -y` - - create a virtual webcam `modprobe v4l2loopback exclusive_caps=1 card_label="GPhoto2 Webcam"` - - `/dev/video0` is used by default, you can use `v4l2-ctl --list-devices` to check which `/dev/*` is the correct one: - If it doesn't match the default setup you need to adjust the `Command to generate a live preview` inside the admin panel! - - Give permissions to /dev/video* `sudo gpasswd -a www-data video` (this was done automatically if you used the installation script) and reboot once - - Admin panel config *"Preview mode"*: `from gphoto2` - - **Note** - - Requires Photobooth v2.11.0 or later! - - You need to access Photobooth directly via [http://localhost](http://localhost) or [http://127.0.0.1](http://localhost) - - There's a delay of about 3 seconds until the preview starts, to avoid that disable the `Battery saving mode on gphoto2 live preview` option to generate a preview in background. **This results in a high battery usage and also a general slowdown.** - - Sometimes Chromium doesn't detect the V4l2 camera launch from php: you need to run `sudo gphoto2 --stdout --capture-movie | ffmpeg -i - -vcodec rawvideo -pix_fmt yuv420p -threads 0 -f v4l2 /dev/video0` from terminal first and load Chromium a first time with a webpage asking for the camera. - - Chromium sometimes has trouble, if there is another webcam like `bcm2835-isp`, it will take it by default instead. Disable other webcams, e.g. `rmmod bcm2835-isp`. - - To ensure that the configuration works after reboot add the following lines to `/etc/rc.local` (You have to add these lines bevor `exit 0`): - - `modprobe v4l2loopback exclusive_caps=1 card_label="GPhoto2 Webcam"` - - `rmmod bcm2835-isp` - - Make sure the countdown is long enough to start the preview and free gphoto2 at the end of the countdown to be able to take a picture (2 seconds before the countdown ends). - - For best user experience the countdown should be set at least to 8 seconds. +#### Preview _"from device cam"_ +If you access Photobooth on your Raspberry Pi you could use a Raspberry Pi Camera. Raspberry Pi Camera will be detected as "device cam". + +- Admin panel config "Preview mode": `from device cam` + +**Note:** + +- Preview `"from device cam"` will always use the camera of the device where Photobooth get opened in a Browser (e.g. on a tablet it will always show the tablet camera while on a smartphone it will always show the smartphone camera instead)! +- Secure origin or exception required! + - [Prefer Secure Origins For Powerful New Features](https://medium.com/@Carmichaelize/enabling-the-microphone-camera-in-chrome-for-local-unsecure-origins-9c90c3149339) + - [Enabling the Microphone/Camera in Chrome for (Local) Unsecure Origins](https://www.chromium.org/Home/chromium-security/prefer-secure-origins-for-powerful-new-features) +- Admin panel config *"Device cam takes picture"* can be used to take a picture from this preview instead using gphoto / digicamcontrol / raspistill / libcamera-still. + +#### Preview _"from URL"_ +If you like to have the same preview independent of the device you access Photobooth from: +Make sure to have a stream available you can use (e.g. from your Webcam, Smartphone Camera or Raspberry Pi Camera) + +- Admin panel config *"Preview mode"*: `from URL` +- Admin panel config *"Preview-URL"* example (add needed IP address instead): `url(http://127.0.0.1:8081)` + +**Note** + +- Do NOT enable *"Device cam takes picture"* in admin panel config! +- Capture pictures via `raspistill` or `libcamera-still` won't work if motion is installed! +- Requires Photobooth v2.2.1 or later! + +#### Preview _"from gohoto2"_ +A preview can also be done using the video mode of your DSLR (Linux only), but only works if you access Photobooth via [http://localhost](http://localhost) or [http://127.0.0.1](http://localhost): + +- Liveview **must** be supported for your camera model, [check here](http://gphoto.org/proj/libgphoto2/support.php) +- install all dependencies `sudo apt install ffmpeg v4l2loopback-dkms -y` +- create a virtual webcam `sudo modprobe v4l2loopback exclusive_caps=1 card_label="GPhoto2 Webcam"` + - `/dev/video0` is used by default, you can use `v4l2-ctl --list-devices` to check which `/dev/*` is the correct one: + If it doesn't match the default setup you need to adjust the `Command to generate a live preview` inside the admin panel! +- Give permissions to /dev/video* `sudo gpasswd -a www-data video` (this was done automatically if you used the installation script) and reboot once +- Admin panel config *"Preview mode"*: `from gphoto2` + +**Note** + +- Requires Photobooth v2.11.0 or later! +- You need to access Photobooth directly via [http://localhost](http://localhost) or [http://127.0.0.1](http://localhost), you won't be able to see the preview on a different device (e.g. Tablet) +- There's a delay of about 3 seconds until the preview starts, to avoid that disable the `Battery saving mode on gphoto2 live preview` option to generate a preview in background. **This results in a high battery usage and also a general slowdown.** +- Sometimes Chromium doesn't detect the V4l2 camera launch from php: you need to run `sudo gphoto2 --stdout --capture-movie | ffmpeg -i - -vcodec rawvideo -pix_fmt yuv420p -threads 0 -f v4l2 /dev/video0` from terminal first and load Chromium a first time with a webpage asking for the camera. +- Chromium sometimes has trouble, if there is another webcam like `bcm2835-isp`, it will take it by default instead. Disable other webcams, e.g. ` sudo rmmod bcm2835-isp`. +- To ensure that the configuration works after reboot add the following lines to `/etc/rc.local` (You have to add these lines bevor `exit 0`): + - `modprobe v4l2loopback exclusive_caps=1 card_label="GPhoto2 Webcam"` + - `rmmod bcm2835-isp` +- Make sure the countdown is long enough to start the preview and free gphoto2 at the end of the countdown to be able to take a picture (2 seconds before the countdown ends). + - For best user experience the countdown should be set at least to 8 seconds.
    @@ -346,7 +387,7 @@ There's different ways depending on your needs and personal setup: Yes you can. There's different ways depending on your needs and personal setup: 1. On Photobooth v2.4.0 and newer you can use the option "Use stream from device cam as background" inside admin panel. - - If enabled, a stream from your device cam is used as background on start screen. It's still possible to use preview from your device cam as background on countdown and also still possible to take pictures via device cam or using `raspistill` for Pi Camera. + - If enabled, a stream from your device cam is used as background on start screen. It's still possible to use preview from your device cam as background on countdown and also still possible to take pictures via device cam or using `raspistill` / `libcamera-still` for Pi Camera. 2. You need to change the background URL path via config or admin panel. Replace `url(../img/bg.jpg)` with your IP-Adress and port (if needed) as URL. Example: @@ -446,6 +487,7 @@ First head over to the hotspot directory to run the installer: cd /var/www/html/vendor/rpihotspot ``` There are a couple of flags you need to change from the example command below: + - change `password` to your desired password, make it easy enough for guests to remember. - change `country code` from `CA` to your own localization. - keep or change the ip address `10.10.10.10`. Remember what you change it to. @@ -466,10 +508,99 @@ sudo ./setup-network.sh --clean This feature will automatically and in regular intervals copy (sync) new pictures to a plugged-in USB stick. Currently works on Raspberry PI OS only. -Use the `install-raspbian.sh` script to get the operating system setup in place. The target USB device is selected through the admin panel +Use the `install-raspbian.sh` script to get the operating system setup in place. +**Note:** If you have declined the question to enable the USB sync file backup while running the `install-raspbian.sh` you need to run the following commands to get the operating system setup done: +``` +wget https://raw.githubusercontent.com/andi34/photobooth/dev/enable-usb-sync.sh +sudo bash enable-usb-sync.sh + +``` + +The target USB device is selected through the admin panel. A USB drive / stick can be identified either by the USB stick label (e.g. `photobooth`), the operating system specific USB device name (e.g. `/dev/sda1`) or the USB device system subsystem name (e.g. `sda`). The preferred method would be the USB stick label (for use of a single USB stick) or the very specific USB device name, for different USB stick use. The default config will look for a drive with the label photobooth. The script only supports one single USB stick connected at a time 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`). +Debugging: Check the server logs for errors at the Debug panel: [http://localhost/admin/debugpanel.php](http://localhost/admin/debugpanel.php) + +
    + +### Raspberry Touchpanel DSI simultaneously with HDMI + +When using a touchscreen on DSI and an HDMI screen simultaneously, the touch input is offset. This is because both monitors are recognized as one screen. + +The remedy is the following: +``` +xinput list +``` +remember the device id=[X] of the touchscreen. +``` +xinput list-props "Device Name" +``` +Get the ID in brackets (Y) of Coordinate Transformation Matrix + +``` +xinput set-prop [X] --type=float [Y] c0 0 c1 0 c2 c3 0 0 1 +``` + +adjust the coding c0 0 c1 0 c2 c3 0 0 1 with your own data. +You can get the values of your screens with the following command: + +``` +xrandr | grep \* # xrandr uses "*" +``` +to identify the screen being used +``` +c0 = touch_area_width / total_width +(width of touch screen divided by width of both screens) +c2 = touch_area_height / total_height +(height touch screen divided by height of both screens) +c1 = touch_area_x_offset / total_width +c3 = touch_area_y_offset / total_height +``` +and execute the above command again with your own coding! + +Example: + +``` +xinput set-prop 6 --type=float 136 0.3478260869565217 0 0 0.55555555555556 0 0 0 1 +``` + +Now unfortunately the settings are only valid for the current session. So create the following desktop startup file with your own values: + +``` +nano ~/.config/autostart/touch.desktop +``` + +``` +[Desktop Entry] +Name=TouchSettingsAutostart +Comment=Set up touch screen setting when starting desktop +Type=Application +## Adapt command to own values +Exec=xinput set-prop 6 --type=float 136 0.3478260869565217 0 0 0 0.55555555555556 0 0 0 1 +Terminal=false +``` + +If you want to use the touchscreen as photobooth and the second monitor for the standalone slideshow for example, open the autostart file: +``` +sudo nano /etc/xdg/lxsession/LXDE-pi/autostart +``` +and enter/adjust the @chromium-browser entries as followed (adjust the value _1920_ to your own resolution and URL if necessary): +``` +@chromium-browser --new-window --start-fullscreen --kiosk http://localhost --window-position=1920,0 --user-data-dir=Default +@chromium-browser --new-window --start-fullscreen --kiosk http://localhost/slideshow/ --window-position=0,0 --user-data-dir='Profile 1' +``` + +
    + +### How to administer CUPS remotely using the web interface? + +By default the CUPS webinterface can only be accessed via [http://localhost:631](http://localhost:631) from your local machine. +To remote access CUPS from other clients you need to run the following commands: +``` +sudo cupsctl --remote-any +sudo /etc/init.d/cups restart +``` + diff --git a/gallery.php b/gallery.php index 73f9cc13c..fe8e2fa23 100644 --- a/gallery.php +++ b/gallery.php @@ -53,6 +53,9 @@ + + + @@ -96,6 +99,7 @@ + diff --git a/gulpfile.js b/gulpfile.js index a2293890e..45a44ba27 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,7 +1,7 @@ 'use strict'; var gulp = require('gulp'); -var sass = require('gulp-sass'); +var sass = require('gulp-sass')(require('sass')); var babel = require('gulp-babel'); gulp.task('sass', function () { diff --git a/index.php b/index.php index 66cc78c41..6e5aa0287 100644 --- a/index.php +++ b/index.php @@ -2,8 +2,16 @@ session_start(); require_once 'lib/config.php'; +if (!$config['ui']['skip_welcome']) { + if (!is_file('.skip_welcome')) { + header('location: welcome.php'); + exit(); + } +} + if ($config['live_keying']['enabled']) { header('location: livechroma.php'); + exit(); } // Login / Authentication check @@ -33,7 +41,7 @@ $galleryIcon = 'fa-th'; } } else { - header('location: login'); + header('location: ' . $config['protect']['index_redirect']); exit(); } ?> @@ -70,6 +78,9 @@ + + + @@ -204,6 +215,7 @@ + diff --git a/install-raspbian.sh b/install-raspbian.sh index d99837c17..1160c6330 100755 --- a/install-raspbian.sh +++ b/install-raspbian.sh @@ -7,11 +7,34 @@ set -e # set -x RUNNING_ON_PI=true +SILENT_INSTALL=false +DATE=$(date +"%Y%m%d-%H-%M") +IPADDRESS=$(hostname -I | cut -d " " -f 1) + +BRANCH="dev" +GIT_INSTALL=true +SUBFOLDER=true +PI_CAMERA=false +KIOSK_MODE=false +USB_SYNC=false +SETUP_CUPS=false +CUPS_REMOTE_ANY=false + +# Node.js v12.22.(4 or newer) is needed on installation via git +NEEDS_NODEJS_CHECK=true +NEEDED_NODE_VERSION="v12.22.(4 or newer)" +NODEJS_NEEDS_UPDATE=false +NODEJS_CHECKED=false if [ ! -z $1 ]; then - webserver=$1 + WEBSERVER=$1 else - webserver=apache + WEBSERVER=apache +fi + +if [ "silent" = "$2" ]; then + SILENT_INSTALL=true + info "Performing silent install" fi function info { @@ -22,10 +45,19 @@ function error { echo -e "\033[0;31m${1}\033[0m" } +#Param 1: Question / Param 2: Default / silent answer +function ask_yes_no { + if [ "$SILENT_INSTALL" = false ]; then + read -p "${1}: " -n 1 -r + else + REPLY=${2} + fi +} + function no_raspberry { - info "WARNING: This reset script is intended to run on a Raspberry Pi." + info "WARNING: This script is intended to run on a Raspberry Pi." info "Running the script on other devices running Debian / a Debian based distribution is possible, but PI specific features will be missing!" - read -p "Do you want to continue? (y/n)" -n 1 -r + ask_yes_no "Do you want to continue? (y/n)" "Y" if [[ $REPLY =~ ^[Yy]$ ]] then RUNNING_ON_PI=false @@ -49,27 +81,162 @@ else fi fi -if [[ ! -z $1 && ("$1" = "nginx" || "$1" = "lighttpd") ]]; then - info "### Used webserver: $webserver" -else - info "### Used webserver: Apache Webserver" -fi - COMMON_PACKAGES=( 'curl' - 'git' 'gphoto2' - 'jq' 'libimage-exiftool-perl' 'nodejs' - 'npm' 'php-gd' 'php-zip' - 'yarn' 'rsync' 'udisks2' ) +print_logo() { +echo " + + + @@@@@@@@@@@@@@@@@@@ + @@. .@@ + %@@@@@@. @@ @@@@@@@@@ @@ + @@@ @@* @@. .@@ + &@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@& +@@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@@@ +@@ @@ +@@ @@@@@@@@@@@@@. *@@ @@@@@ @@ +@@ @@@@ @@@@ @@ +@@@@@@@@@@@@@@@@@@@@@ #@@@@@@@# @@@@@@@@@@@@@@@@@@@@@ +@@ @@@ @@@@( (@@@@ @@@ @@ +@@ &@@ .@@% %@@. @@& @@ +@@ @@ @@ @@ @@ @@ +@@ %@@ @@* /@@ @@% @@ +@@ @@% @@ @@ %@@ @@ +@@ *@@ @@& &@@ @@* @@ +@@ @@ @@* *@@ @@ @@ +@@ @@ @@@ @@@ @@ @@ +@@%%%%%%%%%%%%%%%@@% @@@@@&%&@@@@@ %@@%%%%%%%%%%%%%%%@@ +@@@@@@@@@@@@@@@@@@@@@@ *&@&* @@@@@@@@@@@@@@@@@@@@@@ +@@ ,@@@@& &@@@@, @@ +@@ (@@@@@@@@@( @@ +@@ @@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +" +} + +check_nodejs() { + NODE_VERSION=$(node -v || echo "0") + IFS=. VER=(${NODE_VERSION##*v}) + major=${VER[0]} + minor=${VER[1]} + micro=${VER[2]} + + if [[ -n "$major" && "$major" -eq "12" ]]; then + if [[ -n "$minor" && "$minor" -eq "22" ]]; then + if [[ -n "$micro" && "$micro" -ge "4" ]]; then + info "[Info] Node.js matches our requirements!" + elif [[ -n "$micro" ]]; then + error "[WARN] Node.js needs to be updated, micro version not matching our requirements!" + error "[WARN] Node.js $NODE_VERSION, but $NEEDED_NODE_VERSION is needed!" + NODEJS_NEEDS_UPDATE=true + if [ "$NODEJS_CHECKED" = true ]; then + error "[ERROR] Update was not possible. Aborting Photobooth installation!" + exit 1 + else + update_nodejs + fi + else + error "[ERROR] Unable to handle Node.js version string (micro)" + exit 1 + fi + elif [[ -n "$minor" ]]; then + error "[WARN] Node.js needs to be updated, minor version not matching our requirements!" + error "[WARN] Found Node.js $NODE_VERSION, but $NEEDED_NODE_VERSION is needed!" + NODEJS_NEEDS_UPDATE=true + if [ "$NODEJS_CHECKED" = true ]; then + error "[ERROR] Update was not possible. Aborting Photobooth installation!" + exit 1 + else + update_nodejs + fi + else + error "[ERROR] Unable to handle Node.js version string (minor)" + exit 1 + fi + elif [[ -n "$major" ]]; then + error "[WARN] Node.js needs to be updated, major version not matching our requirements!" + error "[WARN] Found Node.js $NODE_VERSION, but $NEEDED_NODE_VERSION is needed!" + if [ "$NODEJS_CHECKED" = true ]; then + error "[ERROR] Update was not possible. Aborting Photobooth installation!" + exit 1 + else + update_nodejs + fi + else + error "[ERROR] Unable to handle Node.js version string (major)" + exit 1 + fi +} + +update_nodejs() { + if [ $(dpkg-query -W -f='${Status}' "nodejs" 2>/dev/null | grep -c "ok installed") -eq 1 ]; then + info "[Cleanup] Removing nodejs package" + apt purge -y ${package} + fi + + if [ $(dpkg-query -W -f='${Status}' "nodejs-doc" 2>/dev/null | grep -c "ok installed") -eq 1 ]; then + info "[Cleanup] Removing nodejs-doc package" + apt purge -y ${package} + fi + + if [ "$RUNNING_ON_PI" = true ]; then + info "[Package] Installing Node.js v12.22.8" + wget -O - https://raw.githubusercontent.com/audstanley/NodeJs-Raspberry-Pi/master/Install-Node.sh | bash + node-install -v 12.22.8 + NODEJS_CHECKED=true + check_nodejs + else + info "[Package] Installing latest Node.js v12" + curl -fsSL https://deb.nodesource.com/setup_12.x | bash - + apt-get install -y nodejs + NODEJS_CHECKED=true + check_nodejs + fi +} + +common_software() { + info "### First we update your system. That's not worth mentioning." + apt update + apt dist-upgrade -y + + info "### Photobooth needs some software to run." + if [ "$WEBSERVER" == "nginx" ]; then + nginx_webserver + elif [ "$WEBSERVER" == "lighttpd" ]; then + lighttpd_webserver + else + apache_webserver + fi + + info "### Installing common software..." + for package in "${COMMON_PACKAGES[@]}"; do + if [ $(dpkg-query -W -f='${Status}' ${package} 2>/dev/null | grep -c "ok installed") -eq 1 ]; then + info "[Package] ${package} installed already" + else + info "[Package] Installing missing common package: ${package}" + if [[ ${package} == "yarn" ]]; then + curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - + echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list + apt update + fi + apt install -y ${package} + fi + done + + if [ "$NEEDS_NODEJS_CHECK" = true ]; then + check_nodejs + fi +} + apache_webserver() { info "### Installing Apache Webserver..." apt install -y libapache2-mod-php @@ -139,253 +306,130 @@ EOF fi } -echo " - - - @@@@@@@@@@@@@@@@@@@ - @@. .@@ - %@@@@@@. @@ @@@@@@@@@ @@ - @@@ @@* @@. .@@ - &@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@& -@@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@@@ -@@ @@ -@@ @@@@@@@@@@@@@. *@@ @@@@@ @@ -@@ @@@@ @@@@ @@ -@@@@@@@@@@@@@@@@@@@@@ #@@@@@@@# @@@@@@@@@@@@@@@@@@@@@ -@@ @@@ @@@@( (@@@@ @@@ @@ -@@ &@@ .@@% %@@. @@& @@ -@@ @@ @@ @@ @@ @@ -@@ %@@ @@* /@@ @@% @@ -@@ @@% @@ @@ %@@ @@ -@@ *@@ @@& &@@ @@* @@ -@@ @@ @@* *@@ @@ @@ -@@ @@ @@@ @@@ @@ @@ -@@%%%%%%%%%%%%%%%@@% @@@@@&%&@@@@@ %@@%%%%%%%%%%%%%%%@@ -@@@@@@@@@@@@@@@@@@@@@@ *&@&* @@@@@@@@@@@@@@@@@@@@@@ -@@ ,@@@@& &@@@@, @@ -@@ (@@@@@@@@@( @@ -@@ @@ -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -" - -info "### The Photobooth installer for your Raspberry Pi." - -info "### First we update your system. That's not worth mentioning." -apt update -apt dist-upgrade -y +general_setup() { + if [ "$SUBFOLDER" = true ]; then + cd /var/www/html/ + INSTALLFOLDER="photobooth" + INSTALLFOLDERPATH="/var/www/html/$INSTALLFOLDER" + else + cd /var/www/ + INSTALLFOLDER="html" + INSTALLFOLDERPATH="/var/www/html" + fi -info "### Photobooth needs some software to run." -if [ "$webserver" == "nginx" ]; then - nginx_webserver -elif [ "$webserver" == "lighttpd" ]; then - lighttpd_webserver -else - apache_webserver -fi + if [ -d "$INSTALLFOLDERPATH" ]; then + BACKUPFOLDER="html-$DATE" + info "${INSTALLFOLDERPATH} found. Creating backup as ${BACKUPFOLDER}." + mv "$INSTALLFOLDER" "$BACKUPFOLDER" + else + info "$INSTALLFOLDERPATH not found." + fi -info "### Installing common software..." -for package in "${COMMON_PACKAGES[@]}"; do - if [ $(dpkg-query -W -f='${Status}' ${package} 2>/dev/null | grep -c "ok installed") -eq 1 ]; then - info "[Package] ${package} installed already" + if [ "$INSTALLFOLDER" == "photobooth" ]; then + URL="http://$IPADDRESS/photobooth" else - info "[Package] Installing missing common package: ${package}" - if [[ ${package} == "yarn" ]]; then - curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - - echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list - apt update - fi - apt install -y ${package} + URL="http://$IPADDRESS" fi -done -echo -e "\033[0;33m### Is Photobooth the only website on this system?" -read -p "### Warning: If typing y, the whole /var/www/html folder will be removed! [y/N] " -n 1 -r deleteHtmlFolder -echo -e "\033[0m" -if [ "$deleteHtmlFolder" != "${deleteHtmlFolder#[Yy]}" ] ;then - info "### Ok, we will replace the html folder with the Photobooth." - cd /var/www/ - rm -rf html - INSTALLFOLDER="html" - INSTALLFOLDERPATH="/var/www/html" -else - info "### Ok, we will install Photobooth into /var/www/html/photobooth." - cd /var/www/html/ - INSTALLFOLDER="photobooth" - INSTALLFOLDERPATH="/var/www/html/$INSTALLFOLDER" -fi +} -info "### Now we are going to install Photobooth." -git clone https://github.com/andi34/photobooth $INSTALLFOLDER -cd $INSTALLFOLDERPATH -LATEST_VERSION=$( git describe --tags `git rev-list --tags --max-count=1` ) +start_install() { + info "### Now we are going to install Photobooth." + if [ $GIT_INSTALL = true ]; then + git clone https://github.com/andi34/photobooth $INSTALLFOLDER + cd $INSTALLFOLDERPATH -echo -e "\033[0;33m### Please select a version to install:" -echo -e " 1 Install last development version" -echo -e " 2 Install latest stable Release: $LATEST_VERSION" -echo -e " 3 Install last v2 Release (v2.10.0)" -read -p "Please enter your choice: " -n 1 -r -echo -e "\033[0m" -if [[ $REPLY =~ ^[1]$ ]] -then - info "### We are installing last development version" - VERSION="development" - git fetch origin dev - git checkout origin/dev -elif [[ $REPLY =~ ^[3]$ ]] -then - info "### We are installing v2.10.0" - VERSION="stable2" - git fetch origin stable2 - git checkout origin/stable2 -else - if [[ ! $REPLY =~ ^[2]$ ]] - then - info "### Invalid choice!" - fi - VERSION="stable3" - info "### We are installing latest stable Release: $LATEST_VERSION" - git checkout $LATEST_VERSION -fi + info "### We are installing last development version via git." + git fetch origin $BRANCH + git checkout origin/$BRANCH -git submodule update --init + git submodule update --init -info "### Get yourself a hot beverage. The following step can take up to 15 minutes." -yarn install -yarn build + info "### Get yourself a hot beverage. The following step can take up to 15 minutes." + yarn install + yarn build + else + info "### We are downloading the latest release and extracting it to $INSTALLFOLDERPATH." + curl -s https://api.github.com/repos/andi34/photobooth/releases/latest | + jq '.assets[].browser_download_url | select(endswith(".tar.gz"))' | + xargs curl -L --output /tmp/photobooth-latest.tar.gz + + mkdir -p $INSTALLFOLDERPATH + tar -xzvf /tmp/photobooth-latest.tar.gz -C $INSTALLFOLDERPATH + cd $INSTALLFOLDERPATH + fi +} -# Pi specific setup start -if [ "$RUNNING_ON_PI" = true ]; then -echo -e "\033[0;33m### Do you like to use a Raspberry Pi (HQ) Camera to take pictures?" -read -p "### If yes, this will generate a personal configuration with all needed changes. [y/N] " -n 1 -r -echo -e "\033[0m" -if [[ $REPLY =~ ^[Yy]$ ]] -then - (cat << EOF) > $INSTALLFOLDERPATH/config/my.config.inc.php +pi_camera() { + cat > $INSTALLFOLDERPATH/config/my.config.inc.php << EOF array ( - 'cmd' => 'raspistill -n -o %s -q 100 -t 1 | echo "Done"', + 'cmd' => 'libcamera-still -n -o %s -q 100 -t 1 | echo "Done"', 'msg' => 'Done', ), ); EOF -fi -fi -# Pi specific setup end - -info "### Setting permissions." -chown -R www-data:www-data $INSTALLFOLDERPATH/ -gpasswd -a www-data plugdev -gpasswd -a www-data video - -if [ -f "/usr/lib/gvfs/gvfs-gphoto2-volume-monitor" ]; then - info "### Disabling camera automount." - chmod -x /usr/lib/gvfs/gvfs-gphoto2-volume-monitor -fi - -echo -e "\033[0;33m### You probably like to use a printer." -read -p "### You like to install CUPS and set needing printer permissions? [y/N] " -n 1 -r -echo -e "\033[0m" -if [[ $REPLY =~ ^[Yy]$ ]] -then - info "### Installing CUPS and setting printer permissions." - - apt install -y cups - gpasswd -a www-data lp - gpasswd -a www-data lpadmin -fi - -# Pi specific setup start -if [ "$RUNNING_ON_PI" = true ]; then -echo -e "\033[0;33m### You probably like to start the browser on every start." -read -p "### Open Chromium in Kiosk Mode at every boot and hide the mouse cursor? [y/N] " -n 1 -r -echo -e "\033[0m" -if [[ $REPLY =~ ^[Yy]$ ]] -then - apt install -y unclutter - - cat >> /etc/xdg/lxsession/LXDE-pi/autostart < /etc/udev/rules.d/20-photobooth-gpiomem.rules <> /boot/config.txt << EOF -dtoverlay=gpio-no-irq + # Add configuration required for www-data to be able to initiate system shutdown / reboot + info "### Note: In order for the shutdown and reboot button to work we install /etc/sudoers.d/020_www-data-shutdown" + cat > /etc/sudoers.d/020_www-data-shutdown << EOF +# Photobooth buttons for www-data to shutdown or reboot the system from admin panel or via remotebuzzer +www-data ALL=(ALL) NOPASSWD: /sbin/shutdown EOF -else -# latest development version + stable3 - -# remove old artifacts from node-rpio library, if there was -if [ -f '/etc/udev/rules.d/20-photobooth-gpiomem.rules' ]; then - info "### Remotebuzzer switched from node-rpio to onoff library. We detected an old remotebuzzer installation and will remove artifacts" - rm -f /etc/udev/rules.d/20-photobooth-gpiomem.rules - sed -i '/dtoverlay=gpio-no-irq/d' /boot/config.txt -fi -# add configuration required for onoff library -sed -i '/Photobooth/,/Photobooth End/d' /boot/config.txt + if [ "$RUNNING_ON_PI" = true ]; then + info "### Remote Buzzer Feature" + info "### Configure Raspberry PI GPIOs for Photobooth - please reboot in order use the Remote Buzzer Feature" + usermod -a -G gpio www-data + # remove old artifacts from node-rpio library, if there was + if [ -f '/etc/udev/rules.d/20-photobooth-gpiomem.rules' ]; then + info "### Remotebuzzer switched from node-rpio to onoff library. We detected an old remotebuzzer installation and will remove artifacts" + rm -f /etc/udev/rules.d/20-photobooth-gpiomem.rules + sed -i '/dtoverlay=gpio-no-irq/d' /boot/config.txt + fi + # add configuration required for onoff library + sed -i '/Photobooth/,/Photobooth End/d' /boot/config.txt cat >> /boot/config.txt << EOF # Photobooth gpio=16,17,20,21,22,26,27=pu # Photobooth End EOF -# add configuration required for www-data to be able to initiate system shutdown -info "### Note: In order for the shutdown button to work we install /etc/sudoers.d/020_www-data-shutdown" -cat >> /etc/sudoers.d/020_www-data-shutdown << EOF -# Photobooth Remotebuzzer shutdown button for www-data to shutdown the system -www-data ALL=(ALL) NOPASSWD: /sbin/shutdown -EOF -# update artifacts in user configuration from old remotebuzzer implementation -if [ -f "$INSTALLFOLDERPATH/config/my.config.inc.php" ]; then - sed -i '/remotebuzzer/{n;n;s/enabled/usebuttons/}' $INSTALLFOLDERPATH/config/my.config.inc.php -fi - -fi -# remotebuzzer config depending on version end + # update artifacts in user configuration from old remotebuzzer implementation + if [ -f "$INSTALLFOLDERPATH/config/my.config.inc.php" ]; then + sed -i '/remotebuzzer/{n;n;s/enabled/usebuttons/}' $INSTALLFOLDERPATH/config/my.config.inc.php + fi -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 - info "### Disabling automount for pi user" + if [ "$USB_SYNC" = true ]; then + info "### Disabling automount for pi user" - mkdir -p /home/pi/.config/pcmanfm/LXDE-pi/ - cat >> /home/pi/.config/pcmanfm/LXDE-pi/pcmanfm.conf <> /home/pi/.config/pcmanfm/LXDE-pi/pcmanfm.conf <> /etc/polkit-1/localauthority/50-local.d/udisks2.pkla <> /etc/polkit-1/localauthority/50-local.d/udisks2.pkla <> /etc/xdg/lxsession/LXDE-pi/autostart < -45) { - imagettftext($image, $fontsize, $fontrot, $fontlocx, $fontlocy + $linespacing, $color, $font, $line2text); + imagettftext($sourceResource, $fontsize, $fontrot, $fontlocx, $fontlocy + $linespacing, $color, $font, $line2text); } else { - imagettftext($image, $fontsize, $fontrot, $fontlocx + $linespacing, $fontlocy, $color, $font, $line2text); + imagettftext($sourceResource, $fontsize, $fontrot, $fontlocx + $linespacing, $fontlocy, $color, $font, $line2text); } } if (!empty($line3text)) { if ($fontrot < 45 && $fontrot > -45) { - imagettftext($image, $fontsize, $fontrot, $fontlocx, $fontlocy + $linespacing * 2, $color, $font, $line3text); + imagettftext($sourceResource, $fontsize, $fontrot, $fontlocx, $fontlocy + $linespacing * 2, $color, $font, $line3text); } else { - imagettftext($image, $fontsize, $fontrot, $fontlocx + $linespacing * 2, $fontlocy, $color, $font, $line3text); + imagettftext($sourceResource, $fontsize, $fontrot, $fontlocx + $linespacing * 2, $fontlocy, $color, $font, $line3text); } } - imagejpeg($image, $srcImagePath, $quality); - imagedestroy($image); + return $sourceResource; } diff --git a/lib/collage.php b/lib/collage.php index 29a16f265..384dc8788 100644 --- a/lib/collage.php +++ b/lib/collage.php @@ -8,10 +8,11 @@ require_once __DIR__ . '/polaroid.php'; define('COLLAGE_LAYOUT', $config['collage']['layout']); -define('COLLAGE_FRAME', realpath(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . $config['collage']['frame'])); +define('COLLAGE_BACKGROUND_COLOR', $config['collage']['background_color']); +define('COLLAGE_FRAME', $config['collage']['frame']); define('COLLAGE_TAKE_FRAME', $config['collage']['take_frame']); +define('COLLAGE_DASHEDLINE_COLOR', $config['collage']['dashedline_color']); define('COLLAGE_LIMIT', $config['collage']['limit']); -define('PICTURE_KEEP_ORIGINAL', $config['picture']['keep_original'] === true ? 'keep' : 'discard'); define('PICTURE_FLIP', $config['picture']['flip']); define('PICTURE_ROTATION', $config['picture']['rotation']); define('PICTURE_POLAROID_EFFECT', $config['picture']['polaroid_effect'] === true ? 'enabled' : 'disabled'); @@ -29,17 +30,22 @@ define('TEXTONCOLLAGE_LINESPACE', $config['textoncollage']['linespace']); function createCollage($srcImagePaths, $destImagePath, $filter = 'plain') { + $editImages = []; $rotate_after_creation = false; $quality = 100; $image_filter = false; $imageModified = false; + $frame = realpath(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . COLLAGE_FRAME); if (!empty($filter) && $filter !== 'plain') { $image_filter = $filter; } - // colors for background while rotating jpeg images - $white = 16777215; - $black = 0; + // colors for background and while rotating jpeg images + list($bg_r, $bg_g, $bg_b) = sscanf(COLLAGE_BACKGROUND_COLOR, '#%02x%02x%02x'); + $bg_color_hex = hexdec(substr(COLLAGE_BACKGROUND_COLOR, 1)); + + // dashedline color on 2x4 collage layouts + list($dashed_r, $dashed_g, $dashed_b) = sscanf(COLLAGE_DASHEDLINE_COLOR, '#%02x%02x%02x'); if (!is_array($srcImagePaths) || count($srcImagePaths) !== COLLAGE_LIMIT) { return false; @@ -47,23 +53,24 @@ function createCollage($srcImagePaths, $destImagePath, $filter = 'plain') { for ($i = 0; $i < COLLAGE_LIMIT; $i++) { if (!file_exists($srcImagePaths[$i])) { - $errormsg = 'File ' . $srcImagePaths[$i] . ' does not exist'; + $errormsg = basename($_SERVER['PHP_SELF']) . ': File ' . $srcImagePaths[$i] . ' does not exist'; logErrorAndDie($errormsg); } - $imageResource = imagecreatefromjpeg($srcImagePaths[$i]); + $singleimage = substr($srcImagePaths[$i], 0, -4); + $editfilename = $singleimage . '-edit.jpg'; + copy($srcImagePaths[$i], $editfilename); + $editImages[] = $editfilename; + } + + for ($i = 0; $i < COLLAGE_LIMIT; $i++) { + $imageResource = imagecreatefromjpeg($editImages[$i]); // Only jpg/jpeg are supported if (!$imageResource) { - $errormsg = 'Could not read jpeg file. Are you taking raws?'; + $errormsg = basename($_SERVER['PHP_SELF']) . ': Could not read jpeg file. Are you taking raws?'; logErrorAndDie($errormsg); } - if (PICTURE_KEEP_ORIGINAL === 'keep') { - $singleimage = substr($srcImagePaths[$i], 0, -4); - $origfilename = $singleimage . '-orig.jpg'; - copy($srcImagePaths[$i], $origfilename); - } - if (PICTURE_FLIP !== 'off') { if (PICTURE_FLIP === 'horizontal') { imageflip($imageResource, IMG_FLIP_HORIZONTAL); @@ -82,62 +89,63 @@ function createCollage($srcImagePaths, $destImagePath, $filter = 'plain') { } if (PICTURE_ROTATION !== '0') { - $rotatedImg = imagerotate($imageResource, PICTURE_ROTATION, $white); - $imageResource = $rotatedImg; + $imageResource = rotateResizeImage($imageResource, PICTURE_ROTATION, COLLAGE_BACKGROUND_COLOR); $imageModified = true; } if (PICTURE_POLAROID_EFFECT === 'enabled') { - $polaroid_rotation = PICTURE_POLAROID_ROTATION; - $imageResource = effectPolaroid($imageResource, $polaroid_rotation, 200, 200, 200); + $imageResource = effectPolaroid($imageResource, PICTURE_POLAROID_ROTATION, 200, 200, 200); + $imageModified = true; + } + + $width = imagesx($imageResource); + $height = imagesy($imageResource); + + if ($width > $height) { + $landscape = true; + } else { + $landscape = false; + $imageResource = imagerotate($imageResource, 90, $bg_color_hex); + $width = imagesx($imageResource); + $height = imagesy($imageResource); $imageModified = true; } if ($imageModified) { - imagejpeg($imageResource, $srcImagePaths[$i], $quality); + imagejpeg($imageResource, $editImages[$i], $quality); } imagedestroy($imageResource); } - list($width, $height) = getimagesize($srcImagePaths[0]); - if ($width > $height) { - $landscape = true; - } else { - $landscape = false; - for ($i = 0; $i < COLLAGE_LIMIT; $i++) { - $tempImage = imagecreatefromjpeg($srcImagePaths[$i]); - $tempSubRotated = imagerotate($tempImage, 90, $white); - imagejpeg($tempSubRotated, $srcImagePaths[$i], $quality); - imagedestroy($tempImage); - } - list($width, $height) = getimagesize($srcImagePaths[0]); - } - switch (COLLAGE_LAYOUT) { case '2x2': $my_collage = imagecreatetruecolor($width, $height); - $background = imagecolorallocate($my_collage, 0, 0, 0); + $background = imagecolorallocate($my_collage, $bg_r, $bg_g, $bg_b); imagecolortransparent($my_collage, $background); if ($landscape == false) { $rotate_after_creation = true; } + $positions = [[0, 0], [$width / 2, 0], [0, $height / 2], [$width / 2, $height / 2]]; for ($i = 0; $i < 4; $i++) { $position = $positions[$i]; - if (!file_exists($srcImagePaths[$i])) { + if (!file_exists($editImages[$i])) { return false; } - if (COLLAGE_TAKE_FRAME === 'always') { - ApplyFrame($srcImagePaths[$i], $srcImagePaths[$i], COLLAGE_FRAME); + $tempSubImage = imagecreatefromjpeg($editImages[$i]); + + if (COLLAGE_TAKE_FRAME === 'always' && testFile(COLLAGE_FRAME)) { + $tempSubImage = applyFrame($tempSubImage, $frame); } - $tempSubImage = imagecreatefromjpeg($srcImagePaths[$i]); + // copy image to background imagecopyresized($my_collage, $tempSubImage, $position[0], $position[1], 0, 0, $width / 2, $height / 2, $width, $height); + // destroy temporary images imagedestroy($tempSubImage); } break; @@ -145,7 +153,7 @@ function createCollage($srcImagePaths, $destImagePath, $filter = 'plain') { $width = 1800; $height = 1200; $my_collage = imagecreatetruecolor($width, $height); - $background = imagecolorallocate($my_collage, 255, 255, 255); + $background = imagecolorallocate($my_collage, $bg_r, $bg_g, $bg_b); imagefill($my_collage, 0, 0, $background); if ($landscape == false) { @@ -153,42 +161,38 @@ function createCollage($srcImagePaths, $destImagePath, $filter = 'plain') { } $degrees = 0; $heightp = 469; - $widthp = 636; - $PositionsX = [125, 810, 125, 810]; //X offset in Pixel - $PositionsYtop = 111; // y offset for top pictures - $PositionsYbot = 625; //y offset for bottom pictures + $widthp = $heightp * 1.5; + $PositionsX = [170, 925, 170, 925]; // X offset in Pixel + $PositionsY = [111, 111, 625, 625]; // Y offset in Pixel for ($i = 0; $i < 4; $i++) { - if ($i < 2) { - ResizeCropImage($widthp, $heightp, $srcImagePaths[$i], $srcImagePaths[$i]); - $dX = $PositionsX[$i]; - $dY = $PositionsYtop; - list($widthNew, $heightNew) = getimagesize($srcImagePaths[$i]); - } else { - ResizeCropImage($widthp, $heightp, $srcImagePaths[$i], $srcImagePaths[$i]); - $dX = $PositionsX[$i]; - $dY = $PositionsYbot; - list($widthNew, $heightNew) = getimagesize($srcImagePaths[$i]); - } - - if (!file_exists($srcImagePaths[$i])) { + if (!file_exists($editImages[$i])) { return false; } - if (COLLAGE_TAKE_FRAME === 'always') { - ApplyFrame($srcImagePaths[$i], $srcImagePaths[$i], COLLAGE_FRAME); + $dX = $PositionsX[$i]; + $dY = $PositionsY[$i]; + + $tempSubImage = imagecreatefromjpeg($editImages[$i]); + $tempSubImage = resizeCropImage($widthp, $heightp, $tempSubImage); + + $widthNew = imagesx($tempSubImage); + $heightNew = imagesy($tempSubImage); + + if (COLLAGE_TAKE_FRAME === 'always' && testFile(COLLAGE_FRAME)) { + $tempSubImage = applyFrame($tempSubImage, $frame); } - $tempSubImage = imagecreatefromjpeg($srcImagePaths[$i]); - $tempSubRotated = imagerotate($tempSubImage, $degrees, $white); // Rotate image - imagecopy($my_collage, $tempSubRotated, $dX, $dY, 0, 0, $widthNew, $heightNew); // copy image to background - imagedestroy($tempSubRotated); // Destroy temporary images - imagedestroy($tempSubImage); // Destroy temporary images + // copy image to background + imagecopy($my_collage, $tempSubImage, $dX, $dY, 0, 0, $widthNew, $heightNew); + // destroy temporary images + imagedestroy($tempSubImage); } break; case '2x4': $my_collage = imagecreatetruecolor($width, $height); - $background = imagecolorallocate($my_collage, 255, 255, 255); + $background = imagecolorallocate($my_collage, $bg_r, $bg_g, $bg_b); + $dashedline_color = imagecolorallocate($my_collage, $dashed_r, $dashed_g, $dashed_b); imagefill($my_collage, 0, 0, $background); if ($landscape) { @@ -198,17 +202,18 @@ function createCollage($srcImagePaths, $destImagePath, $filter = 'plain') { $images_rotated = []; for ($i = 0; $i < 4; $i++) { - if (!file_exists($srcImagePaths[$i])) { + if (!file_exists($editImages[$i])) { return false; } - if (COLLAGE_TAKE_FRAME === 'always') { - ApplyFrame($srcImagePaths[$i], $srcImagePaths[$i], COLLAGE_FRAME); + $tempSubImage = imagecreatefromjpeg($editImages[$i]); + + if (COLLAGE_TAKE_FRAME === 'always' && testFile(COLLAGE_FRAME)) { + $tempSubImage = applyFrame($tempSubImage, $frame); } - $tempSubImage = imagecreatefromjpeg($srcImagePaths[$i]); - $tempSubRotated = imagerotate($tempSubImage, $degrees, $white); - $images_rotated[] = resizeImage($tempSubRotated, $height / 3.3, $width / 3.5); + $tempSubImage = imagerotate($tempSubImage, $degrees, $bg_color_hex); + $images_rotated[] = resizeImage($tempSubImage, $height / 3.3, $width / 3.5); } $new_width = imagesx($images_rotated[0]); @@ -239,12 +244,14 @@ function createCollage($srcImagePaths, $destImagePath, $filter = 'plain') { } imagescale($my_collage, $width, $height); + imagedashedline($my_collage, 50, $height / 2, $width - 50, $height / 2, $dashedline_color); break; case '2x4-2': $width = 1800; $height = 1200; $my_collage = imagecreatetruecolor($width, $height); - $background = imagecolorallocate($my_collage, 255, 255, 255); + $background = imagecolorallocate($my_collage, $bg_r, $bg_g, $bg_b); + $dashedline_color = imagecolorallocate($my_collage, $dashed_r, $dashed_g, $dashed_b); imagefill($my_collage, 0, 0, $background); if ($landscape) { @@ -252,83 +259,84 @@ function createCollage($srcImagePaths, $destImagePath, $filter = 'plain') { } $degrees = 90; $widthNew = 321; - $heightNew = 482; - $PositionsX = [63, 423, 785, 1146]; //X offset in Pixel - $PositionsY = [64, 652]; //Y offset in Pixel + $heightNew = $widthNew * 1.5; + $PositionsX = [63, 423, 785, 1146]; // X offset in Pixel + $PositionsY = [64, 652]; // Y offset in Pixel for ($i = 0; $i < 4; $i++) { - ResizeCropImage($heightNew, $widthNew, $srcImagePaths[$i], $srcImagePaths[$i]); - } - list($width, $height) = getimagesize($srcImagePaths[0]); + if (!file_exists($editImages[$i])) { + return false; + } - for ($j = 0; $j < 2; $j++) { - //delta Y - $dY = $PositionsY[$j]; - for ($i = 0; $i < 4; $i++) { - // delta X + for ($j = 0; $j < 2; $j++) { $dX = $PositionsX[$i]; - if (!file_exists($srcImagePaths[$i])) { - return false; - } + $dY = $PositionsY[$j]; + + $tempSubImage = imagecreatefromjpeg($editImages[$i]); + $tempSubImage = resizeCropImage($heightNew, $widthNew, $tempSubImage); - if (COLLAGE_TAKE_FRAME === 'always') { - ApplyFrame($srcImagePaths[$i], $srcImagePaths[$i], COLLAGE_FRAME); + $width = imagesx($tempSubImage); + $height = imagesy($tempSubImage); + + if (COLLAGE_TAKE_FRAME === 'always' && testFile(COLLAGE_FRAME)) { + $tempSubImage = applyFrame($tempSubImage, $frame); } - $tempSubImage = imagecreatefromjpeg($srcImagePaths[$i]); - $tempSubRotated = imagerotate($tempSubImage, $degrees, $white); // Rotate image - imagecopy($my_collage, $tempSubRotated, $dX, $dY, 0, 0, $widthNew, $heightNew); // copy image to background - imagedestroy($tempSubRotated); // Destroy temporary images - imagedestroy($tempSubImage); // Destroy temporary images + // Rotate image + $tempSubImage = imagerotate($tempSubImage, $degrees, $bg_color_hex); + // copy image to background + imagecopy($my_collage, $tempSubImage, $dX, $dY, 0, 0, $widthNew, $heightNew); + + // destroy temporary images + imagedestroy($tempSubImage); } } + imagedashedline($my_collage, 50, 600, 1750, 600, $dashedline_color); break; case '1+3': $width = 1800; $height = 1200; $my_collage = imagecreatetruecolor($width, $height); - $background = imagecolorallocate($my_collage, 255, 255, 255); + $background = imagecolorallocate($my_collage, $bg_r, $bg_g, $bg_b); imagefill($my_collage, 0, 0, $background); if ($landscape == false) { $rotate_after_creation = true; } $degrees = 0; - $heightbig = 519; - $widthbig = 813; - $heightsmall = 361; - $widthsmall = 527; - $PositionsX = [0, 81, 638, 1196]; //X offset in Pixel for Small - $PositionsXB = 910; // X offset in Pixel for Big - $PositionsYB = 71; //Y offset in Pixel for Big Pic - $PositionsYS = 749; //Y offset in Pixel for Small Pic + $heightNewBig = 520; + $widthNewBig = $heightNewBig * 1.5; + $heightNewSmall = 360; + $widthNewSmall = $heightNewSmall * 1.5; + $PositionsX = [950, 80, 637, 1195]; // X offset in Pixel + $PositionsY = [71, 749, 749, 749]; // Y offset in Pixel for ($i = 0; $i < 4; $i++) { + if (!file_exists($editImages[$i])) { + return false; + } + + $dY = $PositionsY[$i]; + $dX = $PositionsX[$i]; + + $tempSubImage = imagecreatefromjpeg($editImages[$i]); if ($i == 0) { - ResizeCropImage($widthbig, $heightbig, $srcImagePaths[$i], $srcImagePaths[$i]); - // delta X - $dX = $PositionsXB; - $dY = $PositionsYB; - list($widthNew, $heightNew) = getimagesize($srcImagePaths[0]); + $tempSubImage = resizeCropImage($widthNewBig, $heightNewBig, $tempSubImage); } else { - ResizeCropImage($widthsmall, $heightsmall, $srcImagePaths[$i], $srcImagePaths[$i]); - $dX = $PositionsX[$i]; - $dY = $PositionsYS; - list($widthNew, $heightNew) = getimagesize($srcImagePaths[$i]); - } - if (!file_exists($srcImagePaths[$i])) { - return false; + $tempSubImage = resizeCropImage($widthNewSmall, $heightNewSmall, $tempSubImage); } - if (COLLAGE_TAKE_FRAME === 'always') { - ApplyFrame($srcImagePaths[$i], $srcImagePaths[$i], COLLAGE_FRAME); + $widthNew = imagesx($tempSubImage); + $heightNew = imagesy($tempSubImage); + + if (COLLAGE_TAKE_FRAME === 'always' && testFile(COLLAGE_FRAME)) { + $tempSubImage = applyFrame($tempSubImage, $frame); } - $tempSubImage = imagecreatefromjpeg($srcImagePaths[$i]); - $tempSubRotated = imagerotate($tempSubImage, $degrees, $white); // Rotate image - imagecopy($my_collage, $tempSubRotated, $dX, $dY, 0, 0, $widthNew, $heightNew); // copy image to background - imagedestroy($tempSubRotated); // Destroy temporary images - imagedestroy($tempSubImage); // Destroy temporary images + // copy image to background + imagecopy($my_collage, $tempSubImage, $dX, $dY, 0, 0, $widthNew, $heightNew); + // destroy temporary images + imagedestroy($tempSubImage); } break; case '1+3-2': @@ -336,7 +344,7 @@ function createCollage($srcImagePaths, $destImagePath, $filter = 'plain') { $width = 1800; $height = 1200; $my_collage = imagecreatetruecolor($width, $height); - $background = imagecolorallocate($my_collage, 255, 255, 255); + $background = imagecolorallocate($my_collage, $bg_r, $bg_g, $bg_b); imagefill($my_collage, 0, 0, $background); if ($landscape == false) { @@ -353,11 +361,11 @@ function createCollage($srcImagePaths, $destImagePath, $filter = 'plain') { for ($i = 0; $i < 4; $i++) { $position = $positions[$i]; - if (!file_exists($srcImagePaths[$i])) { + if (!file_exists($editImages[$i])) { return false; } - list($picWidth, $picHeight) = getimagesize($srcImagePaths[$i]); + list($picWidth, $picHeight) = getimagesize($editImages[$i]); if (COLLAGE_LAYOUT === '1+3-2') { switch ($i) { // Picture 1, // Picture 2, // Picture 3, @@ -390,14 +398,16 @@ function createCollage($srcImagePaths, $destImagePath, $filter = 'plain') { break; } } - ResizeCropImage($widthNew, $heightNew, $srcImagePaths[$i], $srcImagePaths[$i]); + $tempSubImage = imagecreatefromjpeg($editImages[$i]); + $tempSubImage = resizeCropImage($widthNew, $heightNew, $tempSubImage); - if (COLLAGE_TAKE_FRAME === 'always') { - ApplyFrame($srcImagePaths[$i], $srcImagePaths[$i], COLLAGE_FRAME); + if (COLLAGE_TAKE_FRAME === 'always' && testFile(COLLAGE_FRAME)) { + $tempSubImage = applyFrame($tempSubImage, $frame); } - $tempSubImage = imagecreatefromjpeg($srcImagePaths[$i]); - imagecopy($my_collage, $tempSubImage, $position[0], $position[1], 0, 0, $widthNew, $heightNew); // copy image to background + // copy image to background + imagecopy($my_collage, $tempSubImage, $position[0], $position[1], 0, 0, $widthNew, $heightNew); + // destroy temporary images imagedestroy($tempSubImage); } break; @@ -405,68 +415,55 @@ function createCollage($srcImagePaths, $destImagePath, $filter = 'plain') { $width = 1800; $height = 1200; $my_collage = imagecreatetruecolor($width, $height); - $background = imagecolorallocate($my_collage, 255, 255, 255); + $background = imagecolorallocate($my_collage, $bg_r, $bg_g, $bg_b); imagefill($my_collage, 0, 0, $background); if ($landscape == false) { $rotate_after_creation = true; } $degrees = 0; - $heightbig = 626; - $widthbig = 965; - $heightsmall = 422; - $widthsmall = 624; - $PositionsX = 1125; //X offset in Pixel for Small - $PositionsXB = 22; // X offset in Pixel for Big - $PositionsYB = 65; //Y offset in Pixel for Big Pic - $PositionsYS = 133; //Y offset in Pixel for Small Pic - $PositionsYSs = 634; //Y offset in Pixel for Small Pic + $heightNewBig = 626; + $widthNewBig = $heightNewBig * 1.5; + $heightNewSmall = 422; + $widthNewSmall = $heightNewSmall * 1.5; + $PositionsX = [40, 1125, 1125]; // X offset in Pixel + $PositionsY = [65, 133, 634]; // Y offset in Pixel for ($i = 0; $i < 3; $i++) { - if ($i == 0) { - ResizeCropImage($widthbig, $heightbig, $srcImagePaths[$i], $srcImagePaths[$i]); - // delta X - $dX = $PositionsXB; - $dY = $PositionsYB; - } elseif ($i == 1) { - ResizeCropImage($widthsmall, $heightsmall, $srcImagePaths[$i], $srcImagePaths[$i]); - // delta X - $dX = $PositionsX; - $dY = $PositionsYS; - list($widthNew, $heightNew) = getimagesize($srcImagePaths[$i]); - } elseif ($i == 2) { - ResizeCropImage($widthsmall, $heightsmall, $srcImagePaths[$i], $srcImagePaths[$i]); - // delta X - $dX = $PositionsX; - $dY = $PositionsYSs; - list($widthNew, $heightNew) = getimagesize($srcImagePaths[$i]); + if (!file_exists($editImages[$i])) { + return false; } - if (!file_exists($srcImagePaths[$i])) { - return false; + $dX = $PositionsX[$i]; + $dY = $PositionsY[$i]; + + $tempSubImage = imagecreatefromjpeg($editImages[$i]); + if ($i == 0) { + $tempSubImage = resizeCropImage($widthNewBig, $heightNewBig, $tempSubImage); + } else { + $tempSubImage = resizeCropImage($widthNewSmall, $heightNewSmall, $tempSubImage); } - if (COLLAGE_TAKE_FRAME === 'always') { - ApplyFrame($srcImagePaths[$i], $srcImagePaths[$i], COLLAGE_FRAME); + if (COLLAGE_TAKE_FRAME === 'always' && testFile(COLLAGE_FRAME)) { + $tempSubImage = applyFrame($tempSubImage, $frame); } if ($i == 0) { $degrees = 11; - $tempSubImage = imagecreatefromjpeg($srcImagePaths[$i]); - // Rotate image and add white background - $tempRotate = imagerotate($tempSubImage, $degrees, $white); - imagejpeg($tempRotate, $srcImagePaths[$i], $quality); - // get new width and height after rotation - list($widthNew, $heightNew) = getimagesize($srcImagePaths[$i]); - imagedestroy($tempRotate); - imagedestroy($tempSubImage); + // Rotate image and add background + $tempSubImage = rotateResizeImage($tempSubImage, $degrees, COLLAGE_BACKGROUND_COLOR); } + + $widthNew = imagesx($tempSubImage); + $heightNew = imagesy($tempSubImage); + + // Image 2 + 3 don't need rotation $degrees = 0; - $tempSubImage = imagecreatefromjpeg($srcImagePaths[$i]); - $tempSubRotated = imagerotate($tempSubImage, $degrees, $white); // Rotate image - imagecopy($my_collage, $tempSubRotated, $dX, $dY, 0, 0, $widthNew, $heightNew); // copy image to background - imagedestroy($tempSubRotated); // Destroy temporary images - imagedestroy($tempSubImage); // Destroy temporary images + + // copy image to background + imagecopy($my_collage, $tempSubImage, $dX, $dY, 0, 0, $widthNew, $heightNew); + // destroy temporary images + imagedestroy($tempSubImage); } break; default: @@ -474,15 +471,13 @@ function createCollage($srcImagePaths, $destImagePath, $filter = 'plain') { break; } - imagejpeg($my_collage, $destImagePath, $quality); // Transfer image to destImagePath with returns the image to core - - if (COLLAGE_TAKE_FRAME === 'once') { - ApplyFrame($destImagePath, $destImagePath, COLLAGE_FRAME); + if (COLLAGE_TAKE_FRAME === 'once' && testFile(COLLAGE_FRAME)) { + $my_collage = applyFrame($my_collage, $frame); } - if (TEXTONCOLLAGE_ENABLED === 'enabled') { - ApplyText( - $destImagePath, + if (TEXTONCOLLAGE_ENABLED === 'enabled' && testFile(TEXTONCOLLAGE_FONT)) { + $my_collage = applyText( + $my_collage, TEXTONCOLLAGE_FONT_SIZE, TEXTONCOLLAGE_ROTATION, TEXTONCOLLAGE_LOCATIONX, @@ -496,14 +491,19 @@ function createCollage($srcImagePaths, $destImagePath, $filter = 'plain') { ); } - imagedestroy($my_collage); // Destroy the created collage in memory - // Rotate image if needed if ($rotate_after_creation) { - $tempRotatedImage = imagecreatefromjpeg($destImagePath); - $resultRotated = imagerotate($tempRotatedImage, -90, $white); - imagejpeg($resultRotated, $destImagePath, $quality); - imagedestroy($tempRotatedImage); + $my_collage = imagerotate($my_collage, -90, $bg_color_hex); } + + // Transfer image to destImagePath with returns the image to core + imagejpeg($my_collage, $destImagePath, $quality); + // Destroy the created collage in memory + imagedestroy($my_collage); + + foreach ($editImages as $tmp) { + unlink($tmp); + } + return true; } diff --git a/lib/config.php b/lib/config.php index 79f4a4ea7..9fc3c9ae8 100644 --- a/lib/config.php +++ b/lib/config.php @@ -1,5 +1,6 @@ [ 'cmd' => '', ], + 'reboot' => [ + 'cmd' => '', + ], 'shutdown' => [ 'cmd' => '', ], @@ -50,6 +54,9 @@ 'nodebin' => [ 'cmd' => '/usr/bin/node', ], + 'reboot' => [ + 'cmd' => '/sbin/shutdown -r now', + ], 'shutdown' => [ 'cmd' => '/sbin/shutdown -h now', ], @@ -94,13 +101,14 @@ $config['preview']['cmd'] = $cmds[$os]['preview']['cmd']; $config['preview']['killcmd'] = $cmds[$os]['preview']['killcmd']; $config['nodebin']['cmd'] = $cmds[$os]['nodebin']['cmd']; +$config['reboot']['cmd'] = $cmds[$os]['reboot']['cmd']; $config['shutdown']['cmd'] = $cmds[$os]['shutdown']['cmd']; $config['adminpanel']['view_default'] = 'expert'; $config['remotebuzzer']['logfile'] = 'remotebuzzer_server.log'; $config['synctodrive']['logfile'] = 'synctodrive_server.log'; -$config['take_picture']['logfile'] = 'take_picture.log'; +$config['dev']['logfile'] = 'error.log'; $config['ui']['github'] = 'andi34'; $config['ui']['branding'] = 'Photobooth'; @@ -150,6 +158,14 @@ $config['background']['chroma'] = 'url(' . getrootpath('../resources/img/bg_bluegray.jpg') . ')'; } +if (!isset($config['webserver']['ip'])) { + $config['webserver']['ip'] = getPhotoboothIp(); +} + +if (!isset($config['qr']['url'])) { + $config['qr']['url'] = getPhotoboothUrl() . '/api/download.php?image='; +} + if (file_exists($my_config_file) && !is_writable($my_config_file)) { die('Abort. Can not write config/my.config.inc.php.'); } elseif (!file_exists($my_config_file) && !is_writable(__DIR__ . '/../config/')) { @@ -178,8 +194,4 @@ $config['foldersAbs'][$key] = $path; } -function getrootpath($relative_path) { - $realpath = realpath($relative_path); - $rootpath = str_replace($_SERVER['DOCUMENT_ROOT'], '', $realpath); - return $rootpath; -} +$config['folders']['lang'] = getrootpath('../resources/lang'); diff --git a/lib/configsetup.inc.php b/lib/configsetup.inc.php index 936baf80c..f372c3fd5 100644 --- a/lib/configsetup.inc.php +++ b/lib/configsetup.inc.php @@ -111,6 +111,24 @@ 'name' => 'dev[enabled]', 'value' => $config['dev']['enabled'], ], + 'dev_demo_images' => [ + 'view' => 'expert', + 'type' => 'checkbox', + 'name' => 'dev[demo_images]', + 'value' => $config['dev']['demo_images'], + ], + 'dev_advanced_log' => [ + 'view' => 'expert', + 'type' => 'checkbox', + 'name' => 'dev[advanced_log]', + 'value' => $config['dev']['advanced_log'], + ], + 'dev_logfile' => [ + 'view' => 'expert', + 'type' => 'hidden', + 'name' => 'dev[logfile]', + 'value' => $config['dev']['logfile'], + ], 'dev_debugpanel' => [ 'view' => 'expert', 'type' => 'button', @@ -166,12 +184,6 @@ 'name' => 'dev[reload_on_error]', 'value' => $config['dev']['reload_on_error'], ], - 'qr_enabled' => [ - 'view' => 'advanced', - 'type' => 'checkbox', - 'name' => 'qr[enabled]', - 'value' => $config['qr']['enabled'], - ], 'webserver_ip' => [ 'view' => 'advanced', 'type' => 'input', @@ -204,10 +216,10 @@ 'placeholder' => $defaultConfig['picture']['time_to_live'], 'name' => 'picture[time_to_live]', 'value' => $config['picture']['time_to_live'], - 'range_min' => 1000, - 'range_max' => 90000, - 'range_step' => 1000, - 'unit' => 'milliseconds', + 'range_min' => 1, + 'range_max' => 90, + 'range_step' => 1, + 'unit' => 'seconds', ], 'picture_preview_before_processing' => [ 'view' => 'expert', @@ -215,6 +227,28 @@ 'name' => 'picture[preview_before_processing]', 'value' => $config['picture']['preview_before_processing'], ], + 'picture_retry_on_error' => [ + 'view' => 'expert', + 'type' => 'range', + 'placeholder' => $defaultConfig['picture']['retry_on_error'], + 'name' => 'picture[retry_on_error]', + 'value' => $config['picture']['retry_on_error'], + 'range_min' => 0, + 'range_max' => 10, + 'range_step' => 1, + 'unit' => 'multiplied', + ], + 'picture_retry_timeout' => [ + 'view' => 'expert', + 'type' => 'range', + 'placeholder' => $defaultConfig['picture']['retry_timeout'], + 'name' => 'picture[retry_timeout]', + 'value' => $config['picture']['retry_timeout'], + 'range_min' => 0, + 'range_max' => 10, + 'range_step' => 1, + 'unit' => 'seconds', + ], 'delete_no_request' => [ 'view' => 'expert', 'type' => 'checkbox', @@ -248,6 +282,13 @@ 'name' => 'DISKUSAGEBUTTON', 'value' => 'diskusage-btn', ], + 'dependencies_button' => [ + 'view' => 'basic', + 'type' => 'button', + 'placeholder' => 'dependencies_check', + 'name' => 'DEPENDENCIESBUTTON', + 'value' => 'dependencies-btn', + ], ], 'frontpage' => [ 'view' => 'basic', @@ -257,6 +298,12 @@ 'name' => 'ui[show_fork]', 'value' => $config['ui']['show_fork'], ], + 'ui_skip_welcome' => [ + 'view' => 'advanced', + 'type' => 'checkbox', + 'name' => 'ui[skip_welcome]', + 'value' => $config['ui']['skip_welcome'], + ], 'ui_github' => [ 'view' => 'expert', 'type' => 'hidden', @@ -264,6 +311,7 @@ 'value' => $config['ui']['github'], ], 'event_enabled' => [ + 'view' => 'basic', 'type' => 'checkbox', 'name' => 'event[enabled]', 'value' => $config['event']['enabled'], @@ -525,14 +573,9 @@ ], 'textonpicture_font_color' => [ 'view' => 'expert', - 'type' => 'select', + 'type' => 'color', 'name' => 'textonpicture[font_color]', 'placeholder' => $defaultConfig['textonpicture']['font_color'], - 'options' => [ - 'white' => 'white', - 'grey' => 'grey', - 'black' => 'black', - ], 'value' => $config['textonpicture']['font_color'], ], 'textonpicture_font_size' => [ @@ -587,7 +630,7 @@ 'name' => 'collage[continuous_time]', 'placeholder' => $defaultConfig['collage']['continuous_time'], 'value' => $config['collage']['continuous_time'], - 'range_min' => 1, + 'range_min' => 0, 'range_max' => 20, 'range_step' => 1, 'unit' => 'seconds', @@ -609,6 +652,19 @@ ], 'value' => $config['collage']['layout'], ], + 'collage_dashedline_color' => [ + 'view' => 'advanced', + 'type' => 'color', + 'name' => 'collage[dashedline_color]', + 'placeholder' => $defaultConfig['collage']['dashedline_color'], + 'value' => $config['collage']['dashedline_color'], + ], + 'collage_keep_single_images' => [ + 'view' => 'advanced', + 'type' => 'checkbox', + 'name' => 'collage[keep_single_images]', + 'value' => $config['collage']['keep_single_images'], + ], 'collage_key' => [ 'view' => 'expert', 'type' => 'input', @@ -616,6 +672,13 @@ 'placeholder' => '', 'value' => $config['collage']['key'], ], + 'collage_background_color' => [ + 'view' => 'basic', + 'type' => 'color', + 'name' => 'collage[background_color]', + 'placeholder' => $defaultConfig['collage']['background_color'], + 'value' => $config['collage']['background_color'], + ], 'collage_take_frame' => [ 'view' => 'advanced', 'type' => 'select', @@ -696,14 +759,9 @@ ], 'textoncollage_font_color' => [ 'view' => 'expert', - 'type' => 'select', + 'type' => 'color', 'name' => 'textoncollage[font_color]', 'placeholder' => $defaultConfig['textoncollage']['font_color'], - 'options' => [ - 'white' => 'white', - 'grey' => 'grey', - 'black' => 'black', - ], 'value' => $config['textoncollage']['font_color'], ], 'textoncollage_font_size' => [ @@ -1185,14 +1243,9 @@ ], 'textonprint_font_color' => [ 'view' => 'expert', - 'type' => 'select', + 'type' => 'color', 'name' => 'textonprint[font_color]', 'placeholder' => $defaultConfig['textonprint']['font_color'], - 'options' => [ - 'white' => 'white', - 'grey' => 'grey', - 'black' => 'black', - ], 'value' => $config['textonprint']['font_color'], ], 'textonprint_font_size' => [ @@ -1210,6 +1263,41 @@ 'value' => $config['textonprint']['linespace'], ], ], + 'qr' => [ + 'view' => 'basic', + 'qr_enabled' => [ + 'view' => 'basic', + 'type' => 'checkbox', + 'name' => 'qr[enabled]', + 'value' => $config['qr']['enabled'], + ], + 'qr_url' => [ + 'view' => 'expert', + 'type' => 'input', + 'placeholder' => $defaultConfig['qr']['url'], + 'name' => 'qr[url]', + 'value' => htmlentities($config['qr']['url']), + ], + 'qr_append_filename' => [ + 'view' => 'expert', + 'type' => 'checkbox', + 'name' => 'qr[append_filename]', + 'value' => $config['qr']['append_filename'], + ], + 'qr_custom_text' => [ + 'view' => 'advanced', + 'type' => 'checkbox', + 'name' => 'qr[custom_text]', + 'value' => $config['qr']['custom_text'], + ], + 'qr_text' => [ + 'view' => 'advanced', + 'type' => 'input', + 'placeholder' => $defaultConfig['qr']['text'], + 'name' => 'qr[text]', + 'value' => htmlentities($config['qr']['text']), + ], + ], 'mail' => [ 'view' => 'basic', 'mail_enabled' => [ @@ -1352,6 +1440,24 @@ 'name' => 'remotebuzzer[userotary]', 'value' => $config['remotebuzzer']['userotary'], ], + 'remotebuzzer_usehid' => [ + 'view' => 'advanced', + 'type' => 'hidden', + 'name' => 'remotebuzzer[usehid]', + 'value' => $config['remotebuzzer']['usehid'], + ], + 'remotebuzzer_usesoftbtn' => [ + 'view' => 'advanced', + 'type' => 'hidden', + 'name' => 'remotebuzzer[usesoftbtn]', + 'value' => $config['remotebuzzer']['usesoftbtn'], + ], + 'remotebuzzer_usegpio' => [ + 'view' => 'advanced', + 'type' => 'hidden', + 'name' => 'remotebuzzer[usegpio]', + 'value' => $config['remotebuzzer']['usegpio'], + ], 'remotebuzzer_picturebutton' => [ 'view' => 'advanced', 'type' => 'checkbox', @@ -1453,6 +1559,17 @@ 'name' => 'remotebuzzer[enable_standalonegallery]', 'value' => $config['remotebuzzer']['enable_standalonegallery'], ], + 'remotebuzzer_debounce' => [ + 'view' => 'expert', + 'type' => 'range', + 'placeholder' => $defaultConfig['remotebuzzer']['debounce'], + 'name' => 'remotebuzzer[debounce]', + 'value' => $config['remotebuzzer']['debounce'], + 'range_min' => 0, + 'range_max' => 100, + 'range_step' => 5, + 'unit' => 'milliseconds', + ], 'remotebuzzer_logfile' => [ 'view' => 'expert', 'type' => 'hidden', @@ -1501,6 +1618,42 @@ 'value' => $config['synctodrive']['logfile'], ], ], + 'get_request' => [ + 'view' => 'advanced', + 'get_request_countdown' => [ + 'view' => 'basic', + 'type' => 'checkbox', + 'name' => 'get_request[countdown]', + 'value' => $config['get_request']['countdown'], + ], + 'get_request_processed' => [ + 'view' => 'basic', + 'type' => 'checkbox', + 'name' => 'get_request[processed]', + 'value' => $config['get_request']['processed'], + ], + 'get_request_server' => [ + 'view' => 'advanced', + 'type' => 'input', + 'placeholder' => 'http://xxx.xxx.xxx.xxx', + 'name' => 'get_request[server]', + 'value' => htmlentities($config['get_request']['server']), + ], + 'get_request_picture' => [ + 'view' => 'advanced', + 'type' => 'input', + 'placeholder' => $defaultConfig['get_request']['picture'], + 'name' => 'get_request[picture]', + 'value' => htmlentities($config['get_request']['picture']), + ], + 'get_request_collage' => [ + 'view' => 'advanced', + 'type' => 'input', + 'placeholder' => $defaultConfig['get_request']['collage'], + 'name' => 'get_request[collage]', + 'value' => htmlentities($config['get_request']['collage']), + ], + ], 'authentication' => [ 'view' => 'basic', 'login_enabled' => [ @@ -1535,6 +1688,18 @@ 'name' => 'protect[localhost_admin]', 'value' => $config['protect']['localhost_admin'], ], + 'protect_update' => [ + 'view' => 'advanced', + 'type' => 'checkbox', + 'name' => 'protect[update]', + 'value' => $config['protect']['update'], + ], + 'protect_localhost_update' => [ + 'view' => 'expert', + 'type' => 'checkbox', + 'name' => 'protect[localhost_update]', + 'value' => $config['protect']['localhost_update'], + ], 'protect_index' => [ 'view' => 'advanced', 'type' => 'checkbox', @@ -1547,6 +1712,19 @@ 'name' => 'protect[localhost_index]', 'value' => $config['protect']['localhost_index'], ], + 'protect_index_redirect' => [ + 'view' => 'advanced', + 'type' => 'select', + 'options' => [ + 'login' => 'Login', + 'gallery.php' => 'Standalone Gallery', + 'slideshow' => 'Standalone Slideshow', + 'private' => 'Private (private/index.php)', + ], + 'placeholder' => $defaultConfig['protect']['index_redirect'], + 'name' => 'protect[index_redirect]', + 'value' => $config['protect']['index_redirect'], + ], 'protect_manual' => [ 'view' => 'advanced', 'type' => 'checkbox', @@ -1586,6 +1764,12 @@ 'name' => 'button[homescreen]', 'value' => $config['button']['homescreen'], ], + 'ui_result_buttons' => [ + 'view' => 'advanced', + 'type' => 'checkbox', + 'name' => 'ui[result_buttons]', + 'value' => $config['ui']['result_buttons'], + ], 'ui_font_size' => [ 'view' => 'advanced', 'type' => 'input', @@ -1769,12 +1953,6 @@ 'name' => 'take_picture[cmd]', 'value' => htmlentities($config['take_picture']['cmd']), ], - 'take_picture_logfile' => [ - 'view' => 'expert', - 'type' => 'hidden', - 'name' => 'take_picture[logfile]', - 'value' => $config['take_picture']['logfile'], - ], 'take_picture_msg' => [ 'view' => 'expert', 'type' => 'input', @@ -1845,6 +2023,13 @@ 'name' => 'nodebin[cmd]', 'value' => htmlentities($config['nodebin']['cmd']), ], + 'reboot_cmd' => [ + 'view' => 'expert', + 'type' => 'input', + 'placeholder' => $defaultConfig['reboot']['cmd'], + 'name' => 'reboot[cmd]', + 'value' => htmlentities($config['reboot']['cmd']), + ], 'shutdown_cmd' => [ 'view' => 'expert', 'type' => 'input', @@ -1956,5 +2141,30 @@ 'name' => 'CHECKVERSIONBUTTON', 'value' => 'checkversion-btn', ], + 'updater_button' => [ + 'view' => 'basic', + 'type' => 'button', + 'placeholder' => 'updater', + 'name' => 'UPDATERBUTTON', + 'value' => 'updater-btn', + ], + ], + 'power' => [ + 'view' => 'basic', + 'platform' => 'linux', + 'reboot_button' => [ + 'view' => 'basic', + 'type' => 'button', + 'placeholder' => 'reboot_button', + 'name' => 'REBOOTBUTTON', + 'value' => 'reboot-btn', + ], + 'shutdown_button' => [ + 'view' => 'basic', + 'type' => 'button', + 'placeholder' => 'shutdown_button', + 'name' => 'SHUTDOWNBUTTON', + 'value' => 'shutdown-btn', + ], ], ]; diff --git a/lib/helper.php b/lib/helper.php new file mode 100644 index 000000000..9d0893995 --- /dev/null +++ b/lib/helper.php @@ -0,0 +1,75 @@ + $file . ' is a path! Frames need to be PNG, Fonts need to be ttf!', + ]; + logError($ErrorData); + return false; + } + + if (!file_exists($realPath)) { + $ErrorData = [ + 'error' => $file . ' does not exist!', + ]; + logError($ErrorData); + return false; + } + return true; +} diff --git a/lib/log.php b/lib/log.php index 2539ff8f4..df244debb 100644 --- a/lib/log.php +++ b/lib/log.php @@ -3,7 +3,7 @@ function logError($data) { global $config; - $logfile = $config['foldersAbs']['tmp'] . DIRECTORY_SEPARATOR . $config['take_picture']['logfile']; + $logfile = $config['foldersAbs']['tmp'] . DIRECTORY_SEPARATOR . $config['dev']['logfile']; $file_data = date('c') . ":\n" . print_r($data, true) . "\n"; if (is_file($logfile)) { diff --git a/lib/resize.php b/lib/resize.php index b0911dc6f..67cf22c47 100644 --- a/lib/resize.php +++ b/lib/resize.php @@ -1,5 +1,50 @@ $width) { + if ($new_width > $old_width) { //cut point by height - $h_point = ($height - $height_new) / 2; + $h_point = ($old_height - $new_height) / 2; //copy image - imagecopyresampled($dst_img, $src_img, 0, 0, 0, $h_point, $max_width, $max_height, $width, $height_new); + imagecopyresampled($dst_img, $source_file, 0, 0, 0, $h_point, $max_width, $max_height, $old_width, $new_height); } else { //cut point by width - $w_point = ($width - $width_new) / 2; - imagecopyresampled($dst_img, $src_img, 0, 0, $w_point, 0, $max_width, $max_height, $width_new, $height); + $w_point = ($old_width - $new_width) / 2; + imagecopyresampled($dst_img, $source_file, 0, 0, $w_point, 0, $max_width, $max_height, $new_width, $old_height); } - $image($dst_img, $dst_dir, $quality); - - if ($dst_img) { - imagedestroy($dst_img); - } - if ($src_img) { - imagedestroy($src_img); - } + return $dst_img; } diff --git a/lib/services_start.php b/lib/services_start.php index bc170faf1..94450112a 100644 --- a/lib/services_start.php +++ b/lib/services_start.php @@ -15,7 +15,7 @@ function processIsRunning($pName, $pidFile) { return count($output) - 1 ? true : false; // true if process is active } -if ($config['remotebuzzer']['usebuttons'] || $config['remotebuzzer']['userotary']) { +if ($config['remotebuzzer']['usebuttons'] || $config['remotebuzzer']['userotary'] || $config['remotebuzzer']['usehid'] || $config['remotebuzzer']['usesoftbtn']) { $connection = @fsockopen('127.0.0.1', $config['remotebuzzer']['port']); if (!is_resource($connection)) { diff --git a/livechroma.php b/livechroma.php index e8b3b13b1..ccf7035b2 100644 --- a/livechroma.php +++ b/livechroma.php @@ -26,7 +26,7 @@ $btnClass2 = 'btn'; } } else { - header('location: login'); + header('location: ' . $config['protect']['index_redirect']); exit(); } ?> @@ -60,6 +60,9 @@ + + +
    @@ -158,6 +161,7 @@ + diff --git a/login/index.php b/login/index.php index ca47c8b0e..41626b4a7 100644 --- a/login/index.php +++ b/login/index.php @@ -49,6 +49,9 @@ + + + @@ -72,6 +75,10 @@

    +

    + + +

    @@ -105,6 +112,6 @@ - + diff --git a/manual/index.php b/manual/index.php index 0b77e0620..82f5e63a2 100644 --- a/manual/index.php +++ b/manual/index.php @@ -39,6 +39,9 @@ + + +
    @@ -139,7 +142,7 @@ - + diff --git a/package.json b/package.json index 4a7d4e982..5a2249926 100644 --- a/package.json +++ b/package.json @@ -2,16 +2,29 @@ "name": "photobooth", "version": "3.2.1", "description": "A Photobooth webinterface for Raspberry Pi and Windows", + "engines": { + "node": "12.22" + }, "scripts": { - "build": "npm-run-all --serial format build:faq build:gulp", + "build": "npm-run-all --serial format build:faq build:gulp build:head clean:skipwelcome", "build:faq": "mdown --input 'faq/*.md' --output manual --header faq/header.php --footer faq/footer.html && mv manual/faq.html manual/faq.php", "build:gulp": "gulp", + "build:head": "git log --format='%h %s' -n 20 > HEAD", "build:sass": "gulp sass", + "clean": "npm-run-all --parallel clean:*", + "clean:css": "rm resources/css/*.css || echo 'resources/css/*.css can not be removed or no .css files exist!'", + "clean:faq": "rm manual/faq.php || echo 'faq.php can not be removed or does not exist!'", + "clean:head": "rm HEAD || echo 'HEAD can not be removed or file not exist!'", + "clean:js": "rm resources/js/*.js || echo 'resources/js/*.js can not be removed or no .js files exist!'", + "clean:node": "rm -rf node_modules/ || echo 'node_modules/ can not be removed or does not exist!'", + "clean:skipwelcome": "rm .skip_welcome || echo '.skip_welcome can not be removed or does not exist!'", "eslint": "eslint src/js/*.js", "eslint:fix": "eslint src/js/*.js --fix", "format": "npm-run-all --parallel format:*", "format:js": "prettier src/js/*.js --write", - "format:php": "prettier {api,lib,test}/{,*/**/}*.php --write", + "format:phpapi": "prettier api/{,*/**/}*.php --write", + "format:phplib": "prettier lib/*.php --write", + "format:phptest": "prettier test/*.php --write", "format:scss": "prettier src/sass/{,*/**/}*.scss --write", "pack:build": "npm-run-all --serial eslint build pack:zip", "pack:zip": "node scripts/pack-build.js", @@ -39,33 +52,34 @@ "events" ], "dependencies": { - "@andreasremdt/simple-translator": "^2.0.3", + "@andreasremdt/simple-translator": "^2.0.4", "font-awesome": "^4.7.0", "gh-markdown-cli": "^0.2.0", - "github-markdown-css": "^4.0.0", - "gulp": "4.0.2", - "gulp-sass": "4.1.0", + "github-markdown-css": "^5.1.0", + "gulp": "^4.0.2", + "gulp-sass": "^5.1.0", "jquery": "^3.6.0", "marvinj": "^1.0.0", "normalize.css": "^8.0.1", "npm-run-all": "^4.1.5", "onoff": "^6.0.3", + "sass": "^1.45.2", "selectize": "^0.12.6", - "socket.io": "^4.1.3", - "socket.io-client": "^4.1.3", + "socket.io": "^4.4.0", + "socket.io-client": "^4.4.0", "waypoints": "^4.0.1", "whatwg-fetch": "^3.6.2" }, "devDependencies": { - "@babel/core": "^7.14.8", - "@babel/preset-env": "^7.14.8", - "@prettier/plugin-php": "^0.17.3", + "@babel/core": "^7.16.7", + "@babel/preset-env": "^7.16.7", + "@prettier/plugin-php": "^0.17.6", "archiver": "^5.3.0", "colors": "^1.4.0", - "eslint": "^7.32.0", + "eslint": "^8.6.0", "eslint-plugin-node": "^11.1.0", "git-tag-version": "^1.3.1", "gulp-babel": "^8.0.0", - "prettier": "^2.3.2" + "prettier": "^2.5.1" } } diff --git a/photobooth.desktop b/photobooth.desktop index 6cb2e3065..551551c10 100644 --- a/photobooth.desktop +++ b/photobooth.desktop @@ -1,9 +1,9 @@ [Desktop Entry] -Version=1.0 +Version=1.2 Terminal=false Type=Application Name=Photobooth -Exec=chromium-browser --noerrdialogs --start-fullscreen --kiosk http://127.0.0.1 --incognito --disable-translate --no-first-run --fast --fast-start --disable-infobars --touch-events=enabled +Exec=chromium-browser --noerrdialogs --disable-infobars --disable-features=Translate --no-first-run --check-for-update-interval=31536000 --kiosk http://127.0.0.1 --touch-events=enabled Icon=/var/www/html/resources/img/favicon-96x96.png StartupNotify=false Terminal=false diff --git a/private/README.md b/private/README.md new file mode 100644 index 000000000..56939351a --- /dev/null +++ b/private/README.md @@ -0,0 +1,4 @@ +### Photobooth private files + +All files and folders inside here will be ignored on git. Please use this folder to place your own files (e.g. own frames, own backgrounds, own font ...). + diff --git a/reset-raspbian.sh b/reset-raspbian.sh deleted file mode 100644 index fce323a62..000000000 --- a/reset-raspbian.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/bash - -# Stop on the first sign of trouble -set -e - -function info { - echo -e "\033[0;36m${1}\033[0m" -} - -function error { - echo -e "\033[0;31m${1}\033[0m" -} - -if [ $UID != 0 ]; then - error "ERROR: Only root is allowed to execute the reset script. Forgot sudo?" - exit 1 -fi - -if [ ! -f /proc/device-tree/model ]; then - error "ERROR: This reset script is only intended to run on a Raspberry Pi." - exit 2 -fi - -PI_MODEL=$(tr -d '\0' https://keycode.info, um die Key-ID herauszufinden.", "manual:collage:collage_layout": "Wählen Sie zwischen den 2x2 und 2x4 Bilder Layouts für die Collage.", "manual:collage:collage_only": "Wenn aktiviert, können nur Fotocollagen aufgenommen werden.", @@ -199,10 +232,14 @@ "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": "Pfad zum Node.js Binary - Beispiel /usr/bin/node", + "manual:commands:post_photo_cmd": "Geben Sie eine Befehlszeile ein, die ausgeführt wird, nachdem das Bild aufgenommen wurde und die Verarbeitung abgeschlossen ist. Das Ausführen ist async / non-blocking. Für die Collage wird dies nur einmal ausgeführt nachdem die Verarbeitung der Collage abgeschlossen ist.", + "manual:commands:pre_photo_cmd": "Geben Sie eine Befehlszeile ein, die direkt ausgeführt wird, sobald Sie ein Bild / Collage aufnehmen. Ausführen ist async / non-blocking. Für Collagen wird dies für jedes einzelne Foto der Collage ausgeführt.", "manual:commands:preview_cmd": "Befehlszeile, die ausgeführt wird, um eine Live-Vorschau zu erzeugen.", "manual:commands:preview_killcmd": "Befehlszeile, die ausgeführt wird, um die Live-Vorschau zu beenden.", "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:reboot_cmd": "Befehl zum Initiieren eines Neustarts per Knopfdruck - Beispiel: /sbin/shutdown -r now.", + "manual:commands:shutdown_cmd": "Befehl zum Initiieren des Herunterfahrens per Knopfdruck - Beispiel: /sbin/shutdown -h now.", "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.", "manual:commands:take_picture_msg": "Vom \"Bild aufnehmen\" zurückgegebene Nachricht.", "manual:folders:folders_archives": "Geben Sie den Namen Ihres Archivordners ein.", @@ -219,6 +256,7 @@ "manual:frontpage:event_textLeft": "Geben Sie den Text ein, der auf der linken Seite des ausgewählten Symbols sichtbar ist.", "manual:frontpage:event_textRight": "Geben Sie den Text ein, der auf der rechten Seite des ausgewählten Symbols sichtbar ist.", "manual:frontpage:ui_show_fork": "Wenn diese Option aktiviert ist, wird in der rechten oberen Ecke des Startbildschirms ein Fork Badge angezeigt.", + "manual:frontpage:ui_skip_welcome": "Wenn Option aktiviert, ist die Begrüßungsseite beim ersten Zugriff auf Photobooth nach Installation/Update nicht sichtbar.", "manual:gallery:gallery_allow_delete": "Wenn diese Option aktiviert ist, können Bilder jederzeit aus der Galerie heraus gelöscht werden.", "manual:gallery:gallery_bottom_bar": "Wenn aktiviert, wird die Schaltflächenleiste in der Galerie unten angezeigt.", "manual:gallery:gallery_date_format": "Geben Sie Ihren Datumsstil ein.", @@ -249,7 +287,10 @@ "manual:general:database_file": "Name der Datenbankdatei.", "manual:general:database_rebuild": "Die Datenbank der Bilder neu aufbauen. Normalerweise ist dies nicht erforderlich, kann aber verwendet werden, um eine beschädigte Datenbankdatei zu reparieren.", "manual:general:delete_no_request": "Wenn aktiviert, werden Bilder ohne Bestätigungsanfrage gelöscht. Das Löschen von Bildern kann nicht rückgängig gemacht werden!", - "manual:general:dev_enabled": "Aktiviert den Entwicklungsmodus. Beispielbilder werden verwendet anstatt ein Bild aufzunehmen.", + "manual:general:dependencies_button": "Dies prüft, ob benötigte Abhängigkeiten installiert sind.", + "manual:general:dev_debugpanel": "Öffnet das Debug-Panel in einem separaten Tab. Stellen Sie sicher, dass Sie den Entwicklungsmodus aktiviert haben für weitere Informationen.", + "manual:general:dev_demo_images": "Wenn aktiviert, werden Beispielbilder verwendet anstatt ein Foto aufzunehmen.", + "manual:general:dev_enabled": "Aktiviert den Entwicklungsmodus mit erweiterter Protokollierung.", "manual:general:dev_error_messages": "Wenn diese Option aktiviert ist, werden bei einem Fehler echte Fehlermeldungen angezeigt.", "manual:general:dev_reload_on_error": "Wenn beim Aufnehmen eines Bildes ein Fehler auftritt, wird die Photobooth nach 5 Sekunden automatisch neu geladen.", "manual:general:diskusage_button": "Dies zeigt die Speicherplatzauslastung durch Photobooth-Datendateien (Bilder, Datenbanken, etc.) an.", @@ -257,8 +298,7 @@ "manual:general:download_thumbs": "Wenn aktiviert, werden Thumbnails beim Download verwendet (falls vorhanden) statt dem Bild in voller Größe.", "manual:general:picture_preview_before_processing": "Wenn diese Option aktiviert ist, werden Bilder vorgeladen und während der Filterverarbeitung angezeigt.", "manual:general:picture_thumb_size": "Thumbnail-Größe auswählen: XS = max 360px, S = max 540px, M = max 900px, L = max 1080px, XL = max 1260px", - "manual:general:picture_time_to_live": "Definieren Sie die Zeit in Millisekunden, die Ihr Bild nach der Aufnahme auf dem Ergebnisbildschirm sichtbar ist.", - "manual:general:qr_enabled": "Wenn diese Option aktiviert ist, wird eine QR-Schaltfläche auf dem Ergebnisbildschirm und in der Galerie angezeigt. Der Benutzer kann beim Scannen des QR-Codes ein Bild herunterladen. Wenn Sie über \"localhost\", \"127.0.0.1\" auf Photobooth zugreifen oder Photobooth in einem Unterordner installiert haben, definieren Sie bitte die IP-Adresse des Photobooth-Webservers, damit der QR-Code funktioniert.

    Beispiel, wenn auf Photobooth direkt zugegriffen werden kann: 192.168.0.50.

    Beispiel, wenn Photobooth in einem Unterordner installiert ist: 192.168.0.50/photobooth.

    ", + "manual:general:picture_time_to_live": "Wählen Sie eine Zeit zwischen 1 und 90 Sekunden. Ihr Bild ist nach der Aufnahme für diese Zeit auf dem Ergebnisbildschirm sichtbar.", "manual:general:start_screen_subtitle": "Geben Sie den auf der Startseite sichtbaren Untertitel ein.", "manual:general:start_screen_subtitle_visible": "Wenn aktiviert, ist der eingegebene Untertitel auf dem Startbildschirm sichtbar.", "manual:general:start_screen_title": "Geben Sie den auf der Startseite sichtbaren Titel ein.", @@ -275,13 +315,12 @@ "manual:keying:keying_variant": "Wählen Sie zwischen verschiedenen Chromakey-Algorithmen. Bitte beachten Sie: Seriously.js erfordert einen Browser, der WebGL unterstützt.", "manual:keying:live_keying_enabled": "Umleiten von Index zu livechroma.php, die Standard-Startseite kann nicht aufgerufen werden!", "manual:keying:live_keying_show_all": "Wenn aktiviert, werden das Originalbild und das Keying-Bild der Galerie hinzugefügt.", - "manual:mail:mail_alt_text": "Wenn die Option HTML-E-Mail aktiviert ist, wird der Inhalt dieses Feldes dann zum Mail-Text, wenn der E-Mail-Client des Empfängers keine HTML-E-Mails anzeigen kann.", "manual:mail:mail_enabled": "Wenn diese Option aktiviert ist, ist für jedes Bild in der Galerie eine E-Mail-Schaltfläche sichtbar. Abhängig von Ihren Einstellungen können Sie Bilder direkt per E-Mail senden oder die eingegebene E-Mail-Adresse in einer Datenbank sammeln.", "manual:mail:mail_file": "Dateiname für die E-Mail-Adressdatenbank.", "manual:mail:mail_fromAddress": "Geben Sie Ihre Absenderadresse ein, die beim Senden von Bildern per E-Mail verwendet wird.", "manual:mail:mail_fromName": "Geben Sie Ihren Absendernamen ein, der beim Senden von Bildern per E-Mail verwendet wird.", "manual:mail:mail_host": "Geben Sie Ihre E-Mail-Host-Adresse ein.", - "manual:mail:mail_is_html": "Wenn diese Option aktiviert ist, unterstützt das Mail-Text-Feld HTML-Formatierungs- und semantische Markup-Funktionen.", + "manual:mail:mail_is_html": "Wenn aktiviert, unterstützt das E-Mail-Textfeld HTML-Formatierung und semantische Markup-Funktionen.", "manual:mail:mail_password": "Geben Sie Ihr Passwort ein, mit dem Sie sich in Ihr E-Mail-Konto einloggen.", "manual:mail:mail_port": "Geben Sie den Port ein, über den E-Mails gesendet werden.", "manual:mail:mail_secure": "Geben Sie das Protokoll ein, welches für die Transportverschlüsselung zwischen dem E-Mail-Client und dem Mailserver verwendet werden soll (SSL oder TLS).", @@ -294,12 +333,13 @@ "manual:pictures:filters_enabled": "Wenn diese Option aktiviert ist, kann der Benutzer nach dem Aufnehmen eines Bildes einen Bildfilter auswählen.", "manual:pictures:picture_allow_delete": "Wenn diese Option aktiviert ist, können Bilder direkt nach der Aufnahme auf der Ergebnisseite gelöscht werden.", "manual:pictures:picture_cheese_time": "Legen Sie eine Zeit fest zur Anzeige von \"Cheeeeeeeese!\" nach dem Countdown.", + "manual:pictures:picture_cntdwn_offset": "Stellen Sie einen Versatz auf den Countdown ein, um die Verzögerung durch die Cheeese-Zeit, Fokussierung und Verschlusszeit zu kompensieren.", "manual:pictures:picture_cntdwn_time": "Legen Sie Ihre Countdown-Zeit fest.", "manual:pictures:picture_flip": "Wählen Sie, ob Ihr Bild nach der Aufnahme gespiegelt wird.", "manual:pictures:picture_frame": "Geben Sie den Pfad des Rahmens ein, der nach der Aufnahme auf Ihr Bild angewendet wird.", "manual:pictures:picture_keep_original": "Wenn diese Option aktiviert ist, werden die Originalbilder im tmp-Ordner gespeichert.", "manual:pictures:picture_key": "Geben Sie die Keyl-ID an, welche zum Erstellen eines Fotos verwendet werden soll (z.B. 13 ist die Eingabetaste). Verwenden Sie beispielsweise https://keycode.info, um die Key-ID herauszufinden.", - "manual:pictures:picture_naming": "Wählen Sie zwischen datumsformatierten, nummerierten oder zufällig benannten Bildern. Bei datumsformatierten Bildern können Sie das Bild mit Datum und Uhrzeit in der Galerie anzeigen lassen.", + "manual:pictures:picture_naming": "Wählen Sie zwischen datumsformatierten oder zufällig benannten Bildern. Bei datumsformatierten Bildern können Sie das Bild mit Datum und Uhrzeit in der Galerie anzeigen lassen.", "manual:pictures:picture_no_cheese": "Wenn aktiviert, werden Bilder direkt nach dem Countdown aufgenommen. \"Cheeeeeeeeeeeese!\" wird nach den Countdown nicht angezeigt.", "manual:pictures:picture_permissions": "Ändern der Berechtigungen für aufgenommene Bilder mit dem Befehl \"chmod\". Bitte ändern Sie diesen Wert nur, wenn Sie mit Dateiberechtigungen unter Linux / Unix vertraut sind.", "manual:pictures:picture_polaroid_effect": "Wenn diese Option aktiviert ist, wird nach der Aufnahme ein Polaroid-Effekt auf Ihr Bild angewendet.", @@ -318,6 +358,8 @@ "manual:pictures:textonpicture_locationx": "X-Koordinaten des Textes beim Hinzufügen von Text auf Bilder.", "manual:pictures:textonpicture_locationy": "Y-Koordinaten des Textes beim Hinzufügen von Text auf Bilder.", "manual:pictures:textonpicture_rotation": "Geben Sie einen Wert ein, der als Grad verwendet wird, um die der Text auf dem Bild gedreht wird.", + "manual:power:reboot_button": "Schaltfläche um einen Neustart auszulösen.", + "manual:power:shutdown_button": "Schaltfläche zum Herunterfahren.", "manual:preview:preview_asBackground": "Wenn diese Option aktiviert ist, wird ein Stream von Ihrer Gerätekamera als Hintergrund auf dem Startbildschirm verwendet.", "manual:preview:preview_camTakesPic": "Wenn diese Option aktiviert ist, wird ein Bild von der Gerätekamera aufgenommen, anstatt den Befehl \"Bild aufnehmen\" auszuführen. Bitte beachten Sie, dass die Auflösung des Bildes von der angegebenen Höhe und Breite abhängt, da dies wie ein Screenshot aufgenommen wird.", "manual:preview:preview_camera_mode": "Wählen Sie den Kameramodus Ihrer Gerätekamera (Kamera mit Blick nach vorne oder hinten).", @@ -353,7 +395,12 @@ "manual:print:textonprint_locationx": "X-Koordinaten des Textes beim Drucken von Text auf Ihr Bild.", "manual:print:textonprint_locationy": "Y-Koordinaten des Textes beim Drucken von Text auf Ihr Bild.", "manual:print:textonprint_rotation": "Geben Sie einen Wert ein, der als Gradzahl verwendet wird, um die der Text auf dem Bild gedreht wird.", - "manual:remotebuzzer:remotebuzzer_logfile": "Wenn der Dev-Mode aktiviert ist, schreibt der Server Debug-Information in die Logdatei. Die Logdatei liegt im tmp Verzeichnis und der Dateiname ist io_server.log.", + "manual:qr:qr_append_filename": "Wenn aktiviert, wird der Bildname zur QR-URL hinzugefügt.", + "manual:qr:qr_custom_text": "Wenn aktiviert, wird unter dem QR-Code ein eigener definierter Hilfetext angezeigt.", + "manual:qr:qr_enabled": "Wenn aktiviert, wird eine QR-Schaltfläche auf dem Ergebnisbildschirm und in der Galerie angezeigt. Der Benutzer kann beim Scannen des QR-Codes ein Bild herunterladen. Wenn Sie über \"localhost\" / \"127.0.0.1\" auf die Photobooth zugreifen oder Photobooth in einem Unterordner installiert haben, definieren Sie bitte die IP-Adresse des Photobooth-Webservers, damit der QR-Code funktioniert.

    Beispiel, wenn auf Photobooth direkt zugegriffen werden kann: 192.168.0.50.

    Beispiel, wenn Photobooth in einem Unterordner installiert ist: 192.168.0.50/photobooth.

    ", + "manual:qr:qr_text": "Definieren Sie einen eigenen Hilfetext, der unter dem QR-Code angezeigt wird.", + "manual:qr:qr_url": "Definieren Sie eine URL, auf die über den QR-Code verwiesen werden soll.", + "manual:remotebuzzer:remotebuzzer_logfile": "Wenn der Entwicklungsmodus aktiviert ist schreibt der Server Debug-Information in die Logdatei. Die Logdatei liegt im tmp Verzeichnis und der Dateiname ist io_server.log.", "manual:remotebuzzer:remotebuzzer_port": "Server TCP Port - Beispiel 14711.", "manual:reset:reset_button": "Die Konfiguration wird zurückgesetzt. Wenn Sie auch Bilder und / oder die Mail-Adress-Datenbank löschen wollen, aktivieren Sie zuerst die jeweilige Einstellung und speichern die Konfiguration ab. Führen Sie erst nach dem Speichern das Zurücksetzen aus.", "manual:reset:reset_remove_config": "Wenn diese Option aktiviert ist, wird die persönliche Konfiguration beim Zurücksetzen entfernt.", @@ -369,16 +416,23 @@ "manual:userinterface:background_admin": "CSS-Stil für den Hintergrund im Admin-Bereich.", "manual:userinterface:background_chroma": "CSS-Stil für den Hintergrund auf der Chromakeying Seite.", "manual:userinterface:background_defaults": "CSS-Stil für den Hintergrund auf Start-, Anmelde- und Diashow-Seiten.", + "manual:userinterface:button_homescreen": "Wenn deaktiviert, wird die Home-Schaltfläche auf dem Ergebnisbildschirm ausgeblendet.", "manual:userinterface:button_show_fs": "Wenn diese Option aktiviert ist, wird dem Startbildschirm eine Schaltfläche zum Umschalten des Vollbildmodus hinzugefügt.", "manual:userinterface:ui_decore_lines": "Wenn aktiviert, werden Linien auf der Startseite angezeigt.", "manual:userinterface:ui_font_size": "Geben Sie die Standardschriftgröße für die Photobooth-Oberfläche ein.", + "manual:userinterface:ui_result_buttons": "Wenn aktiviert, werden die Nachbearbeitungs-Schaltflächen auf dem Ergebnisbildschirm sichtbar. Wenn deaktiviert, wird die gesamte Schaltflächenleiste ausgeblendet.", "manual:userinterface:ui_rounded_corners": "Wenn diese Option aktiviert ist, verwendet die Photobooth-Benutzeroberfläche abgerundete Kanten.", "manual:userinterface:ui_style": "Wählen Sie einen Startseitenstil. Um einen eigenen Startseitenstil zu verwenden, erstellen und verwenden Sie \"/template/custom.template.php\" und \"/resources/css/custom_style.css\", falls eine dieser Dateien nicht lesbar ist, wird auf den Standard-Startseitenstil zurückgegriffen.", "manual:version:check_version": "Dies prüft online über das Github Projektarchiv auf die neueste Photobooth-Version.", + "manual:version:updater_button": "Prüft ob Photobooth aktualisiert und das Update durchgeführt werden kann. Funktioniert nur unter Linux, wenn Photobooth über Git installiert wurde.", "milliseconds": "ms", + "myconfig": "Meine Konfiguration", "newCollage": "Neue Collage", "newPhoto": "Neues Bild", - "nextPhoto": "Nächstes Bild", + "nextPhoto": "Weiter", + "no_connection": "Keine Verbindung zum Internet!", + "ok": "ok", + "os_check": "Betriebssystem wird geprüft...", "path": "Pfad:", "percent": "%", "pictures": "Bilder", @@ -387,6 +441,7 @@ "pictures:filters_enabled": "Bildfilter erlauben", "pictures:picture_allow_delete": "Löschen des Bildes erlauben", "pictures:picture_cheese_time": "Cheeeeeeeese!-Timer:", + "pictures:picture_cntdwn_offset": "Countdown-Versatz:", "pictures:picture_cntdwn_time": "Countdown Timer:", "pictures:picture_flip": "Bild spiegeln:", "pictures:picture_frame": "Rahmen", @@ -411,6 +466,9 @@ "pictures:textonpicture_locationx": "X-Koordinaten", "pictures:textonpicture_locationy": "Y-Koordinaten", "pictures:textonpicture_rotation": "Textdrehung", + "power": "Power", + "power:reboot_button": "Neustart", + "power:shutdown_button": "Herunterfahren", "preview": "Live-Vorschau", "preview:preview_asBackground": "Stream der Gerätekamera als Hintergrund nutzen", "preview:preview_camTakesPic": "Gerätekamera zur Bildaufnahme verwenden", @@ -451,27 +509,35 @@ "printing": "Druckauftrag gestartet! Bitte warten...", "processPhoto": "Collage verarbeiten", "qr": "QR-Code", + "qr:qr_append_filename": "Dateiname an die URL anhängen", + "qr:qr_custom_text": "Eigenen Hilfetext für QR verwenden", + "qr:qr_enabled": "QR-Codes aktivieren", + "qr:qr_text": "Eigener Hilfetext", + "qr:qr_url": "URL für QR-Code", "qrHelp": "Um das Bild auf Ihr Handy herunterzuladen, verbinden Sie sich mit dem WLAN:", "really_delete": "Wirklich nach Ihren Einstellungen zurücksetzen? Dies kann nicht rückgängig gemacht werden!", "really_delete_image": "wird gelöscht! Dies kann nicht rückgängig gemacht werden! Bild wirklich löschen?", + "reboot_button": "Neustart", "reload": "Seite neu laden", "remotebuzzer": "Hardware-Tasten", "remotebuzzer:remotebuzzer_collagebutton": "Collage-Taste", "remotebuzzer:remotebuzzer_collagetime": "Sekunden für Collage", - "remotebuzzer:remotebuzzer_enabled": "Unterstützung von Hardware-Tasten aktivieren", + "remotebuzzer:remotebuzzer_debounce": "Debounce-Zeit", "remotebuzzer:remotebuzzer_logfile": "Logfile", "remotebuzzer:remotebuzzer_picturebutton": "Bild-Taste", "remotebuzzer:remotebuzzer_port": "Server-Port", "remotebuzzer:remotebuzzer_printbutton": "Druck-Taste", "remotebuzzer:remotebuzzer_shutdownbutton": "Abschalt-Taste", "remotebuzzer:remotebuzzer_shutdownholdtime": "Sekunden um das Herunterfahren einzuleiten", - "remotebuzzer:remotebuzzer_userotary": "Rotary-Encoder Modus", + "remotebuzzer:remotebuzzer_usebuttons": "Hardware-Tasten aktivieren", + "remotebuzzer:remotebuzzer_usegpio": "GPIO für Remotebuzzer verwenden", + "remotebuzzer:remotebuzzer_usesoftbtn": "Software-Tasten aktivieren", "reset": "Zurücksetzen", "reset:reset_button": "Konfiguration zurücksetzen", "reset:reset_remove_config": "Persönliche Konfiguration löschen (my.config.inc.php)", "reset:reset_remove_images": "Bilder löschen", "reset:reset_remove_mailtxt": "E-Mail-Adressen-Datenbankdatei löschen", - "retakePhoto": "Aufnahme Wiederholen", + "retakePhoto": "Wiederholen", "save": "Speichern", "saveerror": "Fehler!", "saving": "Speichert ...", @@ -479,9 +545,11 @@ "selectFilter": "Bildfilter", "send": "Senden", "sendAllMail": "Sende mir in den nächsten Tagen ein Link zu allen Bildern", + "serverprocesses": "Prozesse", "show_faq": "FAQ öffnen", "show_manual": "Handbuch öffnen", "show_wiki": "Wiki öffnen (Internetverbindung erforderlich)", + "shutdown_button": "Herunterfahren", "slideshow": "Standalone-Diashow", "slideshow:slideshow_pictureTime": "Millisekunden die ein Bild in der Standalone-Diashow angezeigt wird", "slideshow:slideshow_randomPicture": "Zufällige Bilder anzeigen", @@ -495,9 +563,20 @@ "synctodrive:synctodrive_target": "USB Gerät", "takeCollage": "Collage erstellen!", "takePhoto": "Foto erstellen!", + "telegram": "Community @ Telegram", "test_update_available": "Es ist ein Test-Update verfügbar.", "toggleFullscreen": "Vollbildmodus umschalten", + "unsupported_os": "Betriebssystem nicht unterstützt!", "update_available": "Es ist ein Update verfügbar.", + "update_check_git": "Prüfe Git-Repository...", + "update_done": "Erfolgreich Aktualisiert!", + "update_error": "Etwas ist schiefgegangen!", + "update_fail": "Fehlgeschlagen! Überprüfen Sie das Konsolen-Protokoll für weitere Informationen!", + "update_git_commit": "Commit & Backup", + "update_no_git": "Kein Git Repo! Automatisches Update nicht möglich!", + "update_ready": "Bereit zum Aktualisieren!", + "update_to_stable": "Update auf die neueste Stable v3", + "updater": "Updater", "use_button": "Buzzer verwenden, um ein Bild aufzunehmen", "userinterface": "Benutzeroberfläche", "userinterface:background_admin": "Pfad zum Hintergrundbild im Admin Panel", @@ -523,5 +602,6 @@ "userinterface:ui_style": "Stil", "using_latest_version": "Photobooth ist auf dem aktuellen Stand.", "version": "Version", - "version:check_version": "Photobooth-Version" + "version:check_version": "Photobooth-Version", + "wait_message": "Bitte warten..." } diff --git a/resources/lang/el.json b/resources/lang/el.json index e508e53b8..02517e4a2 100644 --- a/resources/lang/el.json +++ b/resources/lang/el.json @@ -38,7 +38,6 @@ "general:dev_enabled": "Λειτουργία ανάπτυξης", "general:dev_error_messages": "Εμφάνιση μηνυμάτων σφάλματος", "general:download_enabled": "Επιτρέψτε τη λήψη", - "general:qr_enabled": "Ενεργοποιήστε τον κωδικό QR", "general:start_screen_subtitle": "Αρχική οθόνη (υπότιτλος)", "general:start_screen_title": "Αρχική οθόνη (τίτλος)", "general:ui_language": "Επιλέξτε γλώσσα", diff --git a/resources/lang/en.json b/resources/lang/en.json index 2a8f77c5a..8176d4324 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -10,16 +10,20 @@ "authentication:login_username": "Username", "authentication:protect_admin": "Protect admin panel", "authentication:protect_index": "Protect start screen", + "authentication:protect_index_redirect": "Destination from start screen when not logged in", "authentication:protect_localhost_admin": "Protect admin panel on localhost access", "authentication:protect_localhost_index": "Protect start screen on localhost access", "authentication:protect_localhost_manual": "Protect manual & FAQ on localhost access", + "authentication:protect_localhost_update": "Protect updater on localhost access", "authentication:protect_manual": "Protect manual & FAQ", + "authentication:protect_update": "Protect updater", "auto_reload": "Photobooth reloads automatically...", "available_version": "Available:", "bootconfig": "config.txt", "busy": "Processing ...", "busyCollage": "Processing Collage ...", - "cameralog": "Camera log", + "check_connection": "Checking connection...", + "check_dependencies": "Checking installed dependencies...", "check_version": "Check online", "cheese": "Cheeeeeeeese!", "cheeseCollage": "Cheeeeeeeese Collageeeeeeeeeee!", @@ -27,11 +31,14 @@ "chromaInfoBefore": "Please choose a background to take a Picture", "close": "Close", "collage": "Collage", + "collage:collage_background_color": "Collage background color", "collage:collage_cntdwn_time": "Collage-countdown timer:", "collage:collage_continuous": "Take collage without interruption", "collage:collage_continuous_time": "Show single images on continuous collage for:", + "collage:collage_dashedline_color": "Cutting line color on 2x4 Collage layouts", "collage:collage_enabled": "Allow photo collage", "collage:collage_frame": "Frame", + "collage:collage_keep_single_images": "Add single images from collage to gallery", "collage:collage_key": "Key code which triggers a collage", "collage:collage_layout": "Choose collage layout:", "collage:collage_only": "Only allow photo collage", @@ -57,7 +64,8 @@ "commands:preview_killcmd": "Command to kill live preview", "commands:print_cmd": "Print command", "commands:print_msg": "Success message for print", - "commands:shutdown_cmd": "Remote Buzzer shutdown command", + "commands:reboot_cmd": "Reboot command", + "commands:shutdown_cmd": "Shutdown command", "commands:take_picture_cmd": "Take picture command", "commands:take_picture_msg": "Success message for take picture", "current_version": "Installed:", @@ -66,7 +74,9 @@ "debugpanel:autorefresh": "Auto-Refresh", "degrees": "°", "delete": "Delete", + "dependencies_check": "Installed dependencies", "dev_debugpanel": "Debug panel", + "devlog": "Photobooth log", "disk_usage": "Disk Usage", "dot": ".", "download_zip": "Download data folder as zip", @@ -90,6 +100,7 @@ "frontpage:event_textLeft": "Text left", "frontpage:event_textRight": "Text right", "frontpage:ui_show_fork": "Show Fork Badge", + "frontpage:ui_skip_welcome": "Skip welcome screen", "gallery": "Gallery", "gallery:gallery_allow_delete": "Allow deletion of images from gallery", "gallery:gallery_bottom_bar": "Show button bar inside gallery on bottom", @@ -123,7 +134,10 @@ "general:database_file": "Database file name", "general:database_rebuild": "Rebuild image database", "general:delete_no_request": "Delete images without confirm request", + "general:dependencies_button": "Check installed dependencies", + "general:dev_advanced_log": "Advanced logging", "general:dev_debugpanel": "Debug Panel", + "general:dev_demo_images": "Use Demo Images", "general:dev_enabled": "Dev-Mode", "general:dev_error_messages": "Show error messages", "general:dev_reload_on_error": "Automatically reload Photobooth on error", @@ -131,9 +145,10 @@ "general:download_enabled": "Allow downloads", "general:download_thumbs": "Use thumbnails for download", "general:picture_preview_before_processing": "Preload and show image during filter processing", + "general:picture_retry_on_error": "Retry taking a picture", + "general:picture_retry_timeout": "Retry timeout", "general:picture_thumb_size": "Thumbnail size", "general:picture_time_to_live": "Show image after capture:", - "general:qr_enabled": "Use QR Codes", "general:start_screen_subtitle": "Start screen (subtitle)", "general:start_screen_subtitle_visible": "Show subtitle on start screen", "general:start_screen_title": "Start screen (title)", @@ -141,6 +156,13 @@ "general:ui_language": "Choose Language", "general:webserver_ip": "IP address of the Photobooth web server", "general:webserver_ssid": "Wireless network name (SSID) used to access the photobooth", + "get_request": "GET request", + "get_request:get_request_collage": "GET request for collage at countdown", + "get_request:get_request_countdown": "Send GET request to server at countdown", + "get_request:get_request_picture": "GET request for picture at countdown", + "get_request:get_request_processed": "Send \"Photostyle\" GET request to server after processing", + "get_request:get_request_server": "GET request server", + "githead": "Latest changes", "home": "Home", "insertMail": "Enter your e-mail address to receive the photo.", "jpeg_quality": "JPEG quality", @@ -184,15 +206,19 @@ "manual:authentication:login_username": "Define the username used to login into Photobooth.", "manual:authentication:protect_admin": "If enabled, admin panel can only be accessed if a username and password is entered.", "manual:authentication:protect_index": "If enabled, start screen can only be accessed if a username and password is entered.", + "manual:authentication:protect_index_redirect": "Sets the page where the user is redirected to, if protect index is enabled and user is not logged in.", "manual:authentication:protect_localhost_admin": "If disabled, admin panel can be accessed without username and password from localhost.", "manual:authentication:protect_localhost_index": "If disabled, start screen can be accessed without username and password from localhost.", "manual:authentication:protect_localhost_manual": "If disabled, manual and FAQ can be accessed without username and password from localhost.", + "manual:authentication:protect_localhost_update": "If disabled, updater can be accessed without username and password from localhost.", "manual:authentication:protect_manual": "If enabled, manual and FAQ can only be accessed if a username and password is entered.", + "manual:authentication:protect_update": "If enabled, updater can only be accessed if a username and password is entered.", "manual:collage:collage_cntdwn_time": "Set your countdown time between pictures while taking a collage.", "manual:collage:collage_continuous": "Take collage without interrupption.", "manual:collage:collage_continuous_time": "Controll the time a picture is visible on continuous collage before the next picture is taken.", "manual:collage:collage_enabled": "If enabled, user can take a collage. A collage consists of 4 pictures. Optional you can take a collage with or without interruption.", "manual:collage:collage_frame": "Enter the path of the frame which is applied to your collage after taking it.", + "manual:collage:collage_keep_single_images": "If enabled, single images from collage will be added to the gallery. Please note that single images are not visible if you access the gallery at the result screen!", "manual:collage:collage_key": "Specify the key id to use that key to take a collage (e.g. 13 is the enter key). For example use https://keycode.info to find out the key id.", "manual:collage:collage_layout": "Choose between different collage layouts.", "manual:collage:collage_only": "If enabled, only collage can be taken.", @@ -217,7 +243,8 @@ "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.", "manual:commands:print_msg": "Message returned from print command.", - "manual:commands:shutdown_cmd": "Command used by Remote Buzzer to initiate shutdown via button - example /sbin/shutdown -h now.", + "manual:commands:reboot_cmd": "Command used to initiate reboot via button - example /sbin/shutdown -r now.", + "manual:commands:shutdown_cmd": "Command used to initiate shutdown via button - example /sbin/shutdown -h now.", "manual:commands:take_picture_cmd": "Command line which is executed while pressing the \"Take Pic\" button. On Linux you can for example use gphoto2 to take pictures, on Windows you can use digiCamControl.", "manual:commands:take_picture_msg": "Message returned from take picture command.", "manual:folders:folders_archives": "Enter the name of your archives folder.", @@ -234,6 +261,7 @@ "manual:frontpage:event_textLeft": "Enter the text visible on the left side of the chosen symbol.", "manual:frontpage:event_textRight": "Enter the text visible on the right side of the chosen symbol.", "manual:frontpage:ui_show_fork": "If enabled, a fork badge is shown on the right upper corner on the start screen.", + "manual:frontpage:ui_skip_welcome": "If enabled, the welcome screen won't be visible while accessing Photobooth the first time after installation / update.", "manual:gallery:gallery_allow_delete": "If enabled pictures can be deleted from the gallery at any time.", "manual:gallery:gallery_bottom_bar": "If enabled, the button bar is shown in the gallery below.", "manual:gallery:gallery_date_format": "Enter your date style.", @@ -264,24 +292,33 @@ "manual:general:database_file": "Name of the database file.", "manual:general:database_rebuild": "Rebuild the images database. Normally not required but can be used to fix a corrupted database file.", "manual:general:delete_no_request": "If enabled, images will be deleted without a confirm request. Delete of images can't be undone!", + "manual:general:dependencies_button": "This will check if needed dependencies are installed.", + "manual:general:dev_advanced_log": "Success messages will be logged if enabled. Development mode must be enabled.", "manual:general:dev_debugpanel": "Opens the debug panel in a separate window. Make sure you have Dev-Mode enabled for more information.", - "manual:general:dev_enabled": "Enables development mode. Sample pictures will be used instead taking a picture.", + "manual:general:dev_demo_images": "If enabled, sample pictures will be used instead taking a picture.", + "manual:general:dev_enabled": "Enables development mode with advanced logging.", "manual:general:dev_error_messages": "If enabled real error messages are shown on error.", "manual:general:dev_reload_on_error": "If an error occurs while taking a picture, Photobooth will reload automatically after 5 seconds.", "manual:general:diskusage_button": "This will show the actual disk space utilization through Photobooth data files (pictures, database, etc.).", "manual:general:download_enabled": "If enabled, a download button is visible on each picture inside the gallery.", "manual:general:download_thumbs": "If enabled, thumbnails will be used at download (if exist) instead the full sized image.", "manual:general:picture_preview_before_processing": "If enabled, images are preloaded and shown during filter processing.", + "manual:general:picture_retry_on_error": "Set how often the system will try to take a picture in the event of an error. 0 = disabled.", + "manual:general:picture_retry_timeout": "Set a time after which the photo recording will be tried again (countdown). \"Retry taking a picture\" needs to be set to a value > 0.", "manual:general:picture_thumb_size": "Choose thumbnail size: XS = max 360px, S = max 540px, M = max 900px, L = max 1080px, XL = max 1260px", - "manual:general:picture_time_to_live": "Enter a value used as milliseconds. This value defines the time your picture is visible on the result screen after taking a picture.", - "manual:general:qr_enabled": "If enabled, a QR-Button is visible on the result screen and inside gallery. User can download a picture while scanning the QR-Code. If you're accessing Photobooth via \"localhost\", \"127.0.0.1\" or if you have Photobooth installed inside a subfolder, please define IP address of the Photobooth web server to make the QR-Code working.

    Example if Photobooth can be accessed directly: 192.168.0.50.

    Example if Photobooth is installed inside a subfolder: 192.168.0.50/photobooth.

    ", + "manual:general:picture_time_to_live": "Choose a time between 1 and 90 seconds. Your picture is visible for that time on the result screen after taking it.", "manual:general:start_screen_subtitle": "Enter the subtitle visible on startpage.", "manual:general:start_screen_subtitle_visible": "If enabled, entered subtitle is visible on start screen.", "manual:general:start_screen_title": "Enter the title visible on startpage.", "manual:general:start_screen_title_visible": "If enabled, entered title is visible on start screen.", "manual:general:ui_language": "Choose interface language.

    If you're missing a language or like to help improving translations visit the \"How to update or add translations?\" inside the Photobooth Wiki for instructions.

    ", - "manual:general:webserver_ip": "Please define the IP address of the Photobooth web server to make the QR-Code working if you're accessing Photobooth via \"localhost\", \"127.0.0.1\" or if you have Photobooth installed inside a subfolder.

    Example if Photobooth can be accessed directly: 192.168.0.50.

    Example if Photobooth is installed inside a subfolder: 192.168.0.50/photobooth.

    ", + "manual:general:webserver_ip": "Please define the IP address of the Photobooth web server if you're accessing Photobooth via \"localhost\", \"127.0.0.1\" or if you have Photobooth installed inside a subfolder.

    Example if Photobooth can be accessed directly: 192.168.0.50.

    Example if Photobooth is installed inside a subfolder: 192.168.0.50/photobooth.

    ", "manual:general:webserver_ssid": "Please define the wireless network name (SSID) to be used to access the Photobooth. The wireless network name (SSID) is displayed on the results page when the QR code is called up.", + "manual:get_request:get_request_collage": "Define GET request at countdown while taking a collage.", + "manual:get_request:get_request_countdown": "If enabled, a GET request will be made to defined Server at countdown. GET request needs to be defined for picture and collage.", + "manual:get_request:get_request_picture": "Define GET request at countdown while taking a picture.", + "manual:get_request:get_request_processed": "If enabled, a GET request will be made to defined Server after processing the picture. \"Photostyle\" (\"photo\", \"collage\" or \"chroma\") is used for the GET request.", + "manual:get_request:get_request_server": "Define the server for a GET request.", "manual:jpeg_quality:jpeg_quality_chroma": "Picture quality used for chromakeying pictures.", "manual:jpeg_quality:jpeg_quality_image": "Picture quality used for taking pictures. Value of -1 means the original file from the camera will be retained, if there is no other mods i.e. filters active.", "manual:jpeg_quality:jpeg_quality_thumb": "Picture quality used for thumbnails.", @@ -335,11 +372,13 @@ "manual:pictures:textonpicture_locationx": "X-Coordinates of the text while adding text to your picture.", "manual:pictures:textonpicture_locationy": "Y-Coordinates of the text while adding text to your picture.", "manual:pictures:textonpicture_rotation": "Enter a value which is used as degrees the text on your gets rotated.", + "manual:power:reboot_button": "Button to initiate a reboot.", + "manual:power:shutdown_button": "Button to initiate a shutdown.", "manual:preview:preview_asBackground": "If enabled, a stream from your device cam is used as background on start screen.", "manual:preview:preview_camTakesPic": "If enabled, a picture is taken from device cam instead executing the \"Take picture command\". Please note that the resolution depends on the given hight and width because it's acts like taking a screenshot.", - "manual:preview:preview_camera_mode": "Choose between front- or back facing camera mode of your device cam.", + "manual:preview:preview_camera_mode": "Choose between front- or back facing camera mode of your device cam. If gphoto2 live preview is enabled it is used here, as well. gphoto_bsm has to be enabled, as well. This is not recommended for a Raspberry Pi as it requires faster hardware.", "manual:preview:preview_flipHorizontal": "If enabled, preview from device cam is flipped horizontal.", - "manual:preview:preview_gphoto_bsm": "If enabled, gphoto2 live preview is started once a photo get triggered. This results in a delay of ~3 seconds until the preview is visible at countdown. If disabled, a preview is generated in background which results in a high battery usage and also a general slowdown.", + "manual:preview:preview_gphoto_bsm": "If enabled, gphoto2 live preview is started once a photo get triggered. This results in a delay of ~3 seconds until the preview is visible at countdown. If disabled, a preview is generated in background which results in a high battery usage and also a general slowdown. Camera is always active which might generate some heat and damage the camera sensor.", "manual:preview:preview_mode": "Choose a live preview mode. By default live preview is disabled, you can choose between a preview at countdown by your device cam and a preview from a URL. Preview \"from device cam\" will always use the camera of the device where Photobooth get opened in a Browser (e.g. on a tablet it will always show the tablet camera while on a smartphone it will always show the smartphone camera instead)! A secure origin or exception is required! You can find out how to set an exception here.", "manual:preview:preview_rotation": "Choose to rotate the preview from URL.", "manual:preview:preview_url": "CSS style to use a stream from an URL for preview while countdown.

    Example: url(../img/bg_bluegray.jpg)

    ", @@ -370,8 +409,14 @@ "manual:print:textonprint_locationx": "X-Coordinates of the text while printing text on your picture.", "manual:print:textonprint_locationy": "Y-Coordinates of the text while printing text on your picture.", "manual:print:textonprint_rotation": "Enter a value which is used as degrees the text gets rotated at print.", + "manual:qr:qr_append_filename": "If enabled, the image name will be added to the QR URL.", + "manual:qr:qr_custom_text": "If enabled, own defined help text will be visible below the QR Code.", + "manual:qr:qr_enabled": "If enabled, a QR-Button is visible on the result screen and inside gallery. User can download a picture while scanning the QR-Code. If you're accessing Photobooth via \"localhost\", \"127.0.0.1\" or if you have Photobooth installed inside a subfolder, please define IP address of the Photobooth web server to make the QR-Code working.

    Example if Photobooth can be accessed directly: 192.168.0.50.

    Example if Photobooth is installed inside a subfolder: 192.168.0.50/photobooth.

    ", + "manual:qr:qr_text": "Define own help text which will be visible below the QR Code.", + "manual:qr:qr_url": "Define a URL to point at via QR Code.", "manual:remotebuzzer:remotebuzzer_collagebutton": "For COLLAGE connect the hardware button to GPIO20. Pull GPIO to ground for to trigger. If enabled, long-press on PICTURE button will not trigger collage.", "manual:remotebuzzer:remotebuzzer_collagetime": "If PICTURE button pressed less than seconds here, a picture is triggered. If pressed more seconds than here, a collage is triggered. Only works if collage is enabled in the admin settings and the collage button is disbaled.", + "manual:remotebuzzer:remotebuzzer_debounce": "Debounce time for hardware buttons [ms]", "manual:remotebuzzer:remotebuzzer_enable_standalonegallery": "Controls whether the rotary encoder is active on the standalone gallery view.", "manual:remotebuzzer:remotebuzzer_logfile": "In Dev-Mode server debugging information will be written to the logfile, located in the tmp folder and defaults to io_server.log.", "manual:remotebuzzer:remotebuzzer_picturebutton": "For PICTURE connect the hardware button to GPIO21. Pull GPIO to ground for to trigger. Long-press will trigger COLLAGE, if enabled and Collage hardware button is disabled.", @@ -379,8 +424,11 @@ "manual:remotebuzzer:remotebuzzer_printbutton": "For PRINT connect the hardware button to GPIO26. Pull GPIO to ground for to trigger.", "manual:remotebuzzer:remotebuzzer_shutdownbutton": "For SHUTDOWN connect the hardware button to GPIO16. Pull GPIO to ground for to trigger. Hold 5 sec (default) to trigger.", "manual:remotebuzzer:remotebuzzer_shutdownholdtime": "Seconds to hold button until system shutdown will be initiated. Setting to Zero (0) means immediate shutdown without waiting time.", - "manual:remotebuzzer:remotebuzzer_usebuttons": "This feature enables hardware button support through Raspberry GPIO pins - see FAQ for details. IMPORTANT: For WLAN connected screens you must make sure to configure the IP address of the Photobooth web server in the section \"General\", for this feature to work properly.", - "manual:remotebuzzer:remotebuzzer_userotary": "Enable Rotary Encoder support for to navigate the screen. Needs a rotary encoder switch connected to the GPIOs - see FAQ for details. IMPORTANT: For WLAN connected screens you must make sure to configure the IP address of the Photobooth web server in the section \"General\", for this feature to work properly.", + "manual:remotebuzzer:remotebuzzer_usebuttons": "This feature enables hardware button support - see FAQ for details. Please activate GPIO support for remotebuzzer. IMPORTANT: For WLAN connected screens you must make sure to configure the IP address of the Photobooth web server in the section \"General\", for this feature to work properly.", + "manual:remotebuzzer:remotebuzzer_usegpio": "This feature enables hardware support through Raspberry GPIO pins - see FAQ for details. IMPORTANT: For WLAN connected screens you must make sure to configure the IP address of the Photobooth web server in the section \"General\", for this feature to work properly.", + "manual:remotebuzzer:remotebuzzer_usehid": "This feature enables HID Device support - see FAQ for details. Inside the picture section \"Key code which triggers a photo\" and inside collage section \"Key code which triggers a collage\" must be defined! IMPORTANT: For WLAN connected screens you must make sure to configure the IP address of the Photobooth web server in the section \"General\", for this feature to work properly.", + "manual:remotebuzzer:remotebuzzer_userotary": "Enable Rotary Encoder support for to navigate the screen. Needs a rotary encoder switch connected to the GPIOs - see FAQ for details. Please activate GPIO support for remotebuzzer. IMPORTANT: For WLAN connected screens you must make sure to configure the IP address of the Photobooth web server in the section \"General\", for this feature to work properly.", + "manual:remotebuzzer:remotebuzzer_usesoftbtn": "This feature enables Software Button support - see FAQ for details. IMPORTANT: For WLAN connected screens you must make sure to configure the IP address of the Photobooth web server in the section \"General\", for this feature to work properly.", "manual:reset:reset_button": "Will execute config reset. If you like to also reset images and / or the mail address database make sure you first activate those settings and save (!) the config, then perform the config reset itself.", "manual:reset:reset_remove_config": "If enabled, personal config gets removed on reset.", "manual:reset:reset_remove_images": "If enabled, all images gets removed on reset.", @@ -399,14 +447,20 @@ "manual:userinterface:button_show_fs": "If enabled, a button to toggle fullscreenmode will be added to the start screen.", "manual:userinterface:ui_decore_lines": "If enabled, decorate lines will be visible on start page.", "manual:userinterface:ui_font_size": "Enter the default font size used for the Photobooth interface.", + "manual:userinterface:ui_result_buttons": "If enabled, post-processing buttons will be visible on the on result screen. If disabled the whole button bar will be hidden.", "manual:userinterface:ui_rounded_corners": "If enabled, the Photobooth UI uses smooth rounded edges.", "manual:userinterface:ui_style": "Choose a start page styling. To use custom styling please create and use \"/template/custom.template.php\" and \"/resources/css/custom_style.css\", if one of these files is not readable we will fallback to default theme.", "manual:version:check_version": "This will check online against the Github repository for the latest Photobooth version.", + "manual:version:updater_button": "This will check if Photobooth can be updated and perform the update. Only works on Linux if Photobooth was installed via git.", "milliseconds": "ms", + "multiplied": "x", "myconfig": "My Config", "newCollage": "New Collage", "newPhoto": "New Picture", "nextPhoto": "Next Picture", + "no_connection": "Not internect connection!", + "ok": "ok", + "os_check": "Checking OS...", "path": "Path:", "percent": "%", "pictures": "Pictures", @@ -440,8 +494,11 @@ "pictures:textonpicture_locationx": "X Coordinate", "pictures:textonpicture_locationy": "Y Coordinate", "pictures:textonpicture_rotation": "Text rotation", + "power": "Power", + "power:reboot_button": "Reboot", + "power:shutdown_button": "Shutdown", "preview": "Live preview", - "preview:preview_asBackground": "Use stream from device cam as background", + "preview:preview_asBackground": "Use video stream as background", "preview:preview_camTakesPic": "Device cam takes picture", "preview:preview_camera_mode": "Camera facing mode", "preview:preview_flipHorizontal": "Flip image from device cam horizontally", @@ -480,13 +537,20 @@ "printing": "Started printing! Please wait...", "processPhoto": "Process Collage", "qr": "QR Code", + "qr:qr_append_filename": "Append filename to URL", + "qr:qr_custom_text": "Use own help text on QR", + "qr:qr_enabled": "Use QR Codes", + "qr:qr_text": "Own help text", + "qr:qr_url": "URL for QR Code", "qrHelp": "To download the picture to your smartphone, connect to the WiFi:", "really_delete": "Really reset according to your settings? This cannot be undone!", "really_delete_image": "will be deleted! This cannot be undone! Really delete picture?", + "reboot_button": "Reboot", "reload": "Reload Page", "remotebuzzer": "Hardware Button", "remotebuzzer:remotebuzzer_collagebutton": "Collage Button", "remotebuzzer:remotebuzzer_collagetime": "Seconds to trigger collage", + "remotebuzzer:remotebuzzer_debounce": "Debounce time", "remotebuzzer:remotebuzzer_enable_standalonegallery": "Rotary for standalone gallery", "remotebuzzer:remotebuzzer_logfile": "Logfile", "remotebuzzer:remotebuzzer_picturebutton": "Picture Button", @@ -495,13 +559,17 @@ "remotebuzzer:remotebuzzer_shutdownbutton": "Shutdown Button", "remotebuzzer:remotebuzzer_shutdownholdtime": "Seconds to initiate shutdown", "remotebuzzer:remotebuzzer_usebuttons": "Enable Hardware Buttons", + "remotebuzzer:remotebuzzer_usegpio": "Use GPIO for remotebuzzer", + "remotebuzzer:remotebuzzer_usehid": "Enable HID-Device Buttons", "remotebuzzer:remotebuzzer_userotary": "Enable Rotary Encoder", + "remotebuzzer:remotebuzzer_usesoftbtn": "Enable Software Buttons", "reset": "Reset", "reset:reset_button": "Execute config reset", "reset:reset_remove_config": "Delete personal configuration (my.config.inc.php)", "reset:reset_remove_images": "Delete images", "reset:reset_remove_mailtxt": "Delete e-mail addresses database", "retakePhoto": "Retake", + "retry_message": "Taking picture failed. Retrying. Retry:", "save": "Save", "saveerror": "Error!", "saving": "Saving ...", @@ -513,6 +581,7 @@ "show_faq": "Open FAQ", "show_manual": "Open Manual", "show_wiki": "Open Wiki (internet connection required)", + "shutdown_button": "Shutdown", "slideshow": "Standalone Slideshow", "slideshow:slideshow_pictureTime": "Milliseconds an image is displayed at standalone slideshow", "slideshow:slideshow_randomPicture": "Show random pictures", @@ -529,7 +598,22 @@ "telegram": "Community @ Telegram", "test_update_available": "There is a test update available.", "toggleFullscreen": "Toggle Fullscreen", + "unsupported_os": "Unsupported OS!", "update_available": "There is an update available.", + "update_check_git": "Checking git repository...", + "update_commit_backup": "Commit & backup your changes to update!", + "update_committing": "Committing your changes...", + "update_done": "Updated!", + "update_error": "Something went wrong!", + "update_fail": "Failed! Check console log for more informations!", + "update_git_commit": "Commit & Backup", + "update_git_commited": "Changes committed!", + "update_no_git": "Not a git repo! Automated update not possible!", + "update_ready": "Ready to update!", + "update_running": "Updating... Please be patient! Update might take a while! ...", + "update_to_dev": "Update to latest development Version", + "update_to_stable": "Update to latest Stable v3", + "updater": "Updater", "use_button": "Use Buzzer to take a Picture", "userinterface": "User interface", "userinterface:background_admin": "Admin panel background image path", @@ -552,10 +636,12 @@ "userinterface:colors_start_font": "Start screen font color", "userinterface:ui_decore_lines": "Show decorate lines", "userinterface:ui_font_size": "Default font size", + "userinterface:ui_result_buttons": "Show button bar on result screen", "userinterface:ui_rounded_corners": "Rounded corners", "userinterface:ui_style": "Styling", "using_latest_version": "You are using the latest version of photobooth.", "version": "Version", "version:check_version": "Photobooth version", + "version:updater_button": "Update Photobooth (git)", "wait_message": "Please wait..." } diff --git a/resources/lang/es.json b/resources/lang/es.json index 9105f7cb8..3fc48117e 100644 --- a/resources/lang/es.json +++ b/resources/lang/es.json @@ -99,7 +99,6 @@ "general:picture_preview_before_processing": "Precargar y mostrar la imagen durante el procesamiento del filtro", "general:picture_thumb_size": "Tamaño de la vista en miniatura", "general:picture_time_to_live": "Mostrar imagen después de la captura", - "general:qr_enabled": "Utilizar los códigos QR", "general:start_screen_subtitle": "Página de inicio (Subtítulo)", "general:start_screen_title": "Página de inicio (Título)", "general:ui_language": "Elija el idioma", @@ -187,19 +186,15 @@ "manual:gallery:pswp_zoomEl": "Si está activado, el botón Photo Swipe a pantalla completa es visible dentro de la galería.", "manual:general:adminpanel_view": "Permite configurar diferentes puntos de vista en el panel de administración con más (Vista Experta) o menos (Vista Básica) opciones de accesibilidad", "manual:general:database_file": "El nombre del archivo de base de datos.", - "manual:general:dev_enabled": "Habilita el modo de desarrollo. En su lugar, se utilizarán imágenes de muestra para tomar una fotografía.", "manual:general:dev_error_messages": "Si se habilitan los mensajes de error reales se muestran en el error.", "manual:general:dev_reload_on_error": "Si ocurre un error al tomar una foto, el mostrador de fotos se recargará automáticamente después de 5 segundos.", "manual:general:download_enabled": "Si está activado, un botón de descarga es visible en cada imagen dentro de la galería.", "manual:general:download_thumbs": "Si está activado, las miniaturas se utilizarán al descargar (si existe) en su lugar la imagen de tamaño completo.", "manual:general:picture_preview_before_processing": "Si está habilitada, las imágenes se precargan y se muestran durante el procesamiento del filtro.", "manual:general:picture_thumb_size": "Elija el tamaño de la miniatura: XS = max 360px, S = max 540px, M = max 900px, L = max 1080px, XL = max 1260px", - "manual:general:picture_time_to_live": "Introduzca un valor utilizado como milisegundos. Este valor define el tiempo que la imagen es visible en la pantalla de resultados después de tomar una foto.", - "manual:general:qr_enabled": "Si está activado, un botón QR es visible en la pantalla de resultados y dentro de la galería. El usuario puede descargar una imagen mientras escanea el código QR. Si está accediendo a Photobooth a través de \"localhost\", \"127.0.0.1\" o si tiene Photobooth instalado dentro de una subcarpeta, defina la dirección IP del servidor web Photobooth para que el código QR funcione.

    Por ejemplo, si Photobooth se puede acceder directamente: 192.168.0.50.

    Por ejemplo, si Photobooth está instalado dentro de una subcarpeta: 192.168.0.50/photobooth.

    ", "manual:general:start_screen_subtitle": "Introduzca el subtítulo visible en la página de inicio.", "manual:general:start_screen_title": "Introduzca el título visible en la página de inicio.", "manual:general:ui_language": "Elegir el idioma de la interfaz.

    Si te falta un idioma o gusta ayudar a mejorar las traducciones visita \"Cómo actualizar o añadir traducciones?\" dentro de la wiki de Photobooth por instrucciones.

    ", - "manual:general:webserver_ip": "Defina la dirección IP del servidor web Photobooth para que el código QR funcione si está accediendo a Photobooth a través de \"localhost\", \"127.0.0.1\" o si tiene Photobooth instalado dentro de una subcarpeta.

    Por ejemplo, si Photobooth se puede acceder directamente: 192.168.0.50.

    Por ejemplo, si Photobooth está instalado dentro de una subcarpeta: 192.168.0.50/photobooth.

    ", "manual:general:webserver_ssid": "Defina el nombre de la red inalámbrica (SSID) que se utilizará para acceder Photobooth. El nombre de la red inalámbrica (SSID) se muestra en la página de resultados cuando se llama al código QR.", "manual:jpeg_quality:jpeg_quality_chroma": "Calidad de imagen utilizada para el cromakeying de imágenes.", "manual:jpeg_quality:jpeg_quality_image": "Calidad de imagen utilizada para tomar fotografías.", @@ -226,7 +221,6 @@ "manual:pictures:picture_frame": "Introduzca la ruta del marco que se aplica a su imagen después de tomarlo.", "manual:pictures:picture_keep_original": "Si está habilitada, las imágenes originales se conservarán dentro de la carpeta tmp.", "manual:pictures:picture_key": "Especificar el ID de clave a usar esa llave a tomar un foto (e.g. 13 es la tecla de entrada). Por ejemplo usa https://keycode.info descubrir el clave de ID.", - "manual:pictures:picture_naming": "Elija entre imágenes con formato de fecha, numeradas o con nombre aleatorio. Para imágenes con formato de fecha, puede mostrar la imagen con la fecha y hora dentro de la galería.", "manual:pictures:picture_no_cheese": "Si está activado, las fotos se tomarán justo después de la cuenta atrás. Esto omite el mensaje \"¡Cheeeeeeeeeeeeeseeeeeeeeeese!\".", "manual:pictures:picture_permissions": "Cambie los permisos de fotos tomadas con el comando \"chmod\". Por favor, cambie este valor si está familiarizado con los permisos de archivo en Linux/Unix.", "manual:pictures:picture_polaroid_effect": "Si está activado, se aplica un efecto polaroid a la imagen después de que se haya tomado.", @@ -343,7 +337,6 @@ "printing": "¡Impresión! Por favor espera...", "qr": "Código QR", "qrHelp": "Para descargar la imagen en su teléfono inteligente, conéctese al WiFi:", - "really_delete_image": "¡será eliminado! Esto no se puede deshacer! ¿Realmente quier borrar la imagen?", "reload": "Descargar de nuevo la página.", "remotebuzzer:remotebuzzer_collagetime": "Botón de segundos pulsado para disparar collage", "remotebuzzer:remotebuzzer_logfile": "Archivo log", diff --git a/resources/lang/fr.json b/resources/lang/fr.json index 22feb298d..338ca1c0a 100644 --- a/resources/lang/fr.json +++ b/resources/lang/fr.json @@ -1,32 +1,83 @@ { "abort": "Avorter", "admin_panel": "Panneau admin", + "adminpanel_back": "Retour", + "adminpanel_toggletextOFF": "Désactivé", + "adminpanel_toggletextON": "Activé", + "authentication": "Identifiant", + "authentication:login_enabled": "Authentification activée", + "authentication:login_password": "Mot de passe", + "authentication:login_username": "Nom d'utilisateur", + "authentication:protect_admin": "Protéger le panneau d'administration", + "authentication:protect_index": "Protéger l'écran de démarrage", + "authentication:protect_index_redirect": "Destination depuis l'écran d'accueil lorsque vous n'êtes pas connecté", + "authentication:protect_localhost_admin": "Protéger le panneau d'administration avec l'accès localhost", + "authentication:protect_localhost_index": "Protéger l'écran de démarrage lors de l'accès localhost", + "authentication:protect_localhost_manual": "Protéger le manuel et la FAQ lors de l'accès localhost", + "authentication:protect_localhost_update": "Protéger la mise à jour lors de l'accès localhost", "auto_reload": "Actualisation automatique du Photobooth...", + "available_version": "Disponible :", "busy": "Traitement en cours...", "busyCollage": "Traitement du collage en cours...", + "check_connection": "Vérification de la connexion...", + "check_dependencies": "Vérification des dépendances installées...", + "check_version": "Vérifier en ligne", "cheese": "Cheeeeeeeese!", "cheeseCollage": "Cheeeeeeeese 4 fois !", + "chromaInfoAfter": "Veuillez actualiser la page pour prendre une nouvelle photo", + "chromaInfoBefore": "Veuillez choisir un arrière-plan pour prendre une photo", "close": "Fermer", + "collage": "Multi-photos", + "collage:collage_background_color": "Couleur d'arrière-plan pour les Multi-photos", "collage:collage_cntdwn_time": "Montage photo compte à rebours en secondes", "collage:collage_continuous": "Prendre un photo montage sans interruption", + "collage:collage_continuous_time": "Afficher chaque photo lors de prises de vues multiples:", "collage:collage_enabled": "Autorisez la fonction du montage photo", + "collage:collage_frame": "Cadre", + "collage:collage_keep_single_images": "Ajouter les images individuellement du Multi-photos à la galerie", "collage:collage_key": "Code clé qui déclenche une collage", "collage:collage_layout": "Choisissez la mise en page du collage:", + "collage:collage_only": "Autoriser uniquement la prise de vue de Multi-photos", + "collage:collage_take_frame": "Multi-photos avec cadre", + "collage:textoncollage_enabled": "Multi-photos avec Texte", + "collage:textoncollage_font": "Police de caractère", + "collage:textoncollage_font_color": "Couleur de police de caractère", + "collage:textoncollage_font_size": "Taille de la police", + "collage:textoncollage_line1": "Ligne de texte 1", + "collage:textoncollage_line2": "Ligne de texte 2", + "collage:textoncollage_line3": "Ligne de texte 3", + "collage:textoncollage_linespace": "Interligne", + "collage:textoncollage_locationx": "Coordonnée X", + "collage:textoncollage_locationy": "Coordonnée Y", + "collage:textoncollage_rotation": "Rotation du texte", "commands": "Les commandes", "commands:exiftool_cmd": "Commande EXIFtool", "commands:exiftool_msg": "Préservation EXIF réussie avec succès", + "commands:nodebin_cmd": "Chemin de l'exécutable de Node.js", + "commands:post_photo_cmd": "Script / commande post-photo", + "commands:pre_photo_cmd": "Script / commande pré-photo", + "commands:preview_cmd": "Commande permettant de générer un aperçu en direct", + "commands:preview_killcmd": "Commande pour quitter l'aperçu en direct", "commands:print_cmd": "Commande d'impression", "commands:print_msg": "Message de réussite pour impression", + "commands:reboot_cmd": "Commande de redémarrage", + "commands:shutdown_cmd": "Commande d'arrêt de l'ordinateur", "commands:take_picture_cmd": "Prendre une photo", "commands:take_picture_msg": "Message de réussite pour prendre une photo", + "current_version": "Version installée:", + "database_rebuild": "Reconstruire la base de données", + "debugpanel:autorefresh": "Actualisation automatique", "degrees": "°", "delete": "Effacer", + "dependencies_check": "Dépendances installées", "disk_usage": "Utilisation de la mémoire", "dot": ".", + "download_zip": "Télécharger le dossier data zippé", "error": "Il y a eu un problème. Merci de réessayer.", "event": "Événement", "filecount": "Nombre de fichiers:", "folders": "Dossiers", + "folders:folders_archives": "Dossier archive", "folders:folders_data": "Dossier data", "folders:folders_images": "Dossier d'images", "folders:folders_keying": "Dossier font vert", @@ -35,12 +86,14 @@ "folders:folders_thumbs": "Dossier de vignettes", "folders:folders_tmp": "Dossier tmp", "foldersize": "Taille du dossier:", + "frontpage": "Page d'accueil", "frontpage:button_force_buzzer": "masquer le déclencheur", "frontpage:event_enabled": "Événement", "frontpage:event_symbol": "Sélectionnez le symbole", "frontpage:event_textLeft": "Texte à gauche", "frontpage:event_textRight": "Texte droit", "frontpage:ui_show_fork": "Affichez le badge de la page Github", + "frontpage:ui_skip_welcome": "Passer l'écran d'accueil", "gallery": "Galerie", "gallery:gallery_allow_delete": "Autoriser la suppression d'images de la galerie", "gallery:gallery_bottom_bar": "Afficher la barre des touches en bas dans la gallerie", @@ -52,7 +105,15 @@ "gallery:gallery_pictureTime": "Durée en milisecondes durant lesquelles une image est affichée pendant le diaporama", "gallery:gallery_scrollbar": "Affichez la barre de défilement dans la galerie", "gallery:gallery_show_date": "Montrez la date sous les images (fonctionne uniquement avec images au format de date)", + "gallery:gallery_use_slideshow": "Autoriser le diaporama dans la galerie", + "gallery:pswp_animateTransitions": "Changement d'images avec un effet de transition de diapositive", "gallery:pswp_bgOpacity": "Opacité de fond", + "gallery:pswp_clickToCloseNonZoomable": "Cliquez sur l’image pour fermer la galerie", + "gallery:pswp_closeOnOutsideClick": "Fermer la galerie lors d’un clique hors image", + "gallery:pswp_closeOnScroll": "Fermer l'image lors du défilement", + "gallery:pswp_closeOnVerticalDrag": "Fermer la galerie en faisant glisser verticalement et lorsque l'image n'est pas zoomée", + "gallery:pswp_counterEl": "Afficher le nombre d’images", + "gallery:pswp_fullscreenEl": "Afficher le bouton plein écran de PhotoSwipe", "gallery_no_image": "La galerie est vide. Faites des photos!", "general": "Général", "general:database_file": "Nom du fichier de la base de donnée", @@ -61,7 +122,6 @@ "general:dev_reload_on_error": "Actualiser automatiquement le Photobooth en cas d'erreur (F5)", "general:download_enabled": "Autoriser les téléchargements", "general:download_thumbs": "Utiliser des miniatures pour le téléchargement", - "general:qr_enabled": "Utiliser le QR code", "general:start_screen_subtitle": "Page d’accueil (Sous-titre)", "general:start_screen_title": "Page d’accueil (Titre)", "general:ui_language": "Choisissez la langue", @@ -86,6 +146,7 @@ "mail:mail_username": "Nom d'utilisateur du compte de messagerie", "mailError": "Erreur d'envoi d'email", "mailSent": "E-mail envoyé", + "manual:gallery:gallery_scrollbar": "Si activé, une barre de défilement est visible dans la galerie.", "newCollage": "Une autre photo montage", "newPhoto": "Une autre", "nextPhoto": "Photo suivante", diff --git a/resources/lang/hu.json b/resources/lang/hu.json new file mode 100644 index 000000000..9d73b3da8 --- /dev/null +++ b/resources/lang/hu.json @@ -0,0 +1,43 @@ +{ + "abort": "Megszakítás", + "admin_panel": "Adminisztrációs felület", + "cheese": "Csííííííííz!", + "cheeseCollage": "Csííííííííz Kolláááááááázs!", + "close": "Bezárás", + "commands": "Parancsok", + "delete": "Törlés", + "error": "Hiba történt. Kérlek, próbáld újra!", + "event": "Esemény", + "folders": "Mappák", + "gallery": "Galéria", + "gallery_no_image": "A galéria még üres. Készíts egy fényképet!", + "general": "Általános", + "home": "Kezdőlap", + "insertMail": "Add meg az e-mail címedet, amire szeretnéd kérni a képet.", + "jpeg_quality": "JPEG minőség", + "keyingerror": "Greenbox mód nem lehetséges!", + "login_invalid": "A felhasználói név vagy jelszó helytelen!", + "logout": "Kijelentkezés", + "mailError": "Hiba az e-mail küldése során", + "mailSaved": "E-mail cím sikeresen elmentve", + "mailSent": "E-mail elküldve", + "newCollage": "Új kollázs", + "newPhoto": "Új fénykép", + "nextPhoto": "Következő fénykép", + "print": "Nyomtatás", + "printing": "Nyomtatás folyamatban! Kérlek, várj...", + "qr": "QR-Kód", + "qrHelp": "Csatlakozz a Fotófülke Wi-Fi hálózatához, hogy le tudd tölteni a képet a telefonodra. (SSID: Photobooth)", + "reload": "Oldal frissítése", + "save": "Mentés", + "selectFilter": "Képszűrő", + "send": "Küldés", + "sendAllMail": "Link kérése e-mailben, amivel a következő napokban készített összes kép elérhető.", + "startScreen": "

    Fotófülke

    Webinterfész

    készítette: André Rinas", + "takeCollage": "Készíts egy kollázst!", + "takePhoto": "Készíts egy fényképet!", + "test_update_available": "Teszt frissítés elérhető", + "update_available": "Frissítés elérhető.", + "use_button": "Exponáló gomb használata fényképkészítéshez", + "using_latest_version": "A fotófülke legfrissebb verzióját használod." +} diff --git a/resources/lang/it.json b/resources/lang/it.json index 3e392df84..52503ddfe 100644 --- a/resources/lang/it.json +++ b/resources/lang/it.json @@ -10,39 +10,71 @@ "authentication:login_username": "Nome utente", "authentication:protect_admin": "Proteggi pannello amministratore", "authentication:protect_index": "Proteggi lo schermo iniziale", + "authentication:protect_index_redirect": "Destinazione dalla schermata iniziale quando non si è loggati", "authentication:protect_localhost_admin": "Proteggi il pannello di amministrazione sull'accesso localhost", "authentication:protect_localhost_index": "Proteggi la schermata iniziale sull'accesso localhost", + "authentication:protect_localhost_manual": "Proteggi il manuale e le FAQ sull'accesso da localhost", + "authentication:protect_localhost_update": "Proteggi l'updater dall'accesso da localhost", + "authentication:protect_manual": "Proteggi il manuale & le FAQ", + "authentication:protect_update": "Proteggi l'updater", "auto_reload": "Il Photobooth si ricarica automaticamente...", + "available_version": "Disponibile:", + "bootconfig": "config.txt", "busy": "Sto processando ...", "busyCollage": "Sto processando il collage ...", + "check_connection": "Test connessione in corso...", + "check_dependencies": "Controllo delle dipendenze installate...", + "check_version": "Controlla online", "cheese": "Cheeeeeeeese!", "cheeseCollage": "Cheeeeeeeese Collageeeeeeeeeee!", "chromaInfoAfter": "Per favore ricarica la pagina per scattare una nuova foto", "chromaInfoBefore": "Per favore scegli uno sfondo per scattare un'immagine", "close": "Chiudi", "collage": "Collage", + "collage:collage_background_color": "Colore di sfondo collage", "collage:collage_cntdwn_time": "Conto alla rovescia del collage:", "collage:collage_continuous": "Esegui un collage senza interruzioni", + "collage:collage_continuous_time": "Mostra singole immagini sul collage continuo per:", "collage:collage_enabled": "Permetti il collage di foto", "collage:collage_frame": "Cornice", + "collage:collage_keep_single_images": "Aggiungi le singole immagini del collage alla galleria", "collage:collage_key": "Pulsante della tastiera che innesca un collage", "collage:collage_layout": "Scegli layout del collage:", "collage:collage_only": "Consenti solo collage fotografico", "collage:collage_take_frame": "Scatta il collage con la cornice", + "collage:textoncollage_enabled": "Testo sul collage", + "collage:textoncollage_font": "Carattere", + "collage:textoncollage_font_color": "Colore del carattere", + "collage:textoncollage_font_size": "Dimensione del carattere", + "collage:textoncollage_line1": "Testo linea 1", + "collage:textoncollage_line2": "Testo linea 2", + "collage:textoncollage_line3": "Testo linea 3", + "collage:textoncollage_linespace": "Spaziatura linea", + "collage:textoncollage_locationx": "Coordinata X", + "collage:textoncollage_locationy": "Coordinata Y", + "collage:textoncollage_rotation": "Rotazione testo", "commands": "Comandi", "commands:exiftool_cmd": "Comandi EXIF", "commands:exiftool_msg": "Messaggio di successo per la conservazione EXIF", "commands:nodebin_cmd": "Percorso Eseguibile Node.js", + "commands:post_photo_cmd": "Script / comando post-foto", + "commands:pre_photo_cmd": "Script / comando pre-foto", "commands:preview_cmd": "Comando per generare un'anteprima live", "commands:preview_killcmd": "Comando per terminare l'anteprima live", "commands:print_cmd": "Comando di stampa", "commands:print_msg": "Messaggio di successo per la stampa", - "commands:shutdown_cmd": "Comando di spegnimento Buzzer remoto", + "commands:reboot_cmd": "Comando di riavvio", + "commands:shutdown_cmd": "Comando di spegnimento", "commands:take_picture_cmd": "Comando per scattare la foto", "commands:take_picture_msg": "Messaggio di successo per aver scattato la foto", + "current_version": "Installata:", "database_rebuild": "Ricostruisci", + "debugpanel": "Pannello Di Debug", + "debugpanel:autorefresh": "Aggiornamento automatico", "degrees": "°", "delete": "Elimina", + "dependencies_check": "Dipendenze installate", + "dev_debugpanel": "Pannello di debug", "disk_usage": "Utilizzo del disco", "dot": ".", "download_zip": "Scarica la cartella dati come zip", @@ -66,6 +98,7 @@ "frontpage:event_textLeft": "Testo a sinistra", "frontpage:event_textRight": "Testo a destra", "frontpage:ui_show_fork": "Mostra Distintivo Fork", + "frontpage:ui_skip_welcome": "Salta la schermata di benvenuto", "gallery": "Galleria", "gallery:gallery_allow_delete": "Permetti l'eliminazione delle immagini dalla galleria", "gallery:gallery_bottom_bar": "Mostra barra dei pulsanti nella galleria in basso", @@ -98,6 +131,10 @@ "general:database_enabled": "Usa database per le immagini", "general:database_file": "Nome del file del Database", "general:database_rebuild": "Ricostruisci database immagini", + "general:delete_no_request": "Elimina immagini senza richiesta di conferma", + "general:dependencies_button": "Controlla le dipendenze installate", + "general:dev_debugpanel": "Pannello Di Debug", + "general:dev_demo_images": "Utilizza Immagini Demo", "general:dev_enabled": "Modalità sviluppatore", "general:dev_error_messages": "Mostra messaggi di errore", "general:dev_reload_on_error": "Ricarica il Photobooth automaticamente dopo un errore", @@ -107,7 +144,6 @@ "general:picture_preview_before_processing": "Precarica e mostra l'immagine durante l'elaborazione del filtro", "general:picture_thumb_size": "Dimensioni delle miniature", "general:picture_time_to_live": "Mostra immagine dopo la cattura:", - "general:qr_enabled": "Usa QR Codes", "general:start_screen_subtitle": "Schermata iniziale (sottotitolo)", "general:start_screen_subtitle_visible": "Mostra sottotitoli nella schermata iniziale", "general:start_screen_title": "Schermata iniziale (titolo)", @@ -115,6 +151,13 @@ "general:ui_language": "Scegli la Lingua", "general:webserver_ip": "Indirizzo IP del server web Photobooth", "general:webserver_ssid": "Nome di rete wireless (SSID) utilizzato per accedere al photobooth", + "get_request": "Richiesta GET", + "get_request:get_request_collage": "Richiesta GET per il collage al conto alla rovescia", + "get_request:get_request_countdown": "Invia richiesta GET al server al conto alla rovescia", + "get_request:get_request_picture": "Richiesta GET per foto al conto alla rovescia", + "get_request:get_request_processed": "Invia la richiesta \"Photostyle\" GET al server dopo l'elaborazione", + "get_request:get_request_server": "GET request server", + "githead": "Ultime modifiche", "home": "Casa", "insertMail": "Inserisci il tuo indirizzo e-mail per ricevere la foto.", "jpeg_quality": "Qualità JPEG", @@ -124,21 +167,25 @@ "keying": "Chroma keying", "keying:keying_background_path": "Percorso immagini di sfondo", "keying:keying_enabled": "Permetti il chroma keying", + "keying:keying_seriouslyjs_color": "Colore chiave per Seriously.js", "keying:keying_size": "Dimensione del cromakeying", "keying:keying_variant": "Algoritmo del chroma keying", "keying:live_keying_enabled": "Usa il live keying come pagina iniziale", "keying:live_keying_show_all": "Mostra immagine originale e modificate all'interno della galleria mentre usi il live keying", "keyingerror": "Chroma keying non possibile!", + "livechroma": "Live keying", "login_invalid": "Nome utente o password non sono valide!", "login_password": "Password", "login_username": "Nome utente", "logout": "Esci", "mail": "E-mail", + "mail:mail_alt_text": "Testo alternativo", "mail:mail_enabled": "Usa E-Mail", "mail:mail_file": "Nome del database degli indirizzi e-mail", "mail:mail_fromAddress": "Indirizzo di ritorno", "mail:mail_fromName": "Nome del mittente", "mail:mail_host": "Indirizzo email dell'host", + "mail:mail_is_html": "E-mail HTML", "mail:mail_password": "Password", "mail:mail_port": "Porta", "mail:mail_secure": "Sicurezza (tls o ssl)", @@ -154,23 +201,45 @@ "manual:authentication:login_username": "Definisci il nome utente usato per accedere a Photobooth.", "manual:authentication:protect_admin": "Se abilitata, è possibile accedere al pannello di amministrazione solo se viene inserito un nome utente e una password.", "manual:authentication:protect_index": "Se abilitata, è possibile accedere alla schermata iniziale solo se viene inserito un nome utente e una password.", + "manual:authentication:protect_index_redirect": "Imposta la pagina a cui l'utente viene reindirizzato, se \"proteggi indice\" è abilitato e l'utente non è connesso.", "manual:authentication:protect_localhost_admin": "Se disabilitato, è possibile accedere al pannello di amministrazione senza nome utente e password da localhost.", "manual:authentication:protect_localhost_index": "Se disabilitata, è possibile accedere alla schermata iniziale senza nome utente e password da localhost.", + "manual:authentication:protect_localhost_manual": "Se disabilitato, è possibile accedere al manuale e alle FAQ senza nome utente e password da localhost.", + "manual:authentication:protect_localhost_update": "Se disabilitato, l'updater può essere utilizzato senza nome utente e password da localhost.", + "manual:authentication:protect_manual": "Se abilitato, è possibile accedere al manuale e alle FAQ solo se viene inserito un nome utente e una password.", + "manual:authentication:protect_update": "Se abilitata, è possibile accedere all'updater solo se viene inserito un nome utente e una password.", "manual:collage:collage_cntdwn_time": "Imposta il conto alla rovescia tra le foto mentre fai un collage.", "manual:collage:collage_continuous": "Esegui un collage senza interruzioni.", + "manual:collage:collage_continuous_time": "Controlla il tempo in cui una foto è visibile sul collage continuo prima di scattare la foto successiva.", "manual:collage:collage_enabled": "Se abilitato, l'utente può scattare un collage. Un collage è composto da 4 immagini. Facoltativo puoi scattare un collage con o senza interruzioni.", "manual:collage:collage_frame": "Inserisci il percorso del frame che viene applicato al collage dopo lo scatto.", + "manual:collage:collage_keep_single_images": "Se abilitata, le singole immagini dal collage saranno aggiunte alla galleria. Si prega di notare che le singole immagini non sono visibili se si accede alla galleria nella schermata dei risultati!", "manual:collage:collage_key": "Specifica quale pulsante della tastiera usare per scattare un collage (ad es. 13 è un pulsante). Per esempio usa https://keycode.info per scoprire l'id del pulsante.", "manual:collage:collage_layout": "Scegli tra il layout del collage di immagini 2x2 e 2x4.", "manual:collage:collage_only": "Se abilitato, può essere utilizzato solo il collage.", "manual:collage:collage_take_frame": "Se abilitata, il frame scelto verrà applicato al collage dopo lo scatto.", + "manual:collage:textoncollage_enabled": "Se abilitato, puoi aggiungere del testo al tuo collage.", + "manual:collage:textoncollage_font": "Inserisci il percorso del carattere usato per applicare il testo sul tuo collage.", + "manual:collage:textoncollage_font_color": "Scegli il colore del carattere da applicare al tuo collage.", + "manual:collage:textoncollage_font_size": "Scegli la dimensione del carattere da applicare al testo sul collage.", + "manual:collage:textoncollage_line1": "Testo usato per la prima linea che viene aggiunta al collage.", + "manual:collage:textoncollage_line2": "Testo usato per la seconda linea che viene aggiunta al collage.", + "manual:collage:textoncollage_line3": "Testo usato per la terza linea che viene aggiunta al collage.", + "manual:collage:textoncollage_linespace": "Scegli la spaziatura fra il testo da applicare al collage.", + "manual:collage:textoncollage_locationx": "Coordinate X del testo durante l'aggiunta di testo al collage.", + "manual:collage:textoncollage_locationy": "Coordinate Y del testo durante l'aggiunta di testo al collage.", + "manual:collage:textoncollage_rotation": "Inserisci un valore che viene usato come rotazione in gradi del testo sul collage.", "manual:commands:exiftool_cmd": "Comando EXIFtool che viene eseguito dopo aver scattato una foto se \"Preserva i dati EXIF\" è abilitata.", "manual:commands:exiftool_msg": "Messaggio restituito dal comando EXIFtool.", "manual:commands:nodebin_cmd": "Percorso dell'eseguibile per node.js - esempio /usr/bin/node", + "manual:commands:post_photo_cmd": "Immettere un comando che viene eseguito dopo che l'immagine è stata scattata e l'elaborazione è completata. L'esecuzione è asincrona/non bloccante. Per il collage questo viene eseguito solo una volta dopo che l'intero collage è stato completato", + "manual:commands:pre_photo_cmd": "Immettere un comando che viene eseguito direttamente ogni volta che viene scattata una foto singola/collage. L'esecuzione è asincrona/non bloccante. Per il collage questo viene eseguito per ogni singola foto nel collage", "manual:commands:preview_cmd": "Riga di comando che viene eseguita per generare un'anteprima live.", "manual:commands:preview_killcmd": "Riga di comando che viene eseguita per terminare l'anteprima dal vivo.", "manual:commands:print_cmd": "Comando eseguito premendo il pulsante \"Stampa\".", "manual:commands:print_msg": "Messaggio restituito dal comando di stampa.", + "manual:commands:reboot_cmd": "Comando utilizzato per avviare il riavvio tramite pulsante - esempio /sbin/shutdown -r now.", + "manual:commands:shutdown_cmd": "Comando utilizzato per avviare lo spegnimento tramite pulsante - esempio /sbin/shutdown -h now.", "manual:commands:take_picture_cmd": "Comando eseguito premendo il pulsante \"Scatta foto\". Su Linux puoi ad esempio usare gphoto2 per scattare foto, su Windows puoi usare digiCamControl.", "manual:commands:take_picture_msg": "Messaggio restituito dal comando scatta foto.", "manual:folders:folders_archives": "Inserisci il nome della cartella archivi.", @@ -187,6 +256,7 @@ "manual:frontpage:event_textLeft": "Inserire il testo visibile sul lato sinistro del simbolo scelto.", "manual:frontpage:event_textRight": "Inserire il testo visibile sul lato destro del simbolo scelto.", "manual:frontpage:ui_show_fork": "Se abilitata, viene visualizzato il pulsante per il fork nell'angolo superiore destro della schermata di avvio.", + "manual:frontpage:ui_skip_welcome": "Se abilitata, la schermata di benvenuto non sarà visibile durante l'accesso a Photobooth la prima volta dopo l'installazione/aggiornamento.", "manual:gallery:gallery_allow_delete": "Se abilitata, le immagini possono essere cancellate dalla galleria in qualsiasi momento.", "manual:gallery:gallery_bottom_bar": "Se abilitata, la barra dei pulsanti viene mostrata in basso nella galleria.", "manual:gallery:gallery_date_format": "Inserisci lo stile della tua data.", @@ -215,21 +285,17 @@ "manual:general:database_enabled": "Se abilitata, le immagini saranno aggiunte a un database utilizzato dalla galleria. Se disabilitata, la galleria leggerà la cartella delle immagini - questo potrebbe ridurre le prestazioni.", "manual:general:database_file": "Nome del file del database.", "manual:general:database_rebuild": "Ricostruire il database di immagini. Normalmente non è necessario, ma può essere utilizzato per correggere un file di database danneggiato.", - "manual:general:dev_enabled": "Abilita la modalità di sviluppo. Saranno usate le immagini di esempio al posto di quelle scattate.", "manual:general:dev_error_messages": "Se abilitato i messaggi di errore reali vengono mostrati in caso di errore.", "manual:general:dev_reload_on_error": "Se si verifica un errore mentre si scatta una foto, Photobooth ricaricherà automaticamente dopo 5 secondi.", "manual:general:download_enabled": "Se abilitato, un pulsante di download è visibile su ogni immagine all'interno della galleria.", "manual:general:download_thumbs": "Se abilitata, le miniature saranno usate per il download (se presenti) invece dell'immagine a dimensione completa.", "manual:general:picture_preview_before_processing": "Se abilitata, le immagini sono precaricate e mostrate durante l'elaborazione dei filtri.", "manual:general:picture_thumb_size": "Scegli dimensione miniatura: XS = max 360px, S = max 540px, M = max 900px, L = max 1080px, XL = max 1260px", - "manual:general:picture_time_to_live": "Inserisci un valore usato come millisecondi. Questo valore definisce il tempo in cui l'immagine è visibile sulla schermata del risultato dopo aver scattato una foto.", - "manual:general:qr_enabled": "Se abilitato, un pulsante QR-code sarà visibile nella schermta del risultato e nella galleria. Un utente può scaricare la sua foto scannerizzando il QR-code. Se si accede a Photobooth tramite \"localhost\", \"127.0.0.1\" o se Photobooth è installato all'interno di una sottocartella, si prega allora di definire un indirizzo IP per permette al QR-code di funzionare.

    Esempio se Photobooth è accessibile direttamente: 192.168.0.50.

    Esempio se Photobooth è installato all'interno di una sottocartella: 192.168.0.50/photobooth.

    ", "manual:general:start_screen_subtitle": "Inserisci il sottotitolo visibile nella pagina iniziale.", "manual:general:start_screen_subtitle_visible": "Se abilitata, i sottotitoli inseriti sono visibili nella schermata iniziale.", "manual:general:start_screen_title": "Inserisci il titolo visibile nella pagina iniziale.", "manual:general:start_screen_title_visible": "Se abilitata, il titolo inserito è visibile nella schermata iniziale.", "manual:general:ui_language": "Scegli la lingua dell'interfaccia.

    Se ti manca una lingua o vuoi aiutare a migliorare le traduzioni visita il \"Come aggiornare o aggiungere le traduzioni? all'interno della Wiki Photobooth sezione istruzioni.

    ", - "manual:general:webserver_ip": "Si prega di definire l'indirizzo IP del server web di Photobooth per far funzionare il codice QR se si accede a Photobooth tramite \"localhost\", \"127.0.0.1\" o se Photobooth è installato all'interno di una sottocartella.

    Esempio se Photobooth è accessibile direttamente: 192.168.0.50.

    Esempio se Photobooth è installato all'interno di una sottocartella: 192.168.0.50/photobooth.

    ", "manual:general:webserver_ssid": "Si prega di definire il nome di rete wireless (SSID) da utilizzare per accedere al Photobooth. Il nome della rete wireless (SSID) viene visualizzato nella pagina dei risultati quando viene richiamato il codice QR.", "manual:jpeg_quality:jpeg_quality_chroma": "Qualità dell'immagine utilizzata per le immagini di chroma keying.", "manual:jpeg_quality:jpeg_quality_image": "Qualità dell'immagine usata per scattare foto.", @@ -261,7 +327,6 @@ "manual:pictures:picture_frame": "Inserisci il percorso del frame che viene applicato alla foto dopo lo scatto.", "manual:pictures:picture_keep_original": "Se abilitata, le immagini originali saranno mantenute all'interno della cartella tmp.", "manual:pictures:picture_key": "Specifica quale pulsante della tastiera usare per scattare una foto (ad es. 13 è un pulsante). Per esempio usa https://keycode.info per scoprire l'id del pulsante.", - "manual:pictures:picture_naming": "Scegli come salvare le immagini fra formattate per data, numerate o nominate casualmente. Per le immagini formattate per la data, è possibile visualizzare l'immagine con la data e l'ora all'interno della galleria.", "manual:pictures:picture_no_cheese": "Se abilitata, le immagini saranno scattate subito dopo il conto alla rovescia. Questo salta il messaggio \"Cheeeeeeeeese!\".", "manual:pictures:picture_permissions": "Cambia i permessi delle foto scattate con il comando \"chmod\". Attento, cambia questo valore solo se hai familiarità con i permessi dei file in Linux/Unix.", "manual:pictures:picture_polaroid_effect": "Se abilitata, un effetto polaroid viene applicato alla tua immagine dopo che è stata scattata.", @@ -304,14 +369,12 @@ "manual:print:textonprint_locationy": "Coordinate Y del testo durante la stampa del testo sulla foto.", "manual:remotebuzzer:remotebuzzer_collagebutton": "Per il COLLAGE collegare il pulsante fisico al GPIO20. Collegare, poi, la messa a terra per attivare. Se abilitato, premere a lungo sul pulsante PICTURE non attiverà il collage.", "manual:remotebuzzer:remotebuzzer_collagetime": "Se viene premuto il pulsante fisico (collegato al GPIO) per meno di un secondo, viene scattata un'immagine. Se il pulsante viene premuto per più secondi viene scattato un collage. Funziona solo se il collage è abilitato nelle impostazioni di amministrazione e il pulsante del collage è disabilitato.", - "manual:remotebuzzer:remotebuzzer_enabled": "Questa funzione abilita il supporto del pulsante hardware attraverso i pin Raspberry GPIO. IMPORTANTE: Per gli schermi connessi WLAN è necessario assicurarsi di configurare l'indirizzo IP del server web Photobooth nella sezione \"Generale\", in modo da funzionare correttamente.", "manual:remotebuzzer:remotebuzzer_logfile": "Se è attiva la modalità sviluppatore, le informazioni di debug del server saranno scritte nel file di log, si trova nella cartella tmp e si chiama di default io_server.log.", "manual:remotebuzzer:remotebuzzer_picturebutton": "Per scattare collegare il pulsante hardware a GPIO21. Mettere la messa a terra per attivare. Premere a lungo attiverà, il COLLAGE, se abilitato. Il pulsante hardware del Collage deve essere disabilitato.", "manual:remotebuzzer:remotebuzzer_port": "Porta del Server TCP - esempio 14711.", "manual:remotebuzzer:remotebuzzer_printbutton": "Per Stampare collegare il pulsante hardware a GPIO26. Mettere la messa a terra del GPIO per attivare.", "manual:remotebuzzer:remotebuzzer_shutdownbutton": "Per spegnere collegare il pulsante hardware a GPIO16. Mettere la messa a terra del GPIO per attivaare. Tieni premuto 5 sec (predefinito) per spegnere.", "manual:remotebuzzer:remotebuzzer_shutdownholdtime": "Numero di secondi in cui tenere premuto il pulsante affinchè il processo dispegnimento sarà avviato. Impostare a Zero (0) significa arresto immediato senza tempo di attesa.", - "manual:remotebuzzer:remotebuzzer_userotary": "Passa alla modalità codificatore rotativo per navigare sullo schermo. Richiede un interruttore codificatore rotativo collegato al GPIO - vedi FAQ per dettagli", "manual:reset:reset_button": "Eseguirà il ripristino della configurazione. Se vuoi anche resettare le immagini e / o il database degli indirizzi di posta, assicurati di attivare prima queste impostazioni e salvare (!) la configurazione, quindi eseguire il ripristino della configurazione stessa.", "manual:reset:reset_remove_config": "Se abilitata, la configurazione personale viene rimossa al reset.", "manual:reset:reset_remove_images": "Se abilitata, tutte le immagini vengono rimosse al reset.", @@ -396,19 +459,16 @@ "qr": "QR Code", "qrHelp": "Per scaricare l'immagine sul tuo smartphone, connettiti al WiFi:", "really_delete": "Vuoi davvero resettare le tue impostazioni? Questo non può essere annullato!", - "really_delete_image": "sarà eliminato! Questo non può essere annullato! Vuoi davvero cancellare la foto?", "reload": "Ricarica la pagina", "remotebuzzer": "Pulsante Fisico", "remotebuzzer:remotebuzzer_collagebutton": "Pulsante Collage", "remotebuzzer:remotebuzzer_collagetime": "Secondi che il bottone deve essere premuto per attivare un collage", - "remotebuzzer:remotebuzzer_enabled": "Abilita Supporto Pulsante Fisico", "remotebuzzer:remotebuzzer_logfile": "File di log", "remotebuzzer:remotebuzzer_picturebutton": "Pulsante Immagine", "remotebuzzer:remotebuzzer_port": "Porta del server", "remotebuzzer:remotebuzzer_printbutton": "Pulsante Stampa", "remotebuzzer:remotebuzzer_shutdownbutton": "Pulsante Di Spegnimento", "remotebuzzer:remotebuzzer_shutdownholdtime": "Secondi per iniziare l'arresto", - "remotebuzzer:remotebuzzer_userotary": "Modalità Codifica Rotativa", "reset": "Reset", "reset:reset_button": "Esegui ripristino configurazione", "reset:reset_remove_config": "Elimina la configurazione personale (my.config.inc.php)", diff --git a/resources/lang/nl.json b/resources/lang/nl.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/resources/lang/nl.json @@ -0,0 +1 @@ +{} diff --git a/resources/lang/pl.json b/resources/lang/pl.json index d097ad3fc..302fa3e98 100644 --- a/resources/lang/pl.json +++ b/resources/lang/pl.json @@ -73,7 +73,6 @@ "general:picture_preview_before_processing": "Wstępnie załaduj i pokaż obraz podczas przetwarzania filtrów", "general:picture_thumb_size": "Rozmiar miniatur", "general:picture_time_to_live": "Pokaż obraz po wyzwoleniu:", - "general:qr_enabled": "Użyj kodów QR", "general:start_screen_subtitle": "Ekran startowy (napisy)", "general:start_screen_title": "Ekran startowy (tytuł)", "general:ui_language": "Wybierz język", @@ -137,15 +136,12 @@ "manual:gallery:pswp_preventSwiping": "Jeśli włączone, gest przesuwania będzie wyłączony (nawet dla urządzeń dotykowych), a strzałki będą wyświetlane na urządzeniach dotykowych. Tylko strzałki mogą przełączać obrazy.", "manual:gallery:pswp_tapToToggleControls": "Jeśli włączone, widoczność sterowania / przycisków jest przełączana przez dotyk.", "manual:general:database_file": "Nazwa pliku bazy danych.", - "manual:general:dev_enabled": "Włącza tryb deweloperski. Przykładowe zdjęcia będą używane zamiast robienia zdjęć.", "manual:general:dev_reload_on_error": "Jeśli wystąpi błąd podczas wykonywania zdjęcia, fotobudka przeładuje się automatycznie po 5 sekundach.", "manual:general:download_thumbs": "Jeśli włączone, miniatury będą używane podczas pobierania (jeśli istnieje) zamiast pełnego rozmiaru obrazu.", "manual:general:picture_preview_before_processing": "Jeśli włączone, obrazy są wstępnie załadowane i wyświetlane podczas przetwarzania filtrów.", "manual:general:picture_thumb_size": "Wybierz rozmiar miniatury: XS = max 360px, S = max 540px, M = max 900px, L = max 1080px, XL = max 1260px", - "manual:general:picture_time_to_live": "Wprowadź wartość w milisekundach. Ta wartość definiuje czas, w którym obraz jest widoczny na ekranie wyników po wykonaniu zdjęcia.", "manual:general:start_screen_subtitle": "Wprowadź podtytuł widoczny na stronie startowej.", "manual:general:ui_language": "Wybierz język interfejsu.

    Jeśli brakuje języka lub chcesz pomóc w ulepszeniu tłumaczeń, odwiedź \"Jak zaktualizować lub dodać tłumaczenie? Instrukcja na WIKI Photobooth.

    ", - "manual:general:webserver_ip": "Proszę zdefiniować adres IP serwera WWW Fotobudki, aby kod QR działał w przypadku uzyskania dostępu do Fotobudki przez \"localhost\", \"127.0.0.1\" lub jeśli masz zainstalowaną fotobudkę w podfolderze.

    Przykład, jeśli Fotobudka jest dostępna bezpośrednio: 192.168.0.50.

    Przykład, jeśli Fotobudka jest zainstalowana w podfolderze: 192.168.0.50/photobooth

    ", "manual:general:webserver_ssid": "Proszę zdefiniować nazwę sieci bezprzewodowej (SSID), która ma być użyta do uzyskania dostępu do Fotobudki. Nazwa sieci bezprzewodowej (SSID) jest wyświetlana na stronie wyników po wywołaniu kodu QR.", "manual:jpeg_quality:jpeg_quality_chroma": "Jakość obrazu używana do obrazów chromakeying.", "manual:jpeg_quality:jpeg_quality_image": "Jakość obrazu używana do wykonanych zdjęć.", @@ -169,7 +165,6 @@ "manual:pictures:picture_frame": "Wprowadź ścieżkę ramki, która zostanie zastosowana do zdjęcia po jego wykonaniu.", "manual:pictures:picture_keep_original": "Jeśli włączone, oryginalne obrazy będą przechowywane wewnątrz folderu tmp.", "manual:pictures:picture_key": "Określ identyfikator klawisza, aby użyć go do zrobienia zdjęcia (np. 13 jest klawiszem enter). Przykładowo użyj https://keycode.info, aby uzyskać identyfikator klawisza.", - "manual:pictures:picture_naming": "Wybierz między datą, numerowaniem lub losową nazwą obrazów. Dla zdjęć nazwanych datą możesz wyświetlić obraz z datą i czasem wewnątrz galerii.", "manual:pictures:picture_permissions": "Zmień uprawnienia zdjęć poleceniem \"chmod\". Zmień tę wartość tylko, jeśli znasz uprawnienia plików w Linux/Unix.", "manual:pictures:picture_polaroid_effect": "Jeśli opcja jest włączona, efekt polaroidowy zostanie zastosowany do zdjęcia po jego wykonaniu.", "manual:pictures:picture_polaroid_rotation": "Wprowadź wartość, która ustawi stopnie obrotu obrazu po jego wykonaniu z efektem polaroid.", @@ -250,7 +245,6 @@ "printing": "Rozpoczęto drukowanie! Proszę czekać...", "qr": "Kod QR", "qrHelp": "Aby pobrać zdjęcie na swój smartfon, połącz się z WiFi:", - "really_delete_image": "zostanie usunięte! Tej operacji nie można cofnąć! Naprawdę usunąć zdjęcie?", "reload": "Przeładuj Stronę", "remotebuzzer:remotebuzzer_collagetime": "Przytrzymaj przycisk x sekund, aby wyzwolić kolaż", "remotebuzzer:remotebuzzer_logfile": "Plik dziennika", diff --git a/resources/lang/pt.json b/resources/lang/pt.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/resources/lang/pt.json @@ -0,0 +1 @@ +{} diff --git a/resources/lang/ru.json b/resources/lang/ru.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/resources/lang/ru.json @@ -0,0 +1 @@ +{} diff --git a/resources/lang/tr.json b/resources/lang/tr.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/resources/lang/tr.json @@ -0,0 +1 @@ +{} diff --git a/resources/sh/check-dependencies.sh b/resources/sh/check-dependencies.sh new file mode 100644 index 000000000..0d0533843 --- /dev/null +++ b/resources/sh/check-dependencies.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +# Stop on the first sign of trouble +set -e + +WEBSERVER=( + 'libapache2-mod-php' + 'nginx' + 'lighttpd' +) + +COMMON_PACKAGES=( + 'git' + 'gphoto2' + 'libimage-exiftool-perl' + 'nodejs' + 'php-gd' + 'php-zip' + 'yarn' + 'rsync' + 'udisks2' +) + +MISSING_PACKAGES=() + +echo "[Info] Checking for webserver..." +for server in "${WEBSERVER[@]}"; do + if [ $(dpkg-query -W -f='${Status}' ${server} 2>/dev/null | grep -c "ok installed") -eq 1 ]; then + echo "[Webserver] ${server} used." + if [[ ${server} == "nginx" || ${server} == "lighttpd" ]]; then + echo "[NOTE] You're using ${server} as your Webserver." + echo "[NOTE] For a no-hassle-setup Apache2 Webserver is recommend!" + if [ $(dpkg-query -W -f='${Status}' php-fpm 2>/dev/null | grep -c "ok installed") -eq 1 ]; then + echo "[Package] php-fpm installed already" + else + echo "[WARNING] Missing common package: php-fpm" + fi + fi + fi +done + +echo "" +echo "" + +echo "[Info] Checking common software..." +for package in "${COMMON_PACKAGES[@]}"; do + if [ $(dpkg-query -W -f='${Status}' ${package} 2>/dev/null | grep -c "ok installed") -eq 1 ]; then + echo "[Package] ${package} installed already" + else + echo "[WARNING] Missing common package: ${package}" + MISSING_PACKAGES+=("${package}") + fi +done + +echo "" +echo "" + +if [[ ${MISSING_PACKAGES[@]} ]]; then + echo "[RESULT] Missing common packages:" + for dependencie in "${MISSING_PACKAGES[@]}" + do + echo $dependencie + done + echo "[WARNING] Please install missing common packages!" +else + echo "[RESULT] No common packages missing." + echo "[OK]" + +fi + diff --git a/resources/sh/check-github.sh b/resources/sh/check-github.sh new file mode 100644 index 000000000..e1941a518 --- /dev/null +++ b/resources/sh/check-github.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Stop on the first sign of trouble +set -e + +inside_git_repo="$(git rev-parse --is-inside-work-tree 2>/dev/null)" + +if [ $inside_git_repo == 'true' ]; +then + if [ -z "$(git status --porcelain)" ]; + then + echo "true" + else + echo "commit" + fi +else + echo "false" +fi + diff --git a/resources/sh/commit.sh b/resources/sh/commit.sh new file mode 100644 index 000000000..5d8266a49 --- /dev/null +++ b/resources/sh/commit.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Stop on the first sign of trouble +# set -e + +date=$(date +"%Y%m%d-%H-%M") +get_username="$(git config user.name)" +set_username="$(git config user.name Photobooth)" +get_useremail="$(git config user.email)" +set_useremail="$(git config user.email Photobooth@localhost)" + +if [ -z "$get_username" ]; +then + echo "git user.name not set!" + echo "Setting git user.name." + $set_username + get_username="$(git config user.name)" +fi + +if [ -z "$get_useremail" ]; +then + echo "git user.email not set!" + echo "Setting git user.email." + $set_useremail + get_useremail="$(git config user.email)" +fi + +echo "git user.name: $get_username" +echo "git user.email: $get_useremail" + +git add --all +git commit -a -m "backup changes" +git checkout -b "backup-$date" +echo "Backup done to branch: backup-$date" + diff --git a/resources/sh/update-dev.sh b/resources/sh/update-dev.sh new file mode 100644 index 000000000..09a9adaab --- /dev/null +++ b/resources/sh/update-dev.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# Stop on the first sign of trouble +set -e + +git fetch origin +git checkout origin/dev +git submodule update --init +yarn install +yarn build +echo "Update completed!" +echo "Checked out on HEAD:" +git log --format="%h %s" -n 1 + diff --git a/resources/sh/update-stable.sh b/resources/sh/update-stable.sh new file mode 100644 index 000000000..ae586bc17 --- /dev/null +++ b/resources/sh/update-stable.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# Stop on the first sign of trouble +set -e + +git fetch origin +git checkout origin/stable3 +git submodule update --init +yarn install +yarn build +echo "Update completed!" + diff --git a/scripts/pack-build.js b/scripts/pack-build.js index 453a39046..6290d9761 100644 --- a/scripts/pack-build.js +++ b/scripts/pack-build.js @@ -63,14 +63,18 @@ function createArchive(fileName, archive) { archive.file('chromakeying.php'); archive.file('faq/index.php'); archive.file('gallery.php'); + archive.file('HEAD'); archive.file('index.php'); archive.file('LICENSE'); + archive.file('LICENSE_NOTICE'); archive.file('livechroma.php'); archive.file('package.json'); archive.file('photobooth.desktop'); archive.file('phpinfo.php'); + archive.file('private/README.md'); archive.file('README.md'); archive.file('update-booth.sh'); + archive.file('welcome.php'); archive.directory('node_modules/@andreasremdt/simple-translator/'); archive.directory('node_modules/font-awesome/'); archive.file('node_modules/github-markdown-css/github-markdown.css'); diff --git a/slideshow/index.php b/slideshow/index.php index 7382fdf64..087bda242 100644 --- a/slideshow/index.php +++ b/slideshow/index.php @@ -20,7 +20,7 @@ - + <?=$config['ui']['branding']?> Slideshow @@ -39,6 +39,9 @@ + + + @@ -96,6 +99,6 @@ - + diff --git a/src/js/admin.js b/src/js/admin.js index 048330dfc..8d707ed6f 100644 --- a/src/js/admin.js +++ b/src/js/admin.js @@ -1,20 +1,25 @@ -/* globals i18n */ +/* globals photoboothTools */ $(function () { - const getTranslation = function (key) { - const translation = i18n(key, config.ui.language); - const fallbackTranslation = i18n(key, 'en'); - if (translation) { - return translation; - } else if (fallbackTranslation) { - return fallbackTranslation; - } - - return key; + const shellCommand = function ($mode) { + const command = { + mode: $mode + }; + + photoboothTools.console.log('Run' + $mode); + + jQuery + .post('../api/shellCommand.php', command) + .done(function (result) { + photoboothTools.console.log($mode, 'result: ', result); + }) + .fail(function (xhr, status, result) { + photoboothTools.console.log($mode, 'result: ', result); + }); }; $('#reset-btn').on('click', function (e) { e.preventDefault(); - const msg = getTranslation('really_delete'); + const msg = photoboothTools.getTranslation('really_delete'); const really = confirm(msg); const data = {type: 'reset'}; const elem = $(this); @@ -73,6 +78,20 @@ $(function () { return false; }); + $('#updater-btn').on('click', function (e) { + e.preventDefault(); + location.assign('../update.php'); + + return false; + }); + + $('#dependencies-btn').on('click', function (e) { + e.preventDefault(); + location.assign('../dependencies.php'); + + return false; + }); + $('#databaserebuild-btn').on('click', function (e) { e.preventDefault(); const elem = $(this); @@ -104,16 +123,16 @@ $(function () { method: 'GET', success: (data) => { $('#checkVersion').empty(); - console.log('data', data); + photoboothTools.console.log('data', data); if (!data.updateAvailable) { - $('#current_version_text').text(getTranslation('using_latest_version')); + $('#current_version_text').text(photoboothTools.getTranslation('using_latest_version')); } else if (/^\d+\.\d+\.\d+$/u.test(data.availableVersion)) { - $('#current_version_text').text(getTranslation('current_version')); + $('#current_version_text').text(photoboothTools.getTranslation('current_version')); $('#current_version').text(data.currentVersion); - $('#available_version_text').text(getTranslation('available_version')); + $('#available_version_text').text(photoboothTools.getTranslation('available_version')); $('#available_version').text(data.availableVersion); } else { - $('#current_version_text').text(getTranslation('test_update_available')); + $('#current_version_text').text(photoboothTools.getTranslation('test_update_available')); } elem.removeClass('saving'); @@ -126,7 +145,7 @@ $(function () { }, error: (jqXHR) => { - console.log('Error checking Version: ', jqXHR.responseText); + photoboothTools.console.log('Error checking Version: ', jqXHR.responseText); elem.removeClass('saving'); elem.addClass('error'); @@ -146,6 +165,20 @@ $(function () { return false; }); + $('#reboot-btn').on('click', function (ev) { + ev.preventDefault(); + shellCommand('reboot'); + + return false; + }); + + $('#shutdown-btn').on('click', function (ev) { + ev.preventDefault(); + shellCommand('shutdown'); + + return false; + }); + // Admin Panel active section at init $('#nav-general').addClass('active'); diff --git a/src/js/adminshortcut.js b/src/js/adminshortcut.js index c1c5cb5d4..8875e39ce 100644 --- a/src/js/adminshortcut.js +++ b/src/js/adminshortcut.js @@ -1,3 +1,4 @@ +/* globals photoboothTools */ /* exported adminsettings */ let admincount = 0; @@ -10,7 +11,7 @@ function adminsettings() { if (admincount == 3) { window.location.href = 'login/index.php'; } - console.log(admincount); + photoboothTools.console.log(admincount); admincount++; setTimeout(countreset, 10000); } diff --git a/src/js/chromakeying.js b/src/js/chromakeying.js index 575010b23..027ab27d5 100644 --- a/src/js/chromakeying.js +++ b/src/js/chromakeying.js @@ -1,4 +1,4 @@ -/* globals MarvinColorModelConverter AlphaBoundary MarvinImage i18n Seriously initRemoteBuzzerFromDOM rotaryController */ +/* globals MarvinColorModelConverter AlphaBoundary MarvinImage Seriously initRemoteBuzzerFromDOM rotaryController photoboothTools */ /* exported setBackgroundImage */ let mainImage; let mainImageWidth; @@ -10,18 +10,6 @@ let target; let chroma; let seriouslyimage; -const getTranslation = function (key) { - const translation = i18n(key, config.ui.language); - const fallbackTranslation = i18n(key, 'en'); - if (translation) { - return translation; - } else if (fallbackTranslation) { - return fallbackTranslation; - } - - return key; -}; - function greenToTransparency(imageIn, imageOut) { for (let y = 0; y < imageIn.getHeight(); y++) { for (let x = 0; x < imageIn.getWidth(); x++) { @@ -123,10 +111,8 @@ function setMainImage(imgSrc) { const r = parseInt(color.substr(1, 2), 16) / 255; const g = parseInt(color.substr(3, 2), 16) / 255; const b = parseInt(color.substr(5, 2), 16) / 255; - if (config.dev.enabled) { - console.log('Chromakeying color:', color); - console.log('Red:', r, 'Green:', g, 'Blue:', b); - } + photoboothTools.console.logDev('Chromakeying color:', color); + photoboothTools.console.logDev('Red:', r, 'Green:', g, 'Blue:', b); chroma.screen = [r, g, b, 1]; seriously.go(); mainImage = new Image(); @@ -196,10 +182,10 @@ function calculateAspectRatioFit(srcWidth, srcHeight, maxWidth, maxHeight) { } function printImage(filename, cb) { - const errormsg = getTranslation('error'); + const errormsg = photoboothTools.getTranslation('error'); if (isPrinting) { - console.log('Printing already: ' + isPrinting); + photoboothTools.console.log('Printing already: ' + isPrinting); } else { isPrinting = true; setTimeout(function () { @@ -210,22 +196,24 @@ function printImage(filename, cb) { filename: filename }, success: (data) => { - console.log('Picture processed: ', data); + photoboothTools.console.log('Picture processed: ', data); if (data.error) { - console.log('An error occurred: ', data.error); - $('#print_mesg').empty(); + photoboothTools.console.log('An error occurred: ', data.error); + photoboothTools.modal.empty('#print_mesg'); $('#print_mesg').html( '' ); } setTimeout(function () { - $('#print_mesg').removeClass('modal--show'); + photoboothTools.modal.close('#print_mesg'); if (data.error) { - $('#print_mesg').empty(); + photoboothTools.modal.empty('#print_mesg'); $('#print_mesg').html( - '' + '' ); } cb(); @@ -233,17 +221,19 @@ function printImage(filename, cb) { }, config.print.time); }, error: (jqXHR, textStatus) => { - console.log('An error occurred: ', textStatus); - $('#print_mesg').empty(); + photoboothTools.console.log('An error occurred: ', textStatus); + photoboothTools.modal.empty('#print_mesg'); $('#print_mesg').html( '' ); setTimeout(function () { - $('#print_mesg').removeClass('modal--show'); - $('#print_mesg').empty(); + photoboothTools.modal.close('#print_mesg'); + photoboothTools.modal.empty('#print_mesg'); $('#print_mesg').html( - '' + '' ); cb(); isPrinting = false; @@ -273,7 +263,7 @@ function saveImage(cb) { function printImageHandler(ev) { ev.preventDefault(); - $('#print_mesg').addClass('modal--show'); + photoboothTools.modal.open('#print_mesg'); setTimeout(function () { saveImage((data) => { @@ -291,12 +281,12 @@ function printImageHandler(ev) { function saveImageHandler(ev) { ev.preventDefault(); - $('#save_mesg').addClass('modal--show'); + photoboothTools.modal.open('#save_mesg'); setTimeout(function () { saveImage(() => { setTimeout(function () { - $('#save_mesg').removeClass('modal--show'); + photoboothTools.modal.close('#save_mesg'); $('#save-btn').blur(); }, 2000); }); @@ -316,7 +306,7 @@ function closeHandler(ev) { $(document).on('keyup', function (ev) { if (config.print.from_chromakeying && config.print.key && parseInt(config.print.key, 10) === ev.keyCode) { if (isPrinting) { - console.log('Printing already in progress!'); + photoboothTools.console.log('Printing already in progress!'); } else { $('#print-btn').trigger('click'); } diff --git a/src/js/core.js b/src/js/core.js index fc790864f..273dfc5b8 100644 --- a/src/js/core.js +++ b/src/js/core.js @@ -1,4 +1,4 @@ -/* globals initPhotoSwipeFromDOM initRemoteBuzzerFromDOM i18n setMainImage remoteBuzzerClient rotaryController globalGalleryHandle */ +/* globals initPhotoSwipeFromDOM initRemoteBuzzerFromDOM setMainImage remoteBuzzerClient rotaryController globalGalleryHandle photoboothTools */ const photoBooth = (function () { // vars @@ -7,7 +7,26 @@ const photoBooth = (function () { startPage = $('#start'), wrapper = $('#wrapper'), gallery = $('#gallery'), + cheese = $('.cheese'), resultPage = $('#result'), + mySideNav = $('#mySidenav'), + ipcamView = $('#ipcam--view'), + galimages = $('#galimages'), + printMesg = $('#print_mesg'), + loading = $('.loading'), + loaderImage = $('.loaderImage'), + triggerPic = $('.triggerPic'), + triggerCollage = $('.triggerCollage'), + printBtn = $('.printbtn'), + deleteBtn = $('.deletebtn'), + qrCodeModal = $('#qrCode'), + counter = $('#counter'), + resultInner = $('.resultInner'), + spinner = $('.spinner'), + sendMail = $('.send-mail'), + idVideoView = $('#video--view'), + idVideoPreview = $('#video--preview'), + idVideoSensor = $('#video--sensor'), webcamConstraints = { audio: false, video: { @@ -28,36 +47,10 @@ const photoBooth = (function () { currentCollageFile = '', imgFilter = config.filters.defaults, pid, - command; - - const modal = { - open: function (selector) { - $(selector).addClass('modal--show'); - }, - close: function (selector) { - //api.showResultInner(true); - - if ($(selector).hasClass('modal--show')) { - $(selector).removeClass('modal--show'); - - return true; - } - - return false; - }, - toggle: function (selector) { - $(selector).toggleClass('modal--show'); - }, - empty: function (selector) { - modal.close(selector); - - $(selector).find('.modal__body').empty(); - } - }; - - api.reloadPage = function () { - window.location.reload(); - }; + command, + startTime, + endTime, + totalTime; // Returns true when timeOut is pending api.isTimeOutPending = function () { @@ -68,10 +61,17 @@ const photoBooth = (function () { api.resetTimeOut = function () { clearTimeout(timeOut); + photoboothTools.console.log('Timeout for auto reload cleared.'); + if (!takingPic) { + photoboothTools.console.logDev( + 'Timeout for auto reload set to', + config.picture.time_to_live * 1000, + ' seconds.' + ); timeOut = setTimeout(function () { - api.reloadPage(); - }, config.picture.time_to_live); + photoboothTools.reloadPage(); + }, config.picture.time_to_live * 1000); } }; @@ -79,20 +79,20 @@ const photoBooth = (function () { api.reset = function () { loader.removeClass('open'); loader.removeClass('error'); - modal.empty('#qrCode'); + photoboothTools.modal.empty('#qrCode'); $('.qrbtn').removeClass('active').attr('style', ''); - $('.loading').text(''); + loading.text(''); gallery.removeClass('gallery--open'); gallery.find('.gallery__inner').hide(); - $('.spinner').hide(); - $('.send-mail').hide(); - $('#video--view').hide(); - $('#video--preview').hide(); - $('#video--sensor').hide(); - $('#ipcam--view').hide(); + spinner.hide(); + sendMail.hide(); + idVideoView.hide(); + idVideoPreview.hide(); + idVideoSensor.hide(); + ipcamView.hide(); api.resetMailForm(); - $('#loader').css('background', config.colors.background_countdown); - $('#loader').css('background-color', config.colors.background_countdown); + loader.css('background', config.colors.background_countdown); + loader.css('background-color', config.colors.background_countdown); }; // init @@ -103,7 +103,7 @@ const photoBooth = (function () { resultPage.hide(); startPage.addClass('open'); - if (config.preview.asBackground || (config.preview.mode == 'gphoto' && !config.preview.gphoto_bsm)) { + if (config.preview.asBackground || (config.preview.mode === 'gphoto' && !config.preview.gphoto_bsm)) { api.startVideo('preview'); } @@ -111,155 +111,102 @@ const photoBooth = (function () { rotaryController.focusSet('#start'); }; - api.getTranslation = function (key) { - const translation = i18n(key, config.ui.language); - const fallbackTranslation = i18n(key, 'en'); - if (translation) { - return translation; - } else if (fallbackTranslation) { - return fallbackTranslation; - } - - return key; - }; - api.openNav = function () { - $('#mySidenav').addClass('sidenav--open'); + mySideNav.addClass('sidenav--open'); rotaryController.focusSet('#mySidenav'); }; api.closeNav = function () { - $('#mySidenav').removeClass('sidenav--open'); + mySideNav.removeClass('sidenav--open'); }; api.toggleNav = function () { - $('#mySidenav').toggleClass('sidenav--open'); + mySideNav.toggleClass('sidenav--open'); - if ($('#mySidenav').hasClass('sidenav--open')) { + if (mySideNav.hasClass('sidenav--open')) { rotaryController.focusSet('#mySidenav'); } }; - api.startVideo = function (mode) { - if (config.preview.asBackground) { - api.stopVideo('preview'); + api.getAndDisplayMedia = function (mode, retry = 0) { + const getMedia = + navigator.mediaDevices.getUserMedia || + navigator.mediaDevices.webkitGetUserMedia || + navigator.mediaDevices.mozGetUserMedia || + false; + + if (!getMedia) { + return; } + if (config.preview.flipHorizontal) { + idVideoView.addClass('flip-horizontal'); + idVideoPreview.addClass('flip-horizontal'); + } + + getMedia + .call(navigator.mediaDevices, webcamConstraints) + .then(function (stream) { + if (mode === 'preview') { + idVideoPreview.show(); + videoPreview.srcObject = stream; + wrapper.css('background-image', 'none'); + wrapper.css('background-color', 'transparent'); + } else { + idVideoView.show(); + videoView.srcObject = stream; + } + api.stream = stream; + }) + .catch(function (error) { + photoboothTools.console.log('Could not get user media: ', error); + if (config.preview.mode === 'gphoto' && retry < 3) { + photoboothTools.console.logDev('Getting user media failed. Retrying. Retry: ' + retry); + retry += 1; + setTimeout(function () { + api.getAndDisplayMedia('preview', retry); + }, retry * 1000); + } + }); + }; + + api.startWebcam = function () { const dataVideo = { play: 'true' }; + jQuery + .post('api/takeVideo.php', dataVideo) + .done(function (result) { + photoboothTools.console.log('Start webcam', result); + pid = result.pid; + }) + .fail(function (xhr, status, result) { + photoboothTools.console.log('Could not start webcam', result); + }); + }; + + api.startVideo = function (mode) { + if (config.preview.asBackground) { + api.stopVideo('preview'); + } + if (!navigator.mediaDevices) { return; } - if (config.preview.mode === 'gphoto') { - if (!config.preview.gphoto_bsm && mode === 'preview') { - jQuery - .post('api/takeVideo.php', dataVideo) - .done(function (result) { - console.log('Start webcam', result); - pid = result.pid; - }) - .fail(function (xhr, status, result) { - console.log('Could not start webcam', result); - }); - } else if (!config.preview.gphoto_bsm && mode === 'view') { - const getMedia = - navigator.mediaDevices.getUserMedia || - navigator.mediaDevices.webkitGetUserMedia || - navigator.mediaDevices.mozGetUserMedia || - false; - - if (!getMedia) { - return; - } - - if (config.preview.flipHorizontal) { - $('#video--view').addClass('flip-horizontal'); - $('#video--preview').addClass('flip-horizontal'); - } - - getMedia - .call(navigator.mediaDevices, webcamConstraints) - .then(function (stream) { - $('#video--view').show(); - videoView.srcObject = stream; - api.stream = stream; - }) - .catch(function (error) { - console.log('Could not get user media: ', error); - }); - } else { - jQuery - .post('api/takeVideo.php', dataVideo) - .done(function (result) { - console.log('Start webcam', result); - pid = result.pid; - const getMedia = - navigator.mediaDevices.getUserMedia || - navigator.mediaDevices.webkitGetUserMedia || - navigator.mediaDevices.mozGetUserMedia || - false; - - if (!getMedia) { - return; - } - - if (config.preview.flipHorizontal) { - $('#video--view').addClass('flip-horizontal'); - $('#video--preview').addClass('flip-horizontal'); - } - - getMedia - .call(navigator.mediaDevices, webcamConstraints) - .then(function (stream) { - $('#video--view').show(); - videoView.srcObject = stream; - api.stream = stream; - }) - .catch(function (error) { - console.log('Could not get user media: ', error); - }); - }) - .fail(function (xhr, status, result) { - console.log('Could not start webcam', result); - }); + if (mode === 'preview') { + if (config.preview.mode === 'gphoto' && !config.preview.gphoto_bsm) { + api.startWebcam(); } + api.getAndDisplayMedia('preview'); + } else if (mode === 'init') { + api.startWebcam(); } else { - const getMedia = - navigator.mediaDevices.getUserMedia || - navigator.mediaDevices.webkitGetUserMedia || - navigator.mediaDevices.mozGetUserMedia || - false; - - if (!getMedia) { - return; - } - - if (config.preview.flipHorizontal) { - $('#video--view').addClass('flip-horizontal'); - $('#video--preview').addClass('flip-horizontal'); + if (config.preview.mode === 'gphoto' && config.preview.gphoto_bsm) { + api.startWebcam(); } - - getMedia - .call(navigator.mediaDevices, webcamConstraints) - .then(function (stream) { - if (mode === 'preview') { - $('#video--preview').show(); - videoPreview.srcObject = stream; - api.stream = stream; - wrapper.css('background-image', 'none'); - wrapper.css('background-color', 'transparent'); - } else { - $('#video--view').show(); - videoView.srcObject = stream; - } - api.stream = stream; - }) - .catch(function (error) { - console.log('Could not get user media: ', error); - }); + api.getAndDisplayMedia('view'); } }; @@ -268,9 +215,9 @@ const photoBooth = (function () { const track = api.stream.getTracks()[0]; track.stop(); if (mode === 'preview') { - $('#video--preview').hide(); + idVideoPreview.hide(); } else { - $('#video--view').hide(); + idVideoView.hide(); } } }; @@ -285,22 +232,22 @@ const photoBooth = (function () { jQuery .post('api/takeVideo.php', dataVideo) .done(function (result) { - console.log('Stop webcam', result); + photoboothTools.console.log('Stop webcam', result); const track = api.stream.getTracks()[0]; track.stop(); - $('#video--view').hide(); + idVideoView.hide(); }) .fail(function (xhr, status, result) { - console.log('Could not stop webcam', result); + photoboothTools.console.log('Could not stop webcam', result); }); } }; api.showResultInner = function (flag) { if (flag) { - $('.resultInner').addClass('show'); + resultInner.addClass('show'); } else { - $('.resultInner').removeClass('show'); + resultInner.removeClass('show'); } }; @@ -309,23 +256,23 @@ const photoBooth = (function () { mode: $mode }; - console.log('Run', $mode); + photoboothTools.console.log('Run', $mode); jQuery .post('api/shellCommand.php', command) .done(function (result) { - console.log($mode, 'result: ', result); + photoboothTools.console.log($mode, 'result: ', result); }) .fail(function (xhr, status, result) { - console.log($mode, 'result: ', result); + photoboothTools.console.log($mode, 'result: ', result); }); }; api.thrill = function (photoStyle) { api.closeNav(); api.reset(); - api.showResultInner(false); api.closeGallery(); + api.showResultInner(false); remoteBuzzerClient.inProgress(true); @@ -335,9 +282,7 @@ const photoBooth = (function () { api.resetTimeOut(); } - if (config.dev.enabled) { - console.log('Taking photo:', takingPic); - } + photoboothTools.console.logDev('Taking photo: ' + takingPic); if (config.pre_photo.cmd) { api.shellCommand('pre-command'); @@ -358,72 +303,74 @@ const photoBooth = (function () { if (config.preview.mode === 'device_cam' || config.preview.mode === 'gphoto') { api.startVideo('view'); } else if (config.preview.mode === 'url') { - $('#ipcam--view').show(); - $('#ipcam--view').addClass('streaming'); + ipcamView.show(); + ipcamView.addClass('streaming'); } loader.addClass('open'); - api.startCountdown( - nextCollageNumber ? config.collage.cntdwn_time : config.picture.cntdwn_time, - $('#counter'), - () => { - api.cheese(photoStyle); + api.startCountdown(nextCollageNumber ? config.collage.cntdwn_time : config.picture.cntdwn_time, counter, () => { + if (config.get_request.countdown) { + api.getRequest(photoStyle); } - ); + api.cheese(photoStyle); + }); }; // Cheese - api.cheese = function (photoStyle) { - if (config.dev.enabled) { - console.log(photoStyle); - } + api.cheese = function (photoStyle, retry = 0) { + photoboothTools.console.logDev('Photostyle: ' + photoStyle); - $('#counter').empty(); - $('.cheese').empty(); + counter.empty(); + cheese.empty(); if (config.picture.no_cheese) { - console.log('Cheese is disabled.'); + photoboothTools.console.log('Cheese is disabled.'); } else if (photoStyle === 'photo' || photoStyle === 'chroma') { - const cheesemsg = api.getTranslation('cheese'); - $('.cheese').text(cheesemsg); + const cheesemsg = photoboothTools.getTranslation('cheese'); + cheese.text(cheesemsg); } else { - const cheesemsg = api.getTranslation('cheeseCollage'); - $('.cheese').text(cheesemsg); + const cheesemsg = photoboothTools.getTranslation('cheeseCollage'); + cheese.text(cheesemsg); $('

    ') .text(`${nextCollageNumber + 1} / ${config.collage.limit}`) .appendTo('.cheese'); } - if (config.preview.mode === 'gphoto' && !config.picture.no_cheese) { - api.stopPreviewVideo(); - } + if (retry <= 0) { + if (config.preview.mode === 'gphoto' && !config.picture.no_cheese) { + api.stopPreviewVideo(); + } - if (config.preview.mode === 'device_cam' && config.preview.camTakesPic && !api.stream && !config.dev.enabled) { - console.log('No preview by device cam available!'); + if ( + config.preview.mode === 'device_cam' && + config.preview.camTakesPic && + !api.stream && + !config.dev.demo_images + ) { + photoboothTools.console.log('No preview by device cam available!'); - api.errorPic({ - error: 'No preview by device cam available!' - }); - } else if (config.picture.no_cheese) { - api.takePic(photoStyle); - } else { - setTimeout(() => { + api.errorPic({ + error: 'No preview by device cam available!' + }); + } else if (config.picture.no_cheese) { api.takePic(photoStyle); - }, config.picture.cheese_time); + } else { + setTimeout(() => { + api.takePic(photoStyle); + }, config.picture.cheese_time); + } } }; // take Picture api.takePic = function (photoStyle) { - if (config.dev.enabled) { - console.log('Take Picture:' + photoStyle); - } + photoboothTools.console.log('Take Picture:', photoStyle); remoteBuzzerClient.inProgress(true); if (config.preview.mode === 'device_cam' || config.preview.mode === 'gphoto') { - if (config.preview.camTakesPic && !config.dev.enabled) { + if (config.preview.camTakesPic && !config.dev.demo_images) { videoSensor.width = videoView.videoWidth; videoSensor.height = videoView.videoHeight; videoSensor.getContext('2d').drawImage(videoView, 0, 0); @@ -432,8 +379,8 @@ const photoBooth = (function () { api.stopVideo('view'); } } else if (config.preview.mode === 'url') { - $('#ipcam--view').removeClass('streaming'); - $('#ipcam--view').hide(); + ipcamView.removeClass('streaming'); + ipcamView.hide(); } const data = { @@ -456,15 +403,20 @@ const photoBooth = (function () { api.callTakePicApi(data); }; - api.callTakePicApi = function (data) { + api.callTakePicApi = function (data, retry = 0) { + const retrymsg = photoboothTools.getTranslation('retry_message'); + startTime = new Date().getTime(); jQuery .post('api/takePic.php', data) .done(function (result) { - console.log('took picture', result); - $('.cheese').empty(); + endTime = new Date().getTime(); + totalTime = endTime - startTime; + photoboothTools.console.log('took ' + data.style, result); + photoboothTools.console.logDev('Taking picture took ' + totalTime + 'ms'); + cheese.empty(); if (config.preview.flipHorizontal) { - $('#video--view').removeClass('flip-horizontal'); - $('#video--preview').removeClass('flip-horizontal'); + idVideoView.removeClass('flip-horizontal'); + idVideoPreview.removeClass('flip-horizontal'); } // reset filter (selection) after picture was taken @@ -473,98 +425,138 @@ const photoBooth = (function () { $('#' + imgFilter).addClass('activeSidenavBtn'); if (result.error) { - api.errorPic(result); + if (config.picture.retry_on_error > 0 && retry < config.picture.retry_on_error) { + photoboothTools.console.logDev('Taking picture failed. Retrying. Retry: ' + retry); + retry += 1; + loading.append( + $('

    ').text( + retrymsg + ' ' + retry + '/' + config.picture.retry_on_error + ) + ); + api.startCountdown(config.picture.retry_timeout, counter, () => { + loading.empty(); + + if (config.picture.no_cheese) { + photoboothTools.console.log('Cheese is disabled.'); + api.callTakePicApi(data, retry); + } else { + api.cheese(data.style, retry); + setTimeout(() => { + api.callTakePicApi(data, retry); + }, config.picture.cheese_time); + } + }); + } else { + api.errorPic(result); + } } else if (result.success === 'collage') { currentCollageFile = result.file; nextCollageNumber = result.current + 1; - $('.spinner').hide(); - $('.loading').empty(); - $('#video--sensor').hide(); + spinner.hide(); + loading.empty(); + idVideoSensor.hide(); let imageUrl = config.foldersRoot.tmp + '/' + result.collage_file; const preloadImage = new Image(); const picdate = Date.now; - const waitmsg = api.getTranslation('wait_message'); - $('.loading').append($('

    ').text(waitmsg)); + const waitmsg = photoboothTools.getTranslation('wait_message'); + loading.append($('

    ').text(waitmsg)); preloadImage.onload = () => { - $('.loaderImage').css({ + loaderImage.css({ 'background-image': `url(${imageUrl}?filter=${imgFilter})` }); - $('.loaderImage').attr('data-img', picdate); + loaderImage.attr('data-img', picdate); }; preloadImage.src = imageUrl; - $('.loaderImage').show(); + loaderImage.show(); + + photoboothTools.console.logDev( + 'Taken collage photo number: ' + (result.current + 1) + ' / ' + result.limit + ); if (config.collage.continuous) { if (result.current + 1 < result.limit) { setTimeout(() => { - $('.loaderImage').css('background-image', 'none'); + loaderImage.css('background-image', 'none'); imageUrl = ''; - $('.loaderImage').css('display', 'none'); + loaderImage.css('display', 'none'); api.thrill('collage'); }, config.collage.continuous_time * 1000); } else { currentCollageFile = ''; nextCollageNumber = 0; setTimeout(() => { - $('.loaderImage').css('background-image', 'none'); + loaderImage.css('background-image', 'none'); imageUrl = ''; - $('.loaderImage').css('display', 'none'); + loaderImage.css('display', 'none'); api.processPic(data.style, result); }, config.collage.continuous_time * 1000); } } else { // collage with interruption - remoteBuzzerClient.collageWaitForNext(); - if (result.current + 1 < result.limit) { - $('' + api.getTranslation('nextPhoto') + '') + $( + '' + + photoboothTools.getTranslation('nextPhoto') + + '' + ) .appendTo('.loading') .click((ev) => { ev.stopPropagation(); ev.preventDefault(); - $('.loaderImage').css('background-image', 'none'); + + loaderImage.css('background-image', 'none'); imageUrl = ''; - $('.loaderImage').css('display', 'none'); + loaderImage.css('display', 'none'); api.thrill('collage'); }); + + remoteBuzzerClient.collageWaitForNext(); } else { - $('' + api.getTranslation('processPhoto') + '') + $( + '' + + photoboothTools.getTranslation('processPhoto') + + '' + ) .appendTo('.loading') .click((ev) => { ev.stopPropagation(); ev.preventDefault(); - $('.loaderImage').css('background-image', 'none'); + + loaderImage.css('background-image', 'none'); imageUrl = ''; - $('.loaderImage').css('display', 'none'); + loaderImage.css('display', 'none'); currentCollageFile = ''; nextCollageNumber = 0; api.processPic(data.style, result); }); + + remoteBuzzerClient.collageWaitForProcessing(); } + $( '' + - api.getTranslation('retakePhoto') + + photoboothTools.getTranslation('retakePhoto') + '' ) .appendTo('.loading') .click((ev) => { ev.stopPropagation(); ev.preventDefault(); - $('.loaderImage').css('background-image', 'none'); + loaderImage.css('background-image', 'none'); imageUrl = ''; - $('.loaderImage').css('display', 'none'); + loaderImage.css('display', 'none'); api.deleteTmpImage(result.collage_file); nextCollageNumber = result.current; api.thrill('collage'); }); - const abortmsg = api.getTranslation('abort'); - $('.loading') + const abortmsg = photoboothTools.getTranslation('abort'); + loading .append($('').text(abortmsg)) .click(() => { location.assign('./'); @@ -583,52 +575,64 @@ const photoBooth = (function () { } }) .fail(function (xhr, status, result) { - api.errorPic(result); + if (retry < 3) { + retry += 1; + photoboothTools.console.logDev('Taking picture failed. Retrying. Retry: ' + retry); + setTimeout(function () { + api.callTakePicApi(data, retry); + }, retry * 250); + } else { + api.errorPic(result); + } }); }; // Show error Msg and reset api.errorPic = function (data) { setTimeout(function () { - $('.spinner').hide(); - $('.loading').empty(); - $('.cheese').empty(); - $('#video--view').hide(); - $('#video--sensor').hide(); + spinner.hide(); + loading.empty(); + cheese.empty(); + idVideoView.hide(); + idVideoSensor.hide(); loader.addClass('error'); - const errormsg = api.getTranslation('error'); - $('.loading').append($('

    ').text(errormsg)); - if (config.dev.error_messages || config.dev.enabled) { - $('.loading').append($('

    ').text(data.error)); + const errormsg = photoboothTools.getTranslation('error'); + loading.append($('

    ').text(errormsg)); + if (config.dev.error_messages) { + loading.append($('

    ').text(data.error)); } + takingPic = false; + remoteBuzzerClient.inProgress(false); + photoboothTools.console.logDev('Taking photo: ' + takingPic); if (config.dev.reload_on_error) { - const reloadmsg = api.getTranslation('auto_reload'); - $('.loading').append($('

    ').text(reloadmsg)); + const reloadmsg = photoboothTools.getTranslation('auto_reload'); + loading.append($('

    ').text(reloadmsg)); setTimeout(function () { - api.reloadPage(); + photoboothTools.reloadPage(); }, 5000); } else { - const reloadmsg = api.getTranslation('reload'); - $('.loading').append($('').text(reloadmsg)); + const reloadmsg = photoboothTools.getTranslation('reload'); + loading.append($('').text(reloadmsg)); } }, 500); }; api.processPic = function (photoStyle, result) { + startTime = new Date().getTime(); const tempImageUrl = config.foldersRoot.tmp + '/' + result.file; - $('.spinner').show(); - $('.loading').text( + spinner.show(); + loading.text( photoStyle === 'photo' || photoStyle === 'chroma' - ? api.getTranslation('busy') - : api.getTranslation('busyCollage') + ? photoboothTools.getTranslation('busy') + : photoboothTools.getTranslation('busyCollage') ); if (photoStyle === 'photo' && config.picture.preview_before_processing) { const preloadImage = new Image(); preloadImage.onload = () => { - $('#loader').css('background-image', `url(${tempImageUrl})`); - $('#loader').addClass('showBackgroundImage'); + loader.css('background-image', `url(${tempImageUrl})`); + loader.addClass('showBackgroundImage'); }; preloadImage.src = tempImageUrl; } @@ -642,33 +646,34 @@ const photoBooth = (function () { style: photoStyle }, success: (data) => { - console.log('picture processed', data); + photoboothTools.console.log(photoStyle + ' processed', data); + endTime = new Date().getTime(); + totalTime = endTime - startTime; + photoboothTools.console.logDev('Processing ' + photoStyle + ' took ' + totalTime + 'ms'); + photoboothTools.console.logDev('Images:', data.images); + + if (config.get_request.processed) { + const getUrl = config.get_request.server + '/' + photoStyle; + const request = new XMLHttpRequest(); + photoboothTools.console.log('Sending GET request to: ' + getUrl); + request.open('GET', getUrl); + request.send(); + } if (data.error) { api.errorPic(data); - takingPic = false; - remoteBuzzerClient.inProgress(false); - if (config.dev.enabled) { - console.log('Taking photo:', takingPic); - } } else if (photoStyle === 'chroma') { api.renderChroma(data.file); } else { - api.renderPic(data.file); + api.renderPic(data.file, data.images); } }, error: (jqXHR, textStatus) => { - console.log('An error occurred', textStatus); + photoboothTools.console.log('An error occurred', textStatus); api.errorPic({ error: 'Request failed: ' + textStatus }); - - takingPic = false; - remoteBuzzerClient.inProgress(false); - if (config.dev.enabled) { - console.log('Taking photo:', takingPic); - } } }); }; @@ -686,7 +691,7 @@ const photoBooth = (function () { preloadImage.onload = function () { $('body').attr('data-main-image', filename); - console.log(config.foldersRoot.keying + '/' + filename); + photoboothTools.console.log(config.foldersRoot.keying + '/' + filename); const chromaimage = config.foldersRoot.keying + '/' + filename; loader.hide(); @@ -700,26 +705,35 @@ const photoBooth = (function () { remoteBuzzerClient.inProgress(false); api.resetTimeOut(); - - if (config.dev.enabled) { - console.log('Taking photo:', takingPic); - } + photoboothTools.console.logDev('Taking photo: ' + takingPic); }; // Render Picture after taking - api.renderPic = function (filename) { + api.renderPic = function (filename, files) { // Add QR Code Image - const qrCodeModal = $('#qrCode'); - modal.empty(qrCodeModal); - $('').on('load', function () { - const body = qrCodeModal.find('.modal__body'); + const qrHelpText = config.qr.custom_text + ? config.qr.text + : photoboothTools.getTranslation('qrHelp') + '
    ' + config.webserver.ssid + ''; + photoboothTools.modal.empty(qrCodeModal); + const body = qrCodeModal.find('.modal__body'); + $('