拍视频版本

This commit is contained in:
2026-04-01 14:11:47 +08:00
commit a7a67296e3
82 changed files with 591269 additions and 0 deletions

162
bash/disk_tool.sh Normal file
View File

@@ -0,0 +1,162 @@
#!/bin/bash
# ================= 配置区 =================
# 注意:这里不再硬编码 /dev/sda1脚本会自动检测第一个非系统盘的 SATA/USB 设备
TARGET_LABEL=""
MOUNT_POINT="/videos"
FSTAB_FILE="/etc/fstab"
# ==========================================
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 1. 检查 Root 权限
if [ "$EUID" -ne 0 ]; then
echo -e "${RED}❌ 权限不足!请使用 sudo 运行此脚本${NC}"
exit 1
fi
# 2. 自动检测硬盘设备 (智能查找)
# 逻辑:查找名为 sda1, sdb1, sdc1 等且不是根分区的设备
detect_device() {
# 尝试找 sda1
if [ -b "/dev/sda1" ] && [ "$(df /dev/sda1 2>/dev/null | tail -1 | awk '{print $NF}')" != "/" ]; then
echo "/dev/sda1"
return
fi
# 尝试找 sdb1
if [ -b "/dev/sdb1" ]; then
echo "/dev/sdb1"
return
fi
# 尝试找 mmcblk 以外的第一个大容量分区 (针对更复杂情况)
lsblk -ndpo NAME,TYPE,SIZE,MOUNTPOINT | grep -E "part|disk" | grep -v "mmcblk" | head -n 1 | awk '{print $1}'
}
DEVICE=$(detect_device)
if [ -z "$DEVICE" ]; then
echo -e "${RED}❌ 未检测到可用的外接硬盘设备 (sda1/sdb1 等)。请检查硬件连接。${NC}"
exit 1
fi
# 3. 获取 UUID (核心步骤)
UUID=$(blkid -s UUID -o value "$DEVICE" 2>/dev/null)
if [ -z "$UUID" ]; then
echo -e "${RED}❌ 无法获取设备 $DEVICE 的 UUID。可能文件系统损坏或未格式化。${NC}"
echo "提示:尝试执行 'sudo mkfs.ext4 $DEVICE' 进行格式化 (数据会丢失!)"
exit 1
fi
# 获取文件系统类型
FS_TYPE=$(blkid -s TYPE -o value "$DEVICE" 2>/dev/null)
echo -e "${GREEN}✅ 检测到设备:${NC} $DEVICE"
echo -e "${GREEN}✅ 文件系统:${NC} $FS_TYPE"
echo -e "${GREEN}✅ UUID:${NC} $UUID"
echo ""
# 4. 确保挂载点目录存在
if [ ! -d "$MOUNT_POINT" ]; then
echo "正在创建挂载点: $MOUNT_POINT"
mkdir -p "$MOUNT_POINT"
chmod 777 "$MOUNT_POINT"
fi
# 5. 菜单界面
echo "=========================================="
echo " RK3588 硬盘挂载工具"
echo "=========================================="
echo "目标设备: $DEVICE ($FS_TYPE)"
echo "设备 UUID: $UUID"
echo "挂载目录: $MOUNT_POINT"
echo "------------------------------------------"
echo " 1) 立即挂载硬盘 (Mount Now)"
echo " 2) 立即取消挂载 (Unmount Now)"
echo " 3) 开启开机自动挂载 (Enable Auto-Mount via UUID)"
echo " 4) 取消开机自动挂载 (Disable Auto-Mount)"
echo " 5) 查看当前磁盘状态 (Status)"
echo " 6) 退出 (Exit)"
echo "=========================================="
read -p "请输入选项 [1-6]: " choice
case $choice in
1)
echo "正在挂载 UUID=$UUID$MOUNT_POINT ..."
# 使用 UUID 挂载命令
mount -U "$UUID" "$MOUNT_POINT"
if [ $? -eq 0 ]; then
echo -e "${GREEN}✅ 挂载成功!${NC}"
df -h | grep "$MOUNT_POINT"
else
echo -e "${RED}❌ 挂载失败!${NC}"
echo "可能原因:文件系统类型不支持或需要 fsck 修复。"
echo "尝试手动修复sudo fsck -y $DEVICE"
fi
;;
2)
echo "正在卸载 $MOUNT_POINT ..."
umount "$MOUNT_POINT"
if [ $? -eq 0 ]; then
echo -e "${GREEN}✅ 卸载成功!${NC}"
else
echo -e "${RED}❌ 卸载失败!${NC} 可能有程序正在使用该目录 (如日志写入、视频录制)。"
echo "提示:使用 'lsof +D $MOUNT_POINT' 查看占用进程。"
fi
;;
3)
# 检查 fstab 中是否已经存在该 UUID 的记录
if grep -q "$UUID" "$FSTAB_FILE"; then
echo -e "${YELLOW}⚠️ 开机自动挂载已存在 (基于 UUID),无需重复设置。${NC}"
else
# 构造 fstab 行UUID=xxxx /videos auto defaults,nofail,x-systemd.device-timeout=30 0 2
FSTAB_ENTRY="UUID=$UUID $MOUNT_POINT $FS_TYPE defaults,nofail,x-systemd.device-timeout=30 0 2"
echo "即将写入配置:"
echo " $FSTAB_ENTRY"
read -p "确认写入?(y/n): " confirm
if [ "$confirm" == "y" ] || [ "$confirm" == "Y" ]; then
echo "$FSTAB_ENTRY" >> "$FSTAB_FILE"
echo -e "${GREEN}✅ 开机自动挂载已启用!${NC}"
# 尝试立即应用配置测试
echo "正在测试配置有效性..."
umount "$MOUNT_POINT" 2>/dev/null
if mount -a; then
echo -e "${GREEN}✅ 配置测试通过mount -a 执行成功。${NC}"
else
echo -e "${RED}⚠️ 警告mount -a 测试失败,请检查 /etc/fstab 格式。${NC}"
fi
else
echo "操作已取消。"
fi
fi
;;
4)
# 删除包含该 UUID 的行
if grep -q "$UUID" "$FSTAB_FILE"; then
sed -i "/$UUID/d" "$FSTAB_FILE"
echo -e "${GREEN}✅ 开机自动挂载已清理!${NC}"
else
echo "未找到该 UUID 的配置记录。"
fi
;;
5)
echo "--- 当前磁盘使用情况 ---"
df -hT | grep -E "Filesystem|$MOUNT_POINT|/dev/sd"
echo ""
echo "--- /etc/fstab 内容预览 ---"
tail -n 5 "$FSTAB_FILE"
;;
6)
echo "退出脚本。"
exit 0
;;
*)
echo -e "${RED}❌ 错误:输入无效。${NC}"
;;
esac

