Skip to content

Commit 5bf4857

Browse files
authored
add faq script section, faq eos40d (#1411)
1 parent 71ce1b9 commit 5bf4857

File tree

4 files changed

+323
-4
lines changed

4 files changed

+323
-4
lines changed

docs/faq/go2rtc-troubleshooting.md

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,19 @@ If you're using `gphoto2` this issue is generally addressed inside the troublesh
5454

5555
The preview is created using gphoto2 and streamed by go2rtc. In some rare cases, the preview may not be fully stopped when the capture is triggered. Usually, it's just milliseconds that cause problems. Continue reading on [Delay capture](#delay-capture) to know how to fix possible timing issues.
5656

57+
### Error: Device is busy
58+
59+
If the preview is running, the camera may be busy when trying to capture an image. This can lead to errors like "Device is busy" or "Could not claim the USB device". There is a bug in go2rtc startingF
60+
by version 1.9.10 which keeps the camera in use even after stopping the preview.
61+
Try to downgrade to version 1.9.9 as a workaround:
62+
63+
```
64+
sudo bash install-photobooth.sh
65+
choose 4 go2rtc
66+
choose 5 updat or downgrade go2rtc only
67+
chose 1.9.9
68+
```
69+
5770
#### Delay capture
5871

5972
We can use a tiny pause between stopping the preview and starting the capture to work around this:
@@ -144,6 +157,13 @@ The full configuration documentation can be found [here](https://github.com/Alex
144157

145158
While setting up go2rtc using the Photobooth Setup Wizard we're creating a default configuration which covers a wide range of devices. It might be, that your camera does not work out of the box with the default configuration and requires some extra work to be used on Photobooth. You're always welcome to contribute camera specific notes not listed below.
146159

160+
Dont forget to restart go2rtc after changing the configuration!
161+
162+
```ell
163+
sudo systemctl stop go2rtc
164+
sudo systemctl start go2rtc
165+
```
166+
147167
### Raspberry Pi Camera Modules
148168

149169
- Default preview width and height match the Pi Camera v3. You might need to adjust the width and height inside the go2rtc configuration to matching values for your camera module.
@@ -163,9 +183,14 @@ log:
163183
exec: trace
164184
```
165185

166-
Now restart go2rtc to apply your changes:
186+
### Canon EOS EOS40D
167187

168-
```ell
169-
sudo systemctl stop go2rtc
170-
sudo systemctl start go2rtc
171188
```
189+
streams:
190+
photobooth:
191+
- "exec:gphoto2 --capture-movie --stdout#input=mjpeg#fps=8#scale=640:480#pause-on-snapshot=true"
192+
193+
log:
194+
exec: trace
195+
```
196+
Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
# Auto copy zu USB NTFS formatiert
2+
3+
Auto copy script to copy images and temp files to an USB drive formatted with NTFS when plugged in.
4+
This script uses `rsync` to copy files from the Photobooth data folder to the USB drive when it is plugged in. It also creates a status file that can be used to monitor the copy progress via a web
5+
page.
6+
The script is triggered via a udev rule when an USB drive with NTFS filesystem is plugged in.
7+
8+
## Requires:
9+
10+
- python3
11+
- rsync
12+
- ntfs-3g
13+
- udev
14+
15+
## Installation
16+
17+
`sudo touch /usr/local/bin/fotobox-copy.sh`
18+
`sudo chmod +x /usr/local/bin/fotobox-copy.sh`
19+
`sudo nano /usr/local/bin/fotobox-copy.sh`
20+
21+
```
22+
#!/usr/bin/env bash
23+
set -Eeuo pipefail
24+
25+
# Configuration
26+
27+
PART="${1}" # e.g. sdb1
28+
DEV="/dev/${PART}"
29+
SRC="/var/www/html/data"
30+
SUB1="images"
31+
SUB2="tmp"
32+
STATUS="/var/www/html/private/copystatus.json"
33+
34+
# ---------- Status helper ----------
35+
36+
write_status() {
37+
local state="$1" pct="$2" msg="$3"
38+
local tmp="${STATUS}.tmp"
39+
40+
# Create JSON safely
41+
42+
printf '{"state":"%s","percent":%s,"message":%s}\n' \
43+
"$state" "$pct" "$(python3 -c "import json; print(json.dumps('$msg'))")" | sudo tee "$tmp" >/dev/null
44+
45+
sudo mv "$tmp" "$STATUS"
46+
}
47+
48+
fail() {
49+
local code="$1"
50+
local line="$2"
51+
write_status "error" 0 "Error (code ${code}) in line ${line}."
52+
exit "$code"
53+
}
54+
55+
trap 'fail $? $LINENO' ERR
56+
57+
# ---------- Start ----------
58+
59+
write_status "starting" 0 "USB detected: ${DEV}. Prepare..."
60+
61+
# Check if device exists
62+
63+
if [[ ! -b "$DEV" ]]; then
64+
write_status "error" 0 "Device ${DEV} not found."
65+
exit 1
66+
fi
67+
68+
# Mount device if not already mounted
69+
70+
MNT="$(lsblk -no MOUNTPOINT "$DEV" | head -n1 || true)"
71+
if [[ -z "${MNT}" ]]; then
72+
sudo mkdir -p /mnt/usbdrive
73+
74+
# mount mit 'flush' Option für exFAT hilft, Puffer schneller zu leeren
75+
76+
sudo mount -o flush "$DEV" /mnt/usbdrive
77+
MNT="/mnt/usbdrive"
78+
fi
79+
80+
DEST="${MNT}/images"
81+
sudo mkdir -p "$DEST"
82+
83+
# Existenz der Quellordner prüfen
84+
85+
[[ -d "${SRC}/${SUB1}" ]] || { write_status "error" 0 "Source ${SUB1} missing"; exit 2; }
86+
[[ -d "${SRC}/${SUB2}" ]] || { write_status "error" 0 "Source ${SUB2} missing"; exit 2; }
87+
88+
# ---------- Prepare Rsync ----------
89+
90+
# --no-inc-recursive: No jumping progress percentage
91+
# --info=progress2: Compact progress output
92+
93+
RSYNC_OPTS=(
94+
-rltD
95+
--info=progress2
96+
--no-inc-recursive
97+
--no-owner --no-group --no-perms
98+
--no-acls --no-xattrs
99+
--omit-dir-times
100+
--modify-window=2
101+
)
102+
103+
write_status "copying" 1 "Calculate copy size..."
104+
105+
# Run rdync in dry-run to get total size
106+
107+
# stdbuf & tr to handle progress output line by line
108+
109+
set +e
110+
sudo stdbuf -oL rsync "${RSYNC_OPTS[@]}" \
111+
"${SRC}/${SUB1}" "${SRC}/${SUB2}" \
112+
"$DEST/" 2>&1 | tr '\r' '\n' | while IFS= read -r line; do
113+
114+
if [[ "$line" =~ ([0-9]{1,3})% ]]; then
115+
p="${BASH_REMATCH[1]}"
116+
117+
# Scale to 95% to leave room for final sync step
118+
# Prevents user from thinking it hung at 100%
119+
display_p=$(( p * 95 / 100 ))
120+
121+
# Only update every percent to reduce writes
122+
write_status "copying" "$display_p" "Copied ${p}%..."
123+
fi
124+
125+
done
126+
RSYNC_EXIT=${PIPESTATUS[0]}
127+
set -e
128+
129+
if [ "$RSYNC_EXIT" -ne 0 ] && [ "$RSYNC_EXIT" -ne 24 ]; then
130+
fail "$RSYNC_EXIT" "rsync failed"
131+
fi
132+
133+
# ---------- Final sync ----------
134+
135+
write_status "syncing" 96 "Finalise copy..."
136+
137+
# Sync to ensure all data is written to stick to avoid corruption
138+
139+
sync
140+
141+
write_status "done" 100 "Done. You can remove the USB drive now."
142+
143+
# Unmount only if we mounted it
144+
145+
if [[ "${MNT}" == "/mnt/usbdrive" ]]; then
146+
147+
# Give some time to ensure all writes are done
148+
149+
sleep 1
150+
sudo umount /mnt/usbdrive || sudo umount -l /mnt/usbdrive
151+
fi
152+
153+
exit 0
154+
155+
```
156+
157+
Setup udev rule
158+
159+
```
160+
echo 'ACTION=="add", SUBSYSTEM=="block", ENV{DEVTYPE}=="partition", ENV{ID_FS_TYPE}=="ntfs", RUN+="/usr/local/bin/fotobox-copy.sh %k"' | sudo tee /etc/udev/rules.d/99-fotobox-autocopy.rules
161+
162+
sudo udevadm control --reload-rules
163+
sudo udevadm trigger
164+
```
165+
166+
Create status file and set permissions
167+
168+
```
169+
sudo touch /var/www/html/private/copystatus.json
170+
sudo chown root:www-data /var/www/html/private/copystatus.json
171+
sudo chmod 664 /var/www/html/private/copystatus.json
172+
sudo chown -R www-data:www-data /var/www/html/private
173+
```
174+
175+
Create status page to monitor progress in folder `private`
176+
`sudo -u www-data nano /var/www/html/private/status.php`
177+
178+
```
179+
<?php
180+
?>
181+
<!doctype html>
182+
<html lang="de">
183+
<head>
184+
<meta charset="utf-8">
185+
<meta name="viewport" content="width=device-width, initial-scale=1">
186+
<title>Kopiervorgang…</title>
187+
</head>
188+
<body style="
189+
background-image: url('/private/images/background/Copy_Background.jpg');
190+
background-size: cover;
191+
background-position: center;
192+
background-repeat: no-repeat;
193+
margin: 0;
194+
padding: 0;
195+
overflow-x:hidden;
196+
overflow-y:hidden;">
197+
198+
<div id="wrap" style="min-height:100vh; display:flex; align-items:center; justify-content:center; padding:20px;">
199+
<div style="background:#fff; padding:20px; border-radius:12px; width:min(520px,90vw); box-shadow:0 10px 30px rgba(0,0,0,.3);">
200+
<div id="copyMsg" style="margin-bottom:10px;">Warte auf Status…</div>
201+
<div style="height:18px; background:#eee; border-radius:10px; overflow:hidden;">
202+
<div id="copyBar" style="height:100%; width:0%; background:#3b82f6;"></div>
203+
</div>
204+
<div id="copyPct" style="margin-top:8px; font-size:14px; opacity:.8;">0%</div>
205+
</div>
206+
</div>
207+
208+
<script>
209+
(async function(){
210+
const bar = document.getElementById('copyBar');
211+
const msg = document.getElementById('copyMsg');
212+
const pct = document.getElementById('copyPct');
213+
214+
// Passe ggf. den Pfad an:
215+
const STATUS_URL = '/private/copystatus.json';
216+
const BACK_URL = '../index.php';
217+
218+
async function poll(){
219+
try {
220+
const r = await fetch(STATUS_URL, { cache: 'no-store' });
221+
if(!r.ok) throw new Error('Status page not reachable: ' + r.status);
222+
const s = await r.json();
223+
224+
const state = (s.state || '').toLowerCase();
225+
const p = Math.max(0, Math.min(100, Number(s.percent ?? 0)));
226+
227+
bar.style.width = p + '%';
228+
msg.textContent = s.message || (state ? ('Status: ' + state) : 'Kopiere ');
229+
pct.textContent = p + '%';
230+
231+
if (state === 'done') {
232+
bar.style.width = '100%';
233+
msg.textContent = s.message || 'Fertig.';
234+
pct.textContent = '100%';
235+
setTimeout(() => window.location.href = BACK_URL, 3000);
236+
return;
237+
}
238+
239+
// Optional: handle error state
240+
if (state === 'error') {
241+
msg.textContent = s.message || 'Copy error.';
242+
return;
243+
}
244+
245+
} catch(e) {
246+
msg.textContent = 'No status available.';
247+
// do not jump to status page, it will flicker otherwise
248+
} finally {
249+
setTimeout(poll, 400);
250+
}
251+
}
252+
253+
poll();
254+
})();
255+
</script>
256+
</body>
257+
</html>
258+
```
259+
260+
Create auto-refresh script to redirect to status page when copy is running
261+
`sudo nano /var/www/html/private/copyrun.js`
262+
263+
```
264+
(async function(){
265+
const STATUS_URL = '/private/copystatus.json';
266+
const STATUS_PAGE = '/private/status.php';
267+
268+
async function check(){
269+
try {
270+
const r = await fetch(STATUS_URL + '?t=' + Date.now(), { cache: 'no-store' });
271+
if (!r.ok) throw new Error('no status');
272+
const s = await r.json();
273+
const state = (s.state || '').toLowerCase();
274+
275+
if (state === 'starting' || state === 'copying') {
276+
// >>> HIER passiert der Sprung <<<
277+
window.location.href = STATUS_PAGE;
278+
return;
279+
}
280+
} catch(e) {
281+
// no status file or error - do nothing
282+
}
283+
setTimeout(check, 600);
284+
}
285+
286+
check();
287+
})();
288+
```
289+
290+

docs/scripts/index.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## Scripts
2+
3+
- [Auto copy to USB (NTFS)](auto-copy-to-usb-ntfs.md)

mkdocs_remote.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ nav:
5454
- Sounds: customizing/sounds.md
5555
- Themes: customizing/themes.md
5656
- CSS styles: customizing/styles.md
57+
- Scripts: scripts/index.md
5758
- Community:
5859
- Chat on Telegram: https://t.me/PhotoboothGroup
5960
- Contributor Covenant Code of Conduct: code_of_conduct.md

0 commit comments

Comments
 (0)