From 387c26e218f7bf10819d7bed657f7f62b64e18ce Mon Sep 17 00:00:00 2001 From: Evgeny Zinoviev Date: Sun, 11 Jun 2023 03:30:12 +0300 Subject: move some scripts around, delete obsolete ones --- bin/ipcam_capture.sh | 119 ++++++++ bin/ipcam_motion_worker.sh | 327 +++++++++++++++++++++ bin/ipcam_rtsp2hls.sh | 127 ++++++++ include/bash/include.bash | 130 ++++++++ .../homekit_ipcam_capture_restart.sh | 7 + .../homekit_ipcam_rtsp2hls_restart.sh | 8 + .../homekit_make_netns_per_upstream.sh | 38 +++ .../homekit_sunxi_h3_i2c_reset.sh | 25 ++ .../homekit_sunxi_setup_amixer.sh | 114 +++++++ .../homekit_sync_recordings_to_remote.sh | 72 +++++ misc/scripts/ipcam_capture_restart.sh | 7 - misc/scripts/ipcam_rtsp2hls_restart.sh | 8 - misc/scripts/make_netns_per_upstream.sh | 38 --- misc/scripts/remote_server/clickhouse_backup.sh | 31 ++ .../scripts/remote_server/remove_old_recordings.sh | 5 + systemd/ipcam_capture@.service | 2 +- systemd/ipcam_rtsp2hls@.service | 2 +- tools/clickhouse-backup.sh | 31 -- tools/ipcam_capture.sh | 119 -------- tools/ipcam_motion_worker.sh | 327 --------------------- tools/ipcam_rtsp2hls.sh | 127 -------- tools/lib.bash | 130 -------- tools/process-motion-timecodes.py | 61 ---- tools/remove-old-recordings.sh | 5 - tools/rotate-video.sh | 2 +- tools/sunxi-h3-i2c-reset.sh | 25 -- tools/sunxi-setup-amixer.sh | 114 ------- tools/sync-recordings-to-remote.sh | 72 ----- tools/video-util.sh | 2 +- 29 files changed, 1007 insertions(+), 1068 deletions(-) create mode 100755 bin/ipcam_capture.sh create mode 100755 bin/ipcam_motion_worker.sh create mode 100755 bin/ipcam_rtsp2hls.sh create mode 100644 include/bash/include.bash create mode 100644 misc/scripts/home_linux_boards/homekit_ipcam_capture_restart.sh create mode 100644 misc/scripts/home_linux_boards/homekit_ipcam_rtsp2hls_restart.sh create mode 100644 misc/scripts/home_linux_boards/homekit_make_netns_per_upstream.sh create mode 100644 misc/scripts/home_linux_boards/homekit_sunxi_h3_i2c_reset.sh create mode 100755 misc/scripts/home_linux_boards/homekit_sunxi_setup_amixer.sh create mode 100755 misc/scripts/home_linux_boards/homekit_sync_recordings_to_remote.sh delete mode 100644 misc/scripts/ipcam_capture_restart.sh delete mode 100644 misc/scripts/ipcam_rtsp2hls_restart.sh delete mode 100644 misc/scripts/make_netns_per_upstream.sh create mode 100644 misc/scripts/remote_server/clickhouse_backup.sh create mode 100644 misc/scripts/remote_server/remove_old_recordings.sh delete mode 100644 tools/clickhouse-backup.sh delete mode 100755 tools/ipcam_capture.sh delete mode 100755 tools/ipcam_motion_worker.sh delete mode 100755 tools/ipcam_rtsp2hls.sh delete mode 100644 tools/lib.bash delete mode 100755 tools/process-motion-timecodes.py delete mode 100644 tools/remove-old-recordings.sh delete mode 100644 tools/sunxi-h3-i2c-reset.sh delete mode 100755 tools/sunxi-setup-amixer.sh delete mode 100755 tools/sync-recordings-to-remote.sh diff --git a/bin/ipcam_capture.sh b/bin/ipcam_capture.sh new file mode 100755 index 0000000..b97c856 --- /dev/null +++ b/bin/ipcam_capture.sh @@ -0,0 +1,119 @@ +#!/bin/bash + +PROGNAME="$0" +PORT=554 +IP= +CREDS= +DEBUG=0 +CHANNEL=1 +FORCE_UDP=0 +FORCE_TCP=0 +EXTENSION="mp4" + +die() { + echo >&2 "error: $@" + exit 1 +} + +usage() { + cat </dev/null && pwd )" +PROGNAME="$0" + +. "$DIR/../include/bash/include.bash" + +curl_opts="-s --connect-timeout 10 --retry 5 --max-time 180 --retry-delay 0 --retry-max-time 180" +allow_multiple= +fetch_limit=10 + +config= +config_camera= +is_remote= +api_url= + +dvr_scan_path="$HOME/.local/bin/dvr-scan" +fs_root="/var/ipcam_motion_fs" +fs_max_filesize=146800640 + +declare -A config=() + +usage() { + cat </dev/null || die "failed to change to ${fs_root}" + touch tmp || die "directory '${fs_root}' is not writable" + rm tmp + + [ -f "video.mp4" ] && { + echowarn "video.mp4 already exists in ${fs_root}, removing.." + rm "video.mp4" + } + fi + + while read line; do + words=($line) + file=${words[0]} + size=${words[1]} + camera=${words[2]} + + debug "next video: cam=$camera file=$file" + + read_camera_motion_config "$camera" +# dump_config + + if [ "$is_remote" = "0" ]; then + local_recs_dir="$(get_recordings_dir "$camera")" + + debug "[$camera] processing $file..." + + tc=$(do_motion "$camera" "${local_recs_dir}/$file") + debug "[$camera] $file: timecodes=$tc" + + report_timecodes "$camera" "$file" "$tc" + else + if (( size > fs_max_filesize )); then + echoerr "[$camera] won't download $file, size exceeds fs_max_filesize ($size > ${fs_max_filesize})" + report_failure "$camera" "$file" "too large file" + continue + fi + + url="${api_url}/api/recordings/${camera}/download/${file}" + debug "[$camera] downloading $url..." + + if ! download "$url" "video.mp4"; then + echoerr "[$camera] failed to download $file" + report_failure "$camera" "$file" "download error" + continue + fi + + tc=$(do_motion "$camera" "video.mp4") + debug "[$camera] $file: timecodes=$tc" + + report_timecodes "$camera" "$file" "$tc" + + rm "video.mp4" + fi + done < <(get_recordings_list) + + if [ "$is_remote" = "1" ]; then popd >/dev/null; fi +} + +do_motion() { + local camera="$1" + local input="$2" + local tc + + local timecodes=() + + time_start + while read line; do + if ! [[ "$line" =~ ^#.* ]]; then + tc="$(do_dvr_scan "$input" "$line")" + if [ -n "$tc" ]; then + timecodes+=("$tc") + fi + fi + done < <(get_camera_roi_config "$camera") + + debug "[$camera] do_motion: finished in $(time_elapsed)s" + + timecodes="$(echo "${timecodes[@]}" | sed 's/ */ /g' | xargs)" + timecodes="${timecodes// /,}" + + echo "$timecodes" +} + +dvr_scan() { + "${dvr_scan_path}" "$@" +} + +do_dvr_scan() { + local input="$1" + local args= + + if [ ! -z "$2" ]; then + args="-roi $2" + echoinfo "dvr_scan(${BOLD}${input}${RST}${CYAN}): roi=($2), mt=${config[threshold]}" + else + echoinfo "dvr_scan(${BOLD}${input}${RST}${CYAN}): no roi, mt=${config[threshold]}" + fi + + dvr_scan -q -i "$input" -so \ + --min-event-length ${config[min_event_length]} \ + -df ${config[downscale_factor]} \ + --frame-skip ${config[frame_skip]} \ + -t ${config[threshold]} $args | tail -1 +} + +[[ $# -lt 1 ]] && usage + +while [[ $# -gt 0 ]]; do + case $1 in + -L|--fetch-limit) + fetch_limit="$2" + shift; shift + ;; + + --allow-multiple) + allow_multiple=1 + shift + ;; + + --remote) + is_remote=1 + shift + ;; + + --local) + is_remote=0 + shift + ;; + + --dvr-scan-path) + dvr_scan_path="$2" + shift; shift + ;; + + --fs-root) + fs_root="$2" + shift; shift + ;; + + --fs-max-filesize) + fs_max_filesize="$2" + shift; shift + ;; + + --api-url) + api_url="$2" + shift; shift + ;; + + -v) + VERBOSE=1 + shift + ;; + + -vx) + VERBOSE=1 + set -x + shift + ;; + + *) + die "unrecognized argument '$1'" + exit 1 + ;; + esac +done + +if [ -z "$allow_multiple" ] && pidof -o %PPID -x "$(basename "${BASH_SOURCE[0]}")" >/dev/null; then + die "process already running" +fi + +[ -z "$is_remote" ] && die "either --remote or --local is required" +[ -z "$api_url" ] && die "--api-url is required" + +process_queue \ No newline at end of file diff --git a/bin/ipcam_rtsp2hls.sh b/bin/ipcam_rtsp2hls.sh new file mode 100755 index 0000000..c321820 --- /dev/null +++ b/bin/ipcam_rtsp2hls.sh @@ -0,0 +1,127 @@ +#!/bin/bash + +PROGNAME="$0" +OUTDIR=/var/ipcamfs # should be tmpfs +PORT=554 +NAME= +IP= +USER= +PASSWORD= +DEBUG=0 +CHANNEL=1 +FORCE_UDP=0 +FORCE_TCP=0 +CUSTOM_PATH= + +die() { + echo >&2 "error: $@" + exit 1 +} + +usage() { + cat <&2 echo "${CYAN}$@${RST}" +} + +echoerr() { + >&2 echo "${RED}${BOLD}error:${RST}${RED} $@${RST}" +} + +echowarn() { + >&2 echo "${YELLOW}${BOLD}warning:${RST}${YELLOW} $@${RST}" +} + +die() { + echoerr "$@" + exit 1 +} + +debug() { + if [ -n "$VERBOSE" ]; then + >&2 echo "$@" + fi +} + + +# measuring executing time +# ------------------------ + +__time_started= + +time_start() { + __time_started=$(date +%s) +} + +time_elapsed() { + local fin=$(date +%s) + echo $(( fin - __time_started )) +} + + +# config parsing +# -------------- + +read_config() { + local config_file="$1" + local dst="$2" + + [ -f "$config_file" ] || die "read_config: $config_file: no such file" + + local n=0 + local failed= + local key + local value + + while read line; do + n=$(( n+1 )) + + # skip empty lines or comments + if [ -z "$line" ] || [[ "$line" =~ ^#.* ]]; then + continue + fi + + if [[ $line = *"="* ]]; then + key="${line%%=*}" + value="${line#*=}" + eval "$dst[$key]=\"$value\"" + else + echoerr "config: invalid line $n" + failed=1 + fi + done < <(cat "$config_file") + + [ -z "$failed" ] +} + +check_config() { + local var="$1" + local keys="$2" + + local failed= + + for key in $keys; do + if [ -z "$(eval "echo -n \${$var[$key]}")" ]; then + echoerr "config: ${BOLD}${key}${RST}${RED} is missing" + failed=1 + fi + done + + [ -z "$failed" ] +} + + +# other functions +# --------------- + +installed() { + command -v "$1" > /dev/null + return $? +} + +download() { + local source="$1" + local target="$2" + + if installed curl; then + curl -f -s -o "$target" "$source" + elif installed wget; then + wget -q -O "$target" "$source" + else + die "neither curl nor wget found, can't proceed" + fi +} + +file_in_use() { + [ -n "$(lsof "$1")" ] +} + +file_mtime() { + stat -c %Y "$1" +} diff --git a/misc/scripts/home_linux_boards/homekit_ipcam_capture_restart.sh b/misc/scripts/home_linux_boards/homekit_ipcam_capture_restart.sh new file mode 100644 index 0000000..85144da --- /dev/null +++ b/misc/scripts/home_linux_boards/homekit_ipcam_capture_restart.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +for f in $(ls /etc/ipcam_capture.conf.d/ | xargs); do + camera="${f/.conf/}" + echo "restarting $camera" + systemctl restart ipcam_capture@${camera} +done \ No newline at end of file diff --git a/misc/scripts/home_linux_boards/homekit_ipcam_rtsp2hls_restart.sh b/misc/scripts/home_linux_boards/homekit_ipcam_rtsp2hls_restart.sh new file mode 100644 index 0000000..61ee623 --- /dev/null +++ b/misc/scripts/home_linux_boards/homekit_ipcam_rtsp2hls_restart.sh @@ -0,0 +1,8 @@ +#!/bin/bash +cd /etc/ipcam_rtsp2hls.conf.d/ +for f in *-low.conf; do + f=${f/-low.conf/} + echo "restarting $f" + systemctl restart ipcam_rtsp2hls@${f} + systemctl restart ipcam_rtsp2hls@${f}-low +done diff --git a/misc/scripts/home_linux_boards/homekit_make_netns_per_upstream.sh b/misc/scripts/home_linux_boards/homekit_make_netns_per_upstream.sh new file mode 100644 index 0000000..fb152fa --- /dev/null +++ b/misc/scripts/home_linux_boards/homekit_make_netns_per_upstream.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +set -x +set -e + +get_default_iface() { + ip -4 r show default | awk '{print $5}' +} + +declare -A UPSTREAMS=( + [mtsil]=102 + [mtsazov]=100 + [rtazov]=101 +) + +for name in "${!UPSTREAMS[@]}"; do + mark=${UPSTREAMS[$name]} + veth_addr=10.${mark}.1.1 + vpeer_addr=10.${mark}.1.2 + veth_if=veth${name} + vpeer_if=vpeer${name} + + ip netns add $name + ip link add $veth_if type veth peer name $vpeer_if + ip link set $vpeer_if netns $name + ip addr add $veth_addr/24 dev $veth_if + ip link set $veth_if up + + ip netns exec $name ip addr add $vpeer_addr/24 dev $vpeer_if + ip netns exec $name ip link set $vpeer_if up + ip netns exec $name ip link set lo up + ip netns exec $name ip route add default via $veth_addr + + iptables -t mangle -A PREROUTING -s $vpeer_addr/24 -j MARK --set-mark $mark + iptables -t nat -A POSTROUTING -s $vpeer_addr/24 -o "$(get_default_iface)" -j MASQUERADE +done + +sysctl net.ipv4.ip_forward=1 diff --git a/misc/scripts/home_linux_boards/homekit_sunxi_h3_i2c_reset.sh b/misc/scripts/home_linux_boards/homekit_sunxi_h3_i2c_reset.sh new file mode 100644 index 0000000..e654dfb --- /dev/null +++ b/misc/scripts/home_linux_boards/homekit_sunxi_h3_i2c_reset.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +devices="1c2ac00.i2c 1c2b000.i2c" +pins="8 9 28 30" +driver_path="/sys/bus/platform/drivers/mv64xxx_i2c" + +driver_unbind() { + echo -n "$1" > "$driver_path/unbind" +} + +driver_bind() { + echo -n "$1" > "$driver_path/bind" +} + +for dev in $devices; do driver_unbind "$dev"; done +echo "unbind done" + +for pin in pins; do + gpio mode $pin out + gpio write $pin 0 +done +echo "gpio reset done" + +for dev in $devices; do driver_bind "$dev"; done +echo "bind done" \ No newline at end of file diff --git a/misc/scripts/home_linux_boards/homekit_sunxi_setup_amixer.sh b/misc/scripts/home_linux_boards/homekit_sunxi_setup_amixer.sh new file mode 100755 index 0000000..5746514 --- /dev/null +++ b/misc/scripts/home_linux_boards/homekit_sunxi_setup_amixer.sh @@ -0,0 +1,114 @@ +#!/bin/bash + +amixer() { + /usr/bin/amixer "$@" +} + +setup_opi_pc2() { + for v in unmute cap; do + amixer set "Line In" $v + amixer set "Mic1" $v + amixer set "Mic2" $v + done + + for k in "Mic1 Boost" "Line In" "Mic1" "Mic2 Boost" "Mic2"; do + amixer set "$k" "86%" + done +} + +setup_opi_one() { + for v in unmute cap; do + amixer set "Line In" $v + amixer set "Mic1" $v + done + + for k in "Mic1 Boost" "Line In" "Mic1"; do + amixer set "$k" "86%" + done +} + +setup_opi3lts() { + switches=( + "Left DAC Mixer ADCL" + "Left DAC Mixer I2SDACL" + "Left I2S Mixer ADCL" + "Left I2S Mixer I2SDACL" + "Left Input Mixer LINEINL" + "Left Input Mixer MIC1" + "Left Input Mixer MIC2" + "Left Input Mixer OMixerL" + "Left Input Mixer OMixerR" + "Left Input Mixer PhoneN" + "Left Input Mixer PhonePN" + "Left Output Mixer DACL" + "Left Output Mixer DACR" + "Left Output Mixer LINEINL" + "Left Output Mixer MIC1" + "Left Output Mixer MIC2" + "Left Output Mixer PhoneN" + "Left Output Mixer PhonePN" + "Right DAC Mixer ADCR" + "Right DAC Mixer I2SDACR" + "Right I2S Mixer ADCR" + "Right I2S Mixer I2SDACR" + "Right Input Mixer LINEINR" + "Right Input Mixer MIC1" + "Right Input Mixer MIC2" + "Right Input Mixer OMixerL" + "Right Input Mixer OMixerR" + "Right Input Mixer PhoneP" + "Right Input Mixer PhonePN" + "Right Output Mixer DACL" + "Right Output Mixer DACR" + "Right Output Mixer LINEINR" + "Right Output Mixer MIC1" + "Right Output Mixer MIC2" + "Right Output Mixer PhoneP" + "Right Output Mixer PhonePN" + ) + for v in "${switches[@]}"; do + value=on + case "$v" in + *Input*) + value=on + ;; + *Output*) + value=off + ;; + esac + amixer set "$v" $value + done + + to_mute=( + "I2S Mixer ADC" + "I2S Mixer DAC" + "ADC Input" + "DAC Mixer ADC" + "DAC Mxier DAC" # this is not a typo + ) + for v in "${to_mute[@]}"; do + amixer set "$v" "0%" + done + + amixer set "Master" "100%" + amixer set "MIC1 Boost" "100%" + amixer set "MIC2 Boost" "100%" + amixer set "Line Out Mixer" "86%" + amixer set "MIC Out Mixer" "71%" +} + +device="$(tr -d '\0' < /sys/firmware/devicetree/base/model)" +case "$device" in + *"Orange Pi PC 2") + setup_opi_pc2 + ;; + *"Orange Pi One"|*"Orange Pi Lite") + setup_opi_one + ;; + *"OrangePi 3 LTS") + setup_opi3lts + ;; + *) + >&2 echo "error: unidentified device: $device" + ;; +esac diff --git a/misc/scripts/home_linux_boards/homekit_sync_recordings_to_remote.sh b/misc/scripts/home_linux_boards/homekit_sync_recordings_to_remote.sh new file mode 100755 index 0000000..cf979d1 --- /dev/null +++ b/misc/scripts/home_linux_boards/homekit_sync_recordings_to_remote.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +PROGNAME="$0" +NODE_CONFIG="/etc/sound_node.toml" +REMOTE_USER=user +REMOTE_SERVER=solarmon.ru +REMOTE_DIRECTORY=/var/recordings + +set -e + +echoerr() { + >&2 echo "error: $@" +} + +echowarn() { + >&2 echo "warning: $@" +} + +telegram_alert() { + if [ -z "$TG_TOKEN" ] || [ -z "$TG_CHAT_ID" ]; then return; fi + curl -X POST \ + -F "chat_id=${TG_CHAT_ID}" \ + -F "text=$1" \ + "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" +} + +fatal() { + echoerr "$@" + telegram_alert "$PROGNAME: $@" + exit 1 +} + +get_config_var() { + local varname="$1" + cat "$NODE_CONFIG" | grep "^$varname = \"" | awk '{print $3}' | tr -d '"' +} + +get_mp3_count() { + find "$LOCAL_DIR" -mindepth 1 -type f -name "*.mp3" -printf x | wc -c +} + +[ -z "$TG_TOKEN" ] && echowarn "TG_TOKEN is not set" +[ -z "$TG_CHAT_ID" ] && echowarn "TG_CHAT_ID is not set" + +NODE_NAME=$(get_config_var name) +LOCAL_DIR=$(get_config_var storage) + +[ -z "$NODE_NAME" ] && fatal "failed to parse NODE_NAME" +[ -z "$LOCAL_DIR" ] && fatal "failed to parse LOCAL_DIR" + +[ -d "$LOCAL_DIR" ] || fatal "$LOCAL_DIR is not a directory" + +COUNT=$(get_mp3_count) +(( $COUNT < 1 )) && { + echo "seems there's nothing to sync" + exit +} + +cd "$LOCAL_DIR" || fatal "failed to change to $LOCAL_DIR" + +rsync -azPv -e "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=ERROR" \ + *.mp3 \ + ${REMOTE_USER}@${REMOTE_SERVER}:"${REMOTE_DIRECTORY}/${NODE_NAME}/" \ + --exclude temp.mp3 + +RC=$? + +if [ $RC -eq 0 ]; then + find "$LOCAL_DIR" -name "*.mp3" -type f -mmin +1440 -delete || fatal "find failed to delete old files" +else + fatal "failed to rsync: code $RC" +fi diff --git a/misc/scripts/ipcam_capture_restart.sh b/misc/scripts/ipcam_capture_restart.sh deleted file mode 100644 index 85144da..0000000 --- a/misc/scripts/ipcam_capture_restart.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -for f in $(ls /etc/ipcam_capture.conf.d/ | xargs); do - camera="${f/.conf/}" - echo "restarting $camera" - systemctl restart ipcam_capture@${camera} -done \ No newline at end of file diff --git a/misc/scripts/ipcam_rtsp2hls_restart.sh b/misc/scripts/ipcam_rtsp2hls_restart.sh deleted file mode 100644 index 61ee623..0000000 --- a/misc/scripts/ipcam_rtsp2hls_restart.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -cd /etc/ipcam_rtsp2hls.conf.d/ -for f in *-low.conf; do - f=${f/-low.conf/} - echo "restarting $f" - systemctl restart ipcam_rtsp2hls@${f} - systemctl restart ipcam_rtsp2hls@${f}-low -done diff --git a/misc/scripts/make_netns_per_upstream.sh b/misc/scripts/make_netns_per_upstream.sh deleted file mode 100644 index fb152fa..0000000 --- a/misc/scripts/make_netns_per_upstream.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -set -x -set -e - -get_default_iface() { - ip -4 r show default | awk '{print $5}' -} - -declare -A UPSTREAMS=( - [mtsil]=102 - [mtsazov]=100 - [rtazov]=101 -) - -for name in "${!UPSTREAMS[@]}"; do - mark=${UPSTREAMS[$name]} - veth_addr=10.${mark}.1.1 - vpeer_addr=10.${mark}.1.2 - veth_if=veth${name} - vpeer_if=vpeer${name} - - ip netns add $name - ip link add $veth_if type veth peer name $vpeer_if - ip link set $vpeer_if netns $name - ip addr add $veth_addr/24 dev $veth_if - ip link set $veth_if up - - ip netns exec $name ip addr add $vpeer_addr/24 dev $vpeer_if - ip netns exec $name ip link set $vpeer_if up - ip netns exec $name ip link set lo up - ip netns exec $name ip route add default via $veth_addr - - iptables -t mangle -A PREROUTING -s $vpeer_addr/24 -j MARK --set-mark $mark - iptables -t nat -A POSTROUTING -s $vpeer_addr/24 -o "$(get_default_iface)" -j MASQUERADE -done - -sysctl net.ipv4.ip_forward=1 diff --git a/misc/scripts/remote_server/clickhouse_backup.sh b/misc/scripts/remote_server/clickhouse_backup.sh new file mode 100644 index 0000000..6e938e4 --- /dev/null +++ b/misc/scripts/remote_server/clickhouse_backup.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +DIR=/var/lib/clickhouse/backup +MAX_COUNT=3 +NAME=backup_$(date -u +%Y-%m-%d) + +create() { + local name="$1" + clickhouse-backup create "$name" +} + +del() { + local name="$1" + clickhouse-backup delete local "$name" +} + +# create a backup +create "$NAME" + +# compress backup +cd "$DIR" +tar czvf $NAME.tar.gz $NAME + +# delete uncompressed files +del "$NAME" + +# delete old backups +for file in $(ls -t "${DIR}" | tail -n +$(( MAX_COUNT+1 ))); do + echo "removing $file..." + rm "$file" +done \ No newline at end of file diff --git a/misc/scripts/remote_server/remove_old_recordings.sh b/misc/scripts/remote_server/remove_old_recordings.sh new file mode 100644 index 0000000..d376572 --- /dev/null +++ b/misc/scripts/remote_server/remove_old_recordings.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +# to be launched by cron on remote server + +find /var/recordings -type f -mtime +14 -delete diff --git a/systemd/ipcam_capture@.service b/systemd/ipcam_capture@.service index b1c363e..e195231 100644 --- a/systemd/ipcam_capture@.service +++ b/systemd/ipcam_capture@.service @@ -8,7 +8,7 @@ RestartSec=3 User=user Group=user EnvironmentFile=/etc/ipcam_capture.conf.d/%i.conf -ExecStart=/home/user/homekit/tools/ipcam_capture.sh --outdir $OUTDIR --creds $CREDS --ip $IP --port $PORT $ARGS +ExecStart=/home/user/homekit/bin/ipcam_capture.sh --outdir $OUTDIR --creds $CREDS --ip $IP --port $PORT $ARGS Restart=always [Install] diff --git a/systemd/ipcam_rtsp2hls@.service b/systemd/ipcam_rtsp2hls@.service index efcdd6a..9ce6cca 100644 --- a/systemd/ipcam_rtsp2hls@.service +++ b/systemd/ipcam_rtsp2hls@.service @@ -8,7 +8,7 @@ RestartSec=3 User=user Group=user EnvironmentFile=/etc/ipcam_rtsp2hls.conf.d/%i.conf -ExecStart=/home/user/homekit/tools/ipcam_rtsp2hls.sh --name %i --user $USER --password $PASSWORD --ip $IP --port $PORT $ARGS +ExecStart=/home/user/homekit/bin/ipcam_rtsp2hls.sh --name %i --user $USER --password $PASSWORD --ip $IP --port $PORT $ARGS Restart=on-failure RestartSec=3 diff --git a/tools/clickhouse-backup.sh b/tools/clickhouse-backup.sh deleted file mode 100644 index 6e938e4..0000000 --- a/tools/clickhouse-backup.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash - -DIR=/var/lib/clickhouse/backup -MAX_COUNT=3 -NAME=backup_$(date -u +%Y-%m-%d) - -create() { - local name="$1" - clickhouse-backup create "$name" -} - -del() { - local name="$1" - clickhouse-backup delete local "$name" -} - -# create a backup -create "$NAME" - -# compress backup -cd "$DIR" -tar czvf $NAME.tar.gz $NAME - -# delete uncompressed files -del "$NAME" - -# delete old backups -for file in $(ls -t "${DIR}" | tail -n +$(( MAX_COUNT+1 ))); do - echo "removing $file..." - rm "$file" -done \ No newline at end of file diff --git a/tools/ipcam_capture.sh b/tools/ipcam_capture.sh deleted file mode 100755 index 08b9093..0000000 --- a/tools/ipcam_capture.sh +++ /dev/null @@ -1,119 +0,0 @@ -#!/bin/bash - -PROGNAME="$0" -PORT=554 -IP= -CREDS= -DEBUG=0 -CHANNEL=1 -FORCE_UDP=0 -FORCE_TCP=0 -EXTENSION="mp4" - -die() { - echo >&2 "error: $@" - exit 1 -} - -usage() { - cat </dev/null && pwd )" -PROGNAME="$0" - -. "$DIR/lib.bash" - -curl_opts="-s --connect-timeout 10 --retry 5 --max-time 180 --retry-delay 0 --retry-max-time 180" -allow_multiple= -fetch_limit=10 - -config= -config_camera= -is_remote= -api_url= - -dvr_scan_path="$HOME/.local/bin/dvr-scan" -fs_root="/var/ipcam_motion_fs" -fs_max_filesize=146800640 - -declare -A config=() - -usage() { - cat </dev/null || die "failed to change to ${fs_root}" - touch tmp || die "directory '${fs_root}' is not writable" - rm tmp - - [ -f "video.mp4" ] && { - echowarn "video.mp4 already exists in ${fs_root}, removing.." - rm "video.mp4" - } - fi - - while read line; do - words=($line) - file=${words[0]} - size=${words[1]} - camera=${words[2]} - - debug "next video: cam=$camera file=$file" - - read_camera_motion_config "$camera" -# dump_config - - if [ "$is_remote" = "0" ]; then - local_recs_dir="$(get_recordings_dir "$camera")" - - debug "[$camera] processing $file..." - - tc=$(do_motion "$camera" "${local_recs_dir}/$file") - debug "[$camera] $file: timecodes=$tc" - - report_timecodes "$camera" "$file" "$tc" - else - if (( size > fs_max_filesize )); then - echoerr "[$camera] won't download $file, size exceeds fs_max_filesize ($size > ${fs_max_filesize})" - report_failure "$camera" "$file" "too large file" - continue - fi - - url="${api_url}/api/recordings/${camera}/download/${file}" - debug "[$camera] downloading $url..." - - if ! download "$url" "video.mp4"; then - echoerr "[$camera] failed to download $file" - report_failure "$camera" "$file" "download error" - continue - fi - - tc=$(do_motion "$camera" "video.mp4") - debug "[$camera] $file: timecodes=$tc" - - report_timecodes "$camera" "$file" "$tc" - - rm "video.mp4" - fi - done < <(get_recordings_list) - - if [ "$is_remote" = "1" ]; then popd >/dev/null; fi -} - -do_motion() { - local camera="$1" - local input="$2" - local tc - - local timecodes=() - - time_start - while read line; do - if ! [[ "$line" =~ ^#.* ]]; then - tc="$(do_dvr_scan "$input" "$line")" - if [ -n "$tc" ]; then - timecodes+=("$tc") - fi - fi - done < <(get_camera_roi_config "$camera") - - debug "[$camera] do_motion: finished in $(time_elapsed)s" - - timecodes="$(echo "${timecodes[@]}" | sed 's/ */ /g' | xargs)" - timecodes="${timecodes// /,}" - - echo "$timecodes" -} - -dvr_scan() { - "${dvr_scan_path}" "$@" -} - -do_dvr_scan() { - local input="$1" - local args= - - if [ ! -z "$2" ]; then - args="-roi $2" - echoinfo "dvr_scan(${BOLD}${input}${RST}${CYAN}): roi=($2), mt=${config[threshold]}" - else - echoinfo "dvr_scan(${BOLD}${input}${RST}${CYAN}): no roi, mt=${config[threshold]}" - fi - - dvr_scan -q -i "$input" -so \ - --min-event-length ${config[min_event_length]} \ - -df ${config[downscale_factor]} \ - --frame-skip ${config[frame_skip]} \ - -t ${config[threshold]} $args | tail -1 -} - -[[ $# -lt 1 ]] && usage - -while [[ $# -gt 0 ]]; do - case $1 in - -L|--fetch-limit) - fetch_limit="$2" - shift; shift - ;; - - --allow-multiple) - allow_multiple=1 - shift - ;; - - --remote) - is_remote=1 - shift - ;; - - --local) - is_remote=0 - shift - ;; - - --dvr-scan-path) - dvr_scan_path="$2" - shift; shift - ;; - - --fs-root) - fs_root="$2" - shift; shift - ;; - - --fs-max-filesize) - fs_max_filesize="$2" - shift; shift - ;; - - --api-url) - api_url="$2" - shift; shift - ;; - - -v) - VERBOSE=1 - shift - ;; - - -vx) - VERBOSE=1 - set -x - shift - ;; - - *) - die "unrecognized argument '$1'" - exit 1 - ;; - esac -done - -if [ -z "$allow_multiple" ] && pidof -o %PPID -x "$(basename "${BASH_SOURCE[0]}")" >/dev/null; then - die "process already running" -fi - -[ -z "$is_remote" ] && die "either --remote or --local is required" -[ -z "$api_url" ] && die "--api-url is required" - -process_queue \ No newline at end of file diff --git a/tools/ipcam_rtsp2hls.sh b/tools/ipcam_rtsp2hls.sh deleted file mode 100755 index c321820..0000000 --- a/tools/ipcam_rtsp2hls.sh +++ /dev/null @@ -1,127 +0,0 @@ -#!/bin/bash - -PROGNAME="$0" -OUTDIR=/var/ipcamfs # should be tmpfs -PORT=554 -NAME= -IP= -USER= -PASSWORD= -DEBUG=0 -CHANNEL=1 -FORCE_UDP=0 -FORCE_TCP=0 -CUSTOM_PATH= - -die() { - echo >&2 "error: $@" - exit 1 -} - -usage() { - cat <&2 echo "${CYAN}$@${RST}" -} - -echoerr() { - >&2 echo "${RED}${BOLD}error:${RST}${RED} $@${RST}" -} - -echowarn() { - >&2 echo "${YELLOW}${BOLD}warning:${RST}${YELLOW} $@${RST}" -} - -die() { - echoerr "$@" - exit 1 -} - -debug() { - if [ -n "$VERBOSE" ]; then - >&2 echo "$@" - fi -} - - -# measuring executing time -# ------------------------ - -__time_started= - -time_start() { - __time_started=$(date +%s) -} - -time_elapsed() { - local fin=$(date +%s) - echo $(( fin - __time_started )) -} - - -# config parsing -# -------------- - -read_config() { - local config_file="$1" - local dst="$2" - - [ -f "$config_file" ] || die "read_config: $config_file: no such file" - - local n=0 - local failed= - local key - local value - - while read line; do - n=$(( n+1 )) - - # skip empty lines or comments - if [ -z "$line" ] || [[ "$line" =~ ^#.* ]]; then - continue - fi - - if [[ $line = *"="* ]]; then - key="${line%%=*}" - value="${line#*=}" - eval "$dst[$key]=\"$value\"" - else - echoerr "config: invalid line $n" - failed=1 - fi - done < <(cat "$config_file") - - [ -z "$failed" ] -} - -check_config() { - local var="$1" - local keys="$2" - - local failed= - - for key in $keys; do - if [ -z "$(eval "echo -n \${$var[$key]}")" ]; then - echoerr "config: ${BOLD}${key}${RST}${RED} is missing" - failed=1 - fi - done - - [ -z "$failed" ] -} - - -# other functions -# --------------- - -installed() { - command -v "$1" > /dev/null - return $? -} - -download() { - local source="$1" - local target="$2" - - if installed curl; then - curl -f -s -o "$target" "$source" - elif installed wget; then - wget -q -O "$target" "$source" - else - die "neither curl nor wget found, can't proceed" - fi -} - -file_in_use() { - [ -n "$(lsof "$1")" ] -} - -file_mtime() { - stat -c %Y "$1" -} diff --git a/tools/process-motion-timecodes.py b/tools/process-motion-timecodes.py deleted file mode 100755 index 7be7977..0000000 --- a/tools/process-motion-timecodes.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python3 -import os.path -from src.home.camera.util import dvr_scan_timecodes - -from argparse import ArgumentParser -from datetime import datetime, timedelta - -DATETIME_FORMAT = '%Y-%m-%d-%H.%M.%S' - - -def chunks(lst, n): - for i in range(0, len(lst), n): - yield lst[i:i + n] - - -def time2seconds(time: str) -> int: - time, frac = time.split('.') - frac = int(frac) - - h, m, s = [int(i) for i in time.split(':')] - - return round(s + m*60 + h*3600 + frac/1000) - - -def filename_to_datetime(filename: str) -> datetime: - filename = os.path.basename(filename).replace('record_', '').replace('.mp4', '') - return datetime.strptime(filename, DATETIME_FORMAT) - - -if __name__ == '__main__': - parser = ArgumentParser() - parser.add_argument('--source-filename', type=str, required=True, - help='recording filename') - parser.add_argument('--timecodes', type=str, required=True, - help='timecodes') - parser.add_argument('--padding', type=int, default=2, - help='amount of seconds to add before and after each fragment') - arg = parser.parse_args() - - if arg.padding < 0: - raise ValueError('invalid padding') - - fragments = dvr_scan_timecodes(arg.timecodes) - file_dt = filename_to_datetime(arg.source_filename) - - for fragment in fragments: - start, end = fragment - - start -= arg.padding - end += arg.padding - - if start < 0: - start = 0 - - duration = end - start - - dt1 = (file_dt + timedelta(seconds=start)).strftime(DATETIME_FORMAT) - dt2 = (file_dt + timedelta(seconds=end)).strftime(DATETIME_FORMAT) - filename = f'{dt1}__{dt2}.mp4' - - print(f'{start} {duration} {filename}') diff --git a/tools/remove-old-recordings.sh b/tools/remove-old-recordings.sh deleted file mode 100644 index d376572..0000000 --- a/tools/remove-old-recordings.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -# to be launched by cron on remote server - -find /var/recordings -type f -mtime +14 -delete diff --git a/tools/rotate-video.sh b/tools/rotate-video.sh index 6d27b44..5ce4efe 100755 --- a/tools/rotate-video.sh +++ b/tools/rotate-video.sh @@ -5,7 +5,7 @@ set -e DIR="$( cd "$( dirname "$(realpath "${BASH_SOURCE[0]}")" )" &>/dev/null && pwd )" PROGNAME="$0" -. "$DIR/lib.bash" +. "$DIR/../include/bash/include.bash" usage() { diff --git a/tools/sunxi-h3-i2c-reset.sh b/tools/sunxi-h3-i2c-reset.sh deleted file mode 100644 index e654dfb..0000000 --- a/tools/sunxi-h3-i2c-reset.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh - -devices="1c2ac00.i2c 1c2b000.i2c" -pins="8 9 28 30" -driver_path="/sys/bus/platform/drivers/mv64xxx_i2c" - -driver_unbind() { - echo -n "$1" > "$driver_path/unbind" -} - -driver_bind() { - echo -n "$1" > "$driver_path/bind" -} - -for dev in $devices; do driver_unbind "$dev"; done -echo "unbind done" - -for pin in pins; do - gpio mode $pin out - gpio write $pin 0 -done -echo "gpio reset done" - -for dev in $devices; do driver_bind "$dev"; done -echo "bind done" \ No newline at end of file diff --git a/tools/sunxi-setup-amixer.sh b/tools/sunxi-setup-amixer.sh deleted file mode 100755 index 5746514..0000000 --- a/tools/sunxi-setup-amixer.sh +++ /dev/null @@ -1,114 +0,0 @@ -#!/bin/bash - -amixer() { - /usr/bin/amixer "$@" -} - -setup_opi_pc2() { - for v in unmute cap; do - amixer set "Line In" $v - amixer set "Mic1" $v - amixer set "Mic2" $v - done - - for k in "Mic1 Boost" "Line In" "Mic1" "Mic2 Boost" "Mic2"; do - amixer set "$k" "86%" - done -} - -setup_opi_one() { - for v in unmute cap; do - amixer set "Line In" $v - amixer set "Mic1" $v - done - - for k in "Mic1 Boost" "Line In" "Mic1"; do - amixer set "$k" "86%" - done -} - -setup_opi3lts() { - switches=( - "Left DAC Mixer ADCL" - "Left DAC Mixer I2SDACL" - "Left I2S Mixer ADCL" - "Left I2S Mixer I2SDACL" - "Left Input Mixer LINEINL" - "Left Input Mixer MIC1" - "Left Input Mixer MIC2" - "Left Input Mixer OMixerL" - "Left Input Mixer OMixerR" - "Left Input Mixer PhoneN" - "Left Input Mixer PhonePN" - "Left Output Mixer DACL" - "Left Output Mixer DACR" - "Left Output Mixer LINEINL" - "Left Output Mixer MIC1" - "Left Output Mixer MIC2" - "Left Output Mixer PhoneN" - "Left Output Mixer PhonePN" - "Right DAC Mixer ADCR" - "Right DAC Mixer I2SDACR" - "Right I2S Mixer ADCR" - "Right I2S Mixer I2SDACR" - "Right Input Mixer LINEINR" - "Right Input Mixer MIC1" - "Right Input Mixer MIC2" - "Right Input Mixer OMixerL" - "Right Input Mixer OMixerR" - "Right Input Mixer PhoneP" - "Right Input Mixer PhonePN" - "Right Output Mixer DACL" - "Right Output Mixer DACR" - "Right Output Mixer LINEINR" - "Right Output Mixer MIC1" - "Right Output Mixer MIC2" - "Right Output Mixer PhoneP" - "Right Output Mixer PhonePN" - ) - for v in "${switches[@]}"; do - value=on - case "$v" in - *Input*) - value=on - ;; - *Output*) - value=off - ;; - esac - amixer set "$v" $value - done - - to_mute=( - "I2S Mixer ADC" - "I2S Mixer DAC" - "ADC Input" - "DAC Mixer ADC" - "DAC Mxier DAC" # this is not a typo - ) - for v in "${to_mute[@]}"; do - amixer set "$v" "0%" - done - - amixer set "Master" "100%" - amixer set "MIC1 Boost" "100%" - amixer set "MIC2 Boost" "100%" - amixer set "Line Out Mixer" "86%" - amixer set "MIC Out Mixer" "71%" -} - -device="$(tr -d '\0' < /sys/firmware/devicetree/base/model)" -case "$device" in - *"Orange Pi PC 2") - setup_opi_pc2 - ;; - *"Orange Pi One"|*"Orange Pi Lite") - setup_opi_one - ;; - *"OrangePi 3 LTS") - setup_opi3lts - ;; - *) - >&2 echo "error: unidentified device: $device" - ;; -esac diff --git a/tools/sync-recordings-to-remote.sh b/tools/sync-recordings-to-remote.sh deleted file mode 100755 index cf979d1..0000000 --- a/tools/sync-recordings-to-remote.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/bash - -PROGNAME="$0" -NODE_CONFIG="/etc/sound_node.toml" -REMOTE_USER=user -REMOTE_SERVER=solarmon.ru -REMOTE_DIRECTORY=/var/recordings - -set -e - -echoerr() { - >&2 echo "error: $@" -} - -echowarn() { - >&2 echo "warning: $@" -} - -telegram_alert() { - if [ -z "$TG_TOKEN" ] || [ -z "$TG_CHAT_ID" ]; then return; fi - curl -X POST \ - -F "chat_id=${TG_CHAT_ID}" \ - -F "text=$1" \ - "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" -} - -fatal() { - echoerr "$@" - telegram_alert "$PROGNAME: $@" - exit 1 -} - -get_config_var() { - local varname="$1" - cat "$NODE_CONFIG" | grep "^$varname = \"" | awk '{print $3}' | tr -d '"' -} - -get_mp3_count() { - find "$LOCAL_DIR" -mindepth 1 -type f -name "*.mp3" -printf x | wc -c -} - -[ -z "$TG_TOKEN" ] && echowarn "TG_TOKEN is not set" -[ -z "$TG_CHAT_ID" ] && echowarn "TG_CHAT_ID is not set" - -NODE_NAME=$(get_config_var name) -LOCAL_DIR=$(get_config_var storage) - -[ -z "$NODE_NAME" ] && fatal "failed to parse NODE_NAME" -[ -z "$LOCAL_DIR" ] && fatal "failed to parse LOCAL_DIR" - -[ -d "$LOCAL_DIR" ] || fatal "$LOCAL_DIR is not a directory" - -COUNT=$(get_mp3_count) -(( $COUNT < 1 )) && { - echo "seems there's nothing to sync" - exit -} - -cd "$LOCAL_DIR" || fatal "failed to change to $LOCAL_DIR" - -rsync -azPv -e "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=ERROR" \ - *.mp3 \ - ${REMOTE_USER}@${REMOTE_SERVER}:"${REMOTE_DIRECTORY}/${NODE_NAME}/" \ - --exclude temp.mp3 - -RC=$? - -if [ $RC -eq 0 ]; then - find "$LOCAL_DIR" -name "*.mp3" -type f -mmin +1440 -delete || fatal "find failed to delete old files" -else - fatal "failed to rsync: code $RC" -fi diff --git a/tools/video-util.sh b/tools/video-util.sh index 0ee5560..6fe6109 100755 --- a/tools/video-util.sh +++ b/tools/video-util.sh @@ -5,7 +5,7 @@ set -e DIR="$( cd "$( dirname "$(realpath "${BASH_SOURCE[0]}")" )" &> /dev/null && pwd )" PROGNAME="$0" -. "$DIR/lib.bash" +. "$DIR/../include/bash/include.bash" input= output= -- cgit v1.2.3