155
bash/video_tool.sh Normal file
View File

@@ -0,0 +1,155 @@
#!/bin/bash
# 自动以 root 权限运行
if [ "$EUID" -ne 0 ]; then
echo "需要 root 权限,正在提权..."
exec sudo bash "$0" "$@"
fi
# 配置区
APP_NAME="lj360_camera"
WORK_DIR="/home/ztl/LJ360"
PYTHON_SCRIPT="web.py"
USER="ztl"
SYSTEMD_SERVICE_FILE="/etc/systemd/system/${APP_NAME}.service"
SCRIPT_PATH="$WORK_DIR/camera_manager.sh"
# 日志文件(保活日志)
LOG_FILE="$WORK_DIR/${APP_NAME}_keepalive.log"
# 启动主程序(带保活循环)
start_app() {
cd "$WORK_DIR" || exit 1
echo "$(date): 启动 $APP_NAME 应用..." >> "$LOG_FILE"
while true; do
if sudo -u ztl python3 "$PYTHON_SCRIPT"; then
echo "$(date): 程序正常退出,即将重启..." >> "$LOG_FILE"
else
echo "$(date): 程序异常退出(代码 $?5秒后重启..." >> "$LOG_FILE"
fi
sleep 5
done
}
# 创建 systemd 服务文件
create_systemd_service() {
cat > "$SYSTEMD_SERVICE_FILE" <<EOF
[Unit]
Description=LJ360 Four-Camera BirdView System
After=multi-user.target
[Service]
Type=simple
User=$USER
Group=$USER
WorkingDirectory=$WORK_DIR
ExecStart=/bin/bash $SCRIPT_PATH run
Restart=always
RestartSec=5
StandardOutput=append:$LOG_FILE
StandardError=append:$LOG_FILE
Environment=HOME=/home/ztl
Environment=DISPLAY=:0
[Install]
WantedBy=multi-user.target
EOF
chmod 644 "$SYSTEMD_SERVICE_FILE"
systemctl daemon-reload
echo "✅ systemd 服务已创建: $APP_NAME"
}
# 启用开机自启
enable_autostart() {
# 确保 ztl 用户有串口和摄像头权限
usermod -aG dialout ztl 2>/dev/null
usermod -aG video ztl 2>/dev/null
# 确保日志文件归属正确
touch "$LOG_FILE"
chown ztl:ztl "$LOG_FILE"
create_systemd_service
systemctl enable "$APP_NAME".service
systemctl start "$APP_NAME".service
echo "✅ 已启用开机自启,并以 ztl 用户启动服务"
}
# 禁用开机自启
disable_autostart() {
systemctl stop "$APP_NAME".service 2>/dev/null
systemctl disable "$APP_NAME".service 2>/dev/null
rm -f "$SYSTEMD_SERVICE_FILE"
systemctl daemon-reload
echo "✅ 已禁用开机自启,并移除服务"
}
# 显示状态
show_status() {
if systemctl is-active --quiet "$APP_NAME".service; then
echo "🟢 服务正在运行(开机自启已启用)"
systemctl status "$APP_NAME".service --no-pager
elif [ -f "$SYSTEMD_SERVICE_FILE" ]; then
echo "🟠 服务已安装但未运行"
else
echo "🔴 服务未安装(开机自启已关闭)"
fi
}
# 主菜单
show_menu() {
echo "========================================"
echo " LJ360 摄像头系统管理工具"
echo "========================================"
show_status
echo ""
echo "请选择操作:"
echo " 1) 启用开机自启并启动服务ztl 用户)"
echo " 2) 禁用开机自启并停止服务"
echo " 3) 仅手动运行一次(不保活)"
echo " 4) 查看日志"
echo " 0) 退出"
echo -n "请输入编号 (0-4): "
}
# 处理命令行参数(用于 systemd 调用)
if [ "$1" = "run" ]; then
start_app
exit 0
fi
# 交互式主程序
while true; do
show_menu
read -r choice
case $choice in
1)
enable_autostart
;;
2)
disable_autostart
;;
3)
echo "🚀 手动运行一次Ctrl+C 可退出)..."
cd "$WORK_DIR" && sudo -u ztl python3 "$PYTHON_SCRIPT"
;;
4)
echo "📄 最近日志(最后 20 行):"
tail -n 20 "$LOG_FILE"
echo -n "按回车返回菜单..."
read -r
;;
0)
echo "退出。"
exit 0
;;
*)
echo "❌ 无效选项,请输入 0-4"
sleep 1
;;
esac
echo ""
done

213
bash/ztlOut_rootfs.sh Normal file
View File

@@ -0,0 +1,213 @@
#!/bin/bash
function showhelp()
{
echo -e "
ff_export_rootfs </path/to/store> [-t <ext4|btrfs>]
"
exit -1
}
function prepare_rootfs()
{
systemctl enable resize-helper.service > /dev/null 2>&1
apt-get clean -y
}
function clead_target_rootfs()
{
ROOT_DIR=$1
rm -rf ${ROOT_DIR}/usr/share/doc/*
rm -rf ${ROOT_DIR}/var/lib/apt/lists/*
rm -rf ${ROOT_DIR}/tmp/*
find ${ROOT_DIR}/usr/share/man/ -name "*.gz" -exec rm {} \;
find ${ROOT_DIR}/var/log/ -type f -name "*.gz" -exec rm {} \;
find ${ROOT_DIR}/var/log/ -type f -exec truncate -s 0 {} \;
for item in $(ls ${ROOT_DIR}/media/ 2>/dev/null); do
if id ${item} > /dev/null 2>&1
then
chown -R ${item}:${item} ${ROOT_DIR}/media/${item}
fi
done
if [[ -L "${ROOT_DIR}/var/lib/docker" ]]
then
orig_dir=$(readlink "${ROOT_DIR}/var/lib/docker")
if [[ ! -d ${ROOT_DIR}/${orig_dir} ]]
then
rm -rf "${ROOT_DIR}/var/lib/docker"
rsync -aqx "${orig_dir}/" "${ROOT_DIR}/var/lib/docker"
fi
fi
}
function umount_img() {
if mountpoint -q $TEMP_MOUNT_POINT; then
umount $TEMP_MOUNT_POINT
fi
rm -rf $TEMP_MOUNT_POINT
}
function finish() {
umount_img
exit -1
}
[[ $UID -ne 0 ]] && echo -e "\033[31m should run as root \033[0m" && showhelp
[[ -n "$(which rsync)" ]] || { echo -e " rsync not found\n\033[31m apt install rsync \033[0m"; exit -1; }
[[ $# -lt 1 ]] && showhelp;
[[ "$1" == "-u" ]] && { PORT_UPPER="1"; shift; }
DEST_PATH=$1
MEDIA_DEVICE=
STORE_FS_TYPE=
[[ -d $DEST_PATH ]] || { echo -e "\033[31m store path not exist \033[0m"; exit -1; }
if [[ $# -eq 3 ]] && [[ $2 == "-t" ]]; then
case $3 in
ext4|btrfs)
STORE_FS_TYPE=$3
;;
*)
showhelp
;;
esac
fi
PORT_OVERLAYROOT=false
ROOTFS_DEVICE=`findmnt -n -o SOURCE --target /`
if [[ "${ROOTFS_DEVICE}" == "overlayroot" ]]; then
PORT_OVERLAYROOT=true
OVERLAY_MOUNT=$(mount | sed -n '/^overlayroot/p')
UPPER_MOUNTPOINT=$(echo ${OVERLAY_MOUNT} | grep -Eo "upperdir.*" | cut -f 1 -d , | cut -f 2 -d =)
LOWER_MOUNTPOINT=$(echo ${OVERLAY_MOUNT} | grep -Eo "lowerdir.*" | cut -f 1 -d , | cut -f 2 -d =)
LOWER_DEVICE=$(findmnt -n -o SOURCE --target ${LOWER_MOUNTPOINT})
UPPER_DEVICE=$(findmnt -n -o SOURCE --target ${UPPER_MOUNTPOINT})
if [[ "${PORT_UPPER}" == "1" ]]; then
PORT_OVERLAYROOT=false
ROOTFS_DEVICE=${UPPER_DEVICE}
ROOTFS_MOUNTPOINT=${UPPER_MOUNTPOINT}/
ROOTFS_TYPE=$(blkid -o value -s TYPE ${ROOTFS_DEVICE})
ROOTFS_PARTLABEL=$(lsblk -n -o PARTLABEL ${ROOTFS_DEVICE})
[[ -n "${ROOTFS_PARTLABEL}" ]] || ROOTFS_PARTLABEL="userdata"
else
ROOTFS_MOUNTPOINT="/"
ROOTFS_TYPE=$(blkid -o value -s TYPE ${LOWER_DEVICE})
ROOTFS_PARTLABEL=$(lsblk -n -o PARTLABEL ${LOWER_DEVICE})
[[ -n "${ROOTFS_PARTLABEL}" ]] || ROOTFS_PARTLABEL="rootfs"
fi
else
if [[ "${ROOTFS_DEVICE}" == "/dev/root" ]]; then
MAJOR_ROOTFS=$(mountpoint -d / | cut -f 1 -d ":")
MINOR_ROOTFS=$(mountpoint -d / | cut -f 2 -d ":")
DEV_ROOTFS=$(cat /proc/partitions | awk {'if ($1 == "'${MAJOR_ROOTFS}'" && $2 == "'${MINOR_ROOTFS}'") print $4 '})
ROOTFS_DEVICE=/dev/${DEV_ROOTFS}
fi
PORT_OVERLAYROOT=false
ROOTFS_MOUNTPOINT="/"
ROOTFS_TYPE=$(blkid -o value -s TYPE ${ROOTFS_DEVICE})
ROOTFS_PARTLABEL=$(lsblk -n -o PARTLABEL ${ROOTFS_DEVICE})
[[ -n "${ROOTFS_PARTLABEL}" ]] || ROOTFS_PARTLABEL="rootfs"
fi
#MEDIA_DEVICE=`findmnt -n -o SOURCE --target $DEST_PATH`
MEDIA_FS_TYPE=`df $DEST_PATH --output=fstype | awk 'NR==2 {print $1}'`
MEDIA_FREE_SPACE=`df $DEST_PATH -m --output=avail | awk 'NR==2 {print $1}'`
#[[ $MEDIA_DEVICE != $ROOTFS_DEVICE ]] || { echo -e "\033[31m can not store in local device \033[0m"; exit -1; }
#[[ $MEDIA_FS_TYPE != "vfat" ]] || { echo -e "\033[31m store media fs type cannot be FAT \033[0m"; exit -1; }
prepare_rootfs
if [[ $ROOTFS_TYPE == "btrfs" ]]; then
ROOTFS_SIZE=`btrfs filesystem usage -m / | grep "Device allocated" | awk '{print $3}'`
else
if [[ "${PORT_OVERLAYROOT}" == "true" ]]; then
LOWER_SIZE=$(du -s -k "${LOWER_MOUNTPOINT}" |sed -r -e 's/[[:space:]]+.*$//')
UPPER_SIZE=$(du -s -k "${UPPER_MOUNTPOINT}" |sed -r -e 's/[[:space:]]+.*$//')
DOCKER_SIZE=0
if [[ -L "/var/lib/docker" ]]
then
DOCKER_FINAL_DIR=$(readlink "/var/lib/docker")
if [[ -d ${DOCKER_FINAL_DIR} ]]
then
DOCKER_SIZE=$(du -s -k "${DOCKER_FINAL_DIR}" |sed -r -e 's/[[:space:]]+.*$//')
fi
fi
ROOTFS_SIZE=$((LOWER_SIZE+UPPER_SIZE+DOCKER_SIZE))
else
ROOTFS_SIZE=`df ${ROOTFS_MOUNTPOINT} -k --output=used | awk 'NR==2 {print $1}'`
fi
fi
IMAGE_SIZE=$((ROOTFS_SIZE>>10))
IMAGE_SIZE=$((IMAGE_SIZE+IMAGE_SIZE/10+300))
echo -e "MEDIA FREE SPACE SIZE \t $MEDIA_FREE_SPACE \t MBytes"
echo -e "EXPORT IMAGE SIZE \t $IMAGE_SIZE \t MBytes"
if [[ $MEDIA_FREE_SPACE -lt $IMAGE_SIZE ]]; then
echo -e "\033[31m No enough free space on $MOUNT_POINT \033[0m"
exit -1
fi
[[ $STORE_FS_TYPE != "" ]] || STORE_FS_TYPE=$ROOTFS_TYPE
CURDATE=`date "+%Y%m%d%H%M"`
OS_D=`lsb_release -d -s`
IMAGE_FILE=$DEST_PATH/${OS_D// /}_ztl_${STORE_FS_TYPE}_$CURDATE.img
TEMP_MOUNT_POINT=`mktemp -d -p $DEST_PATH`
set -e
trap finish ERR INT
if [[ $STORE_FS_TYPE == "btrfs" ]]; then
truncate -s ${IMAGE_SIZE}M $IMAGE_FILE
mkfs.btrfs -fq -L $ROOTFS_PARTLABEL $IMAGE_FILE
mount -t btrfs -o noatime,compress=lzo $IMAGE_FILE $TEMP_MOUNT_POINT
btrfs subvolume create $TEMP_MOUNT_POINT/root
umount $TEMP_MOUNT_POINT
mount -t btrfs -o noatime,compress=lzo,subvol=root $IMAGE_FILE $TEMP_MOUNT_POINT
else
INODE_COUNT=$(find "${ROOTFS_MOUNTPOINT}" 2>/dev/null | wc -l)
INODE_COUNT=$((INODE_COUNT+512))
BLOCK_COUNT=$((2000+(ROOTFS_SIZE+INODE_COUNT/4)*12/10))
echo BLOCK_COUNT $BLOCK_COUNT
echo INODE_COUNT $INODE_COUNT
mkfs.ext4 -Fq -L $ROOTFS_PARTLABEL -b 1024 -I 128 -N $INODE_COUNT $IMAGE_FILE $BLOCK_COUNT
mount $IMAGE_FILE $TEMP_MOUNT_POINT
fi
echo "sync..."
rsync -aqx --delete --exclude={"$IMAGE_FILE","$TEMP_MOUNT_POINT"} ${ROOTFS_MOUNTPOINT} ${TEMP_MOUNT_POINT}
echo "sync finish"
set +e
trap - ERR
clead_target_rootfs ${TEMP_MOUNT_POINT}
while IFS= read -r LINE
do
[ "${LINE}" == "/" ] && continue
E_FILE="${TEMP_MOUNT_POINT}/${LINE}"
[[ -e "${E_FILE}" ]] && rm -rf "${E_FILE}"
done < <(findmnt -n -lo TARGET -t ext4,ext3,ext2,vat)
umount_img
if [[ "${STORE_FS_TYPE}" == "ext4" ]]; then
e2fsck -fy ${IMAGE_FILE} 2>&1 > /dev/null
tune2fs -C 1 -c 0 -i 0 -e "remount-ro" ${IMAGE_FILE} 2>&1 > /dev/null
fi
echo -e "\033[31m Export rootfs to $IMAGE_FILE Success \033[0m"