first commit

This commit is contained in:
teasiu
2026-04-19 05:07:07 +08:00
commit 734c00f494
11 changed files with 3243 additions and 0 deletions

512
install.sh Executable file
View File

@@ -0,0 +1,512 @@
#!/bin/bash
set -euo pipefail
# ==============================================================================
# 配置中心
# ==============================================================================
readonly APP_DIR="/opt/nas-media-player"
readonly SERVICE_NAME="nas-media-player"
readonly WAIT_TIMEOUT=10
readonly SYSTEMD_SERVICE="/etc/systemd/system/${SERVICE_NAME}.service"
readonly PORT=8800 # 服务监听端口
readonly VIDEO_DIR="/mnt" # 媒体文件根目录(和程序逻辑对齐)
readonly LOG_FILE="${APP_DIR}/${SERVICE_NAME}.log" # 日志文件固定在运行目录
readonly PIP_MIRROR="https://mirrors.tencent.com/pypi/simple" # 网络检查镜像
readonly BIN_NAME="${SERVICE_NAME}" # 运行目录下的二进制文件名(统一命名)
# 必需文件列表(包含所有架构二进制,但部署时仅复制匹配的)
readonly REQUIRED_FILES=(
"nas-media-player.py"
"index.html"
"zhinan.html"
"releases/nas-media-player-armhf"
"releases/nas-media-player-arm64"
"releases/nas-media-player-x86_64"
)
# 架构映射表uname -m → 二进制后缀)
declare -A ARCH_MAP=(
["armv7l"]="armhf"
["aarch64"]="arm64"
["x86_64"]="x86_64"
)
# ==============================================================================
# 颜色与样式定义(提升用户体验)
# ==============================================================================
readonly COLOR_RESET="\033[0m"
readonly COLOR_RED="\033[31m"
readonly COLOR_GREEN="\033[32m"
readonly COLOR_YELLOW="\033[33m"
readonly COLOR_BLUE="\033[34m"
readonly COLOR_BOLD="\033[1m"
# ==============================================================================
# 全局变量(检测后赋值)
# ==============================================================================
OS_NAME="" # 系统发行版
DETECTED_ARCH="" # 检测到的系统架构
SOURCE_BIN_FILE="" # 源二进制文件releases下的匹配文件
TARGET_BIN_FILE="" # 目标二进制文件(运行目录下)
HAS_SYSTEMD="false" # 是否支持systemd
# ==============================================================================
# 日志与输出函数(统一输出格式)
# ==============================================================================
log_info() {
echo -e "${COLOR_BLUE}[INFO]${COLOR_RESET} $*"
}
log_success() {
echo -e "${COLOR_GREEN}[SUCCESS]${COLOR_RESET} $*"
}
log_warn() {
echo -e "${COLOR_YELLOW}[WARN]${COLOR_RESET} $*"
}
log_error() {
echo -e "${COLOR_RED}[ERROR]${COLOR_RESET} $*" >&2
}
log_step() {
echo -e "\n${COLOR_BOLD}[$1/$2] $3${COLOR_RESET}"
}
# ==============================================================================
# 系统检测函数(核心:架构/发行版/systemd检测
# ==============================================================================
detect_os_info() {
log_step 1 7 "检测系统信息"
# 1. 检测系统发行版
if [ -f /etc/os-release ]; then
source /etc/os-release
OS_NAME="${NAME} ${VERSION_ID}"
elif [ -f /etc/lsb-release ]; then
source /etc/lsb-release
OS_NAME="${DISTRIB_ID} ${DISTRIB_RELEASE}"
else
OS_NAME="Unknown Linux"
fi
log_info "系统发行版:${OS_NAME}"
# 2. 检测系统架构并匹配二进制文件
local raw_arch=$(uname -m)
if [[ -v ARCH_MAP["${raw_arch}"] ]]; then
DETECTED_ARCH="${ARCH_MAP["${raw_arch}"]}"
SOURCE_BIN_FILE="releases/${SERVICE_NAME}-${DETECTED_ARCH}" # 源文件(脚本同目录)
TARGET_BIN_FILE="${APP_DIR}/${BIN_NAME}" # 目标文件(运行目录)
log_info "检测到架构:${raw_arch} → 匹配二进制:${SOURCE_BIN_FILE} → 部署到:${TARGET_BIN_FILE}"
else
log_error "不支持的架构:${raw_arch}仅支持armhf/arm64/x86_64"
exit 1
fi
# 3. 检测是否支持systemd
if command -v systemctl >/dev/null 2>&1 && systemctl >/dev/null 2>&1; then
HAS_SYSTEMD="true"
log_info "系统支持systemd服务管理"
else
log_warn "系统不支持systemd无法配置开机自启"
fi
}
# ==============================================================================
# 前置检查函数
# ==============================================================================
check_root() {
if [ "$(id -u)" -ne 0 ]; then
log_error "请使用 root 用户执行sudo -i 后运行)"
exit 1
fi
}
check_network() {
log_step 2 7 "检查网络连通性"
if ! curl -s --connect-timeout 5 "${PIP_MIRROR}" >/dev/null; then
log_warn "网络连接可能不稳定(不影响本地部署)"
else
log_success "网络连通性检查通过"
fi
}
check_required_files() {
log_step 3 7 "检查必需文件"
local missing_files=()
for file in "${REQUIRED_FILES[@]}"; do
if [ ! -f "${file}" ]; then
missing_files+=("${file}")
fi
done
if [ ${#missing_files[@]} -gt 0 ]; then
log_error "缺失必需文件:${missing_files[*]}"
log_error "请确保所有必需文件与脚本同目录"
exit 1
fi
log_success "所有必需文件检查通过"
}
check_system_deps() {
log_step 4 7 "检查系统依赖"
# 检查必要命令
local required_cmds=("curl" "awk" "grep" "pkill" "pgrep")
for cmd in "${required_cmds[@]}"; do
if ! command -v "${cmd}" >/dev/null 2>&1; then
log_error "缺失必需命令:${cmd}(请先安装)"
exit 1
fi
done
log_success "系统依赖检查通过"
}
# ==============================================================================
# 核心功能函数
# ==============================================================================
create_app_dirs() {
log_step 5 7 "创建应用目录"
# 仅创建必要目录
mkdir -p "${APP_DIR}" \
"${APP_DIR}/static" \
"${VIDEO_DIR}"
# 创建日志文件并设置权限(确保运行目录可写)
touch "${LOG_FILE}"
chmod 644 "${LOG_FILE}"
chmod 755 "${APP_DIR}" # 确保运行目录有执行权限
log_success "应用目录创建完成:${APP_DIR}(日志文件:${LOG_FILE}"
}
deploy_app_files() {
log_step 6 7 "部署程序文件"
# 复制主程序文件
cp -f "nas-media-player.py" "${APP_DIR}/" || { log_error "复制主程序文件失败"; exit 1; }
# 复制静态文件
cp -f "index.html" "zhinan.html" "${APP_DIR}/static/" || { log_error "复制静态文件失败"; exit 1; }
# 仅复制匹配架构的二进制文件到运行目录(核心改动)
cp -f "${SOURCE_BIN_FILE}" "${TARGET_BIN_FILE}" || { log_error "复制二进制文件 ${SOURCE_BIN_FILE} 失败"; exit 1; }
# 给二进制文件加可执行权限(关键)
chmod +x "${TARGET_BIN_FILE}" || { log_error "设置二进制文件可执行权限失败"; exit 1; }
# 验证部署
if [ -f "${APP_DIR}/static/index.html" ] && [ -x "${TARGET_BIN_FILE}" ]; then
log_success "程序文件部署完成:"
log_success " - 主程序:${APP_DIR}/nas-media-player.py"
log_success " - 静态文件:${APP_DIR}/static/"
log_success " - 二进制文件:${TARGET_BIN_FILE}${DETECTED_ARCH}架构)"
log_success " - 日志文件:${LOG_FILE}"
else
log_error "文件部署失败!请检查 ${APP_DIR} 目录权限"
exit 1
fi
}
create_systemd_service() {
log_step 7 7 "配置系统服务(开机启动)"
if [ "${HAS_SYSTEMD}" != "true" ]; then
log_warn "跳过systemd服务配置系统不支持"
return 0
fi
# 写入服务文件(日志固定输出到运行目录,二进制路径为运行目录)
cat > "${SYSTEMD_SERVICE}" <<EOF
[Unit]
Description=Lightweight NAS Media Player Service
Documentation=https://github.com/teasiu/nas-media-player
After=network.target network-online.target local-fs.target
[Service]
Type=simple
User=root
Group=root
WorkingDirectory=${APP_DIR}
ExecStart=${TARGET_BIN_FILE}
ExecReload=/bin/kill -HUP \$MAINPID
Restart=always
RestartSec=5
TimeoutStartSec=30
TimeoutStopSec=10
LimitNOFILE=65535
StandardOutput=append:${LOG_FILE}
StandardError=append:${LOG_FILE}
[Install]
WantedBy=multi-user.target
EOF
# 验证并启用服务
if [ -f "${SYSTEMD_SERVICE}" ]; then
systemctl daemon-reload
systemctl enable "${SERVICE_NAME}" >/dev/null 2>&1
log_success "systemd服务配置完成已启用开机启动"
else
log_error "systemd服务文件创建失败"
exit 1
fi
}
# 检查端口监听状态
check_port_listen() {
local port=$1
if command -v ss >/dev/null 2>&1; then
ss -tulpn 2>/dev/null | grep -q ":${port}.*${BIN_NAME}"
elif command -v netstat >/dev/null 2>&1; then
netstat -tulpn 2>/dev/null | grep -q ":${port}.*${BIN_NAME}"
else
return 1
fi
}
# 启动服务
start_service() {
log_info "\n========== 启动服务 =========="
# 停止旧进程
log_info "清理旧进程..."
pkill -f "${TARGET_BIN_FILE}" >/dev/null 2>&1 || true
sleep 2
if [ "${HAS_SYSTEMD}" = "true" ]; then
# systemd启动
systemctl start "${SERVICE_NAME}"
# 等待服务启动
log_info "等待服务启动(最长 ${WAIT_TIMEOUT} 秒)..."
local counter=0
while [ ${counter} -lt ${WAIT_TIMEOUT} ]; do
if systemctl is-active --quiet "${SERVICE_NAME}"; then
if check_port_listen "${PORT}"; then
log_success "服务启动成功(端口${PORT}已监听)"
return 0
else
log_warn "服务已启动,但端口${PORT}未监听嵌入式设备可能延迟建议等待1分钟后重试"
return 0
fi
fi
counter=$((counter + 1))
sleep 1
done
# 启动失败处理
log_error "服务启动失败!"
log_info "错误日志最后20行"
tail -n 20 "${LOG_FILE}" || log_warn "无法读取日志文件:${LOG_FILE}"
log_info "请检查服务状态systemctl status ${SERVICE_NAME}"
exit 1
else
# 非systemd启动前台运行
log_warn "非systemd系统将以前台方式启动服务关闭终端则停止"
nohup "${TARGET_BIN_FILE}" > "${LOG_FILE}" 2>&1 &
sleep 3
if check_port_listen "${PORT}"; then
log_success "服务前台启动成功(端口${PORT}已监听)"
log_info "日志文件:${LOG_FILE}"
else
log_error "服务启动失败!请查看日志:${LOG_FILE}"
exit 1
fi
fi
}
stop_service() {
log_info "\n========== 停止服务 =========="
# systemd停止
if [ "${HAS_SYSTEMD}" = "true" ] && systemctl is-active --quiet "${SERVICE_NAME}"; then
systemctl stop "${SERVICE_NAME}"
sleep 2
fi
# 强制清理残留进程
pkill -9 -f "${TARGET_BIN_FILE}" >/dev/null 2>&1 || true
sleep 1
if ! pgrep -f "${TARGET_BIN_FILE}" >/dev/null; then
log_success "服务已停止"
else
log_error "服务停止失败请手动执行pkill -9 -f '${TARGET_BIN_FILE}'"
exit 1
fi
}
# 获取本地IP优先非回环IPv4
get_local_ip() {
local ip
ip=$(hostname -I | awk '{print $1}' | grep -v '^127.' | grep -v '^::') || \
ip=$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | grep -v '::1' | head -n1 | awk '{print $2}' | cut -d'/' -f1) || \
ip="127.0.0.1"
echo "${ip}"
}
# 安装完成总结
show_install_summary() {
local ip=$(get_local_ip)
echo -e "\n${COLOR_BOLD}========================================${COLOR_RESET}"
echo -e "${COLOR_GREEN}🎉 ${SERVICE_NAME} 安装成功!${COLOR_RESET}"
echo -e "${COLOR_BOLD}========================================${COLOR_RESET}"
echo -e "📍 访问地址:${COLOR_BLUE}http://${ip}:${PORT}${COLOR_RESET}"
echo -e "📁 运行目录:${APP_DIR}"
echo -e "🎬 媒体目录:${VIDEO_DIR}"
echo -e "📜 日志文件:${LOG_FILE}(固定在运行目录)"
echo -e "⚙️ 运行二进制:${TARGET_BIN_FILE}${DETECTED_ARCH}架构)"
echo -e "🔧 系统服务:${SERVICE_NAME}"
echo -e "${COLOR_BOLD}========================================${COLOR_RESET}"
echo -e "✨ 功能特性:"
echo -e " ✅ 自动匹配系统架构部署二进制文件"
echo -e " ✅ 支持子目录浏览和播放"
echo -e " ✅ 支持视频文件上传(大小不限)"
echo -e " ✅ 支持创建新目录/私密目录"
echo -e " ✅ 丝滑的上传进度条显示"
echo -e " ✅ 支持MP4/AVI/MKV/WEBM等主流格式"
echo -e "${COLOR_BOLD}========================================${COLOR_RESET}"
echo -e "📋 常用命令:"
echo -e " 启动服务:${0} start 或 systemctl start ${SERVICE_NAME}"
echo -e " 停止服务:${0} stop 或 systemctl stop ${SERVICE_NAME}"
echo -e " 重启服务:${0} restart 或 systemctl restart ${SERVICE_NAME}"
echo -e " 查看状态:${0} status 或 systemctl status ${SERVICE_NAME}"
echo -e " 查看日志tail -f ${LOG_FILE}(推荐)或 journalctl -u ${SERVICE_NAME} -f"
echo -e " 卸载服务:${0} uninstall"
echo -e "${COLOR_BOLD}========================================${COLOR_RESET}"
}
# 卸载服务
uninstall_service() {
echo -e "${COLOR_BOLD}========================================${COLOR_RESET}"
echo -e "${COLOR_YELLOW}开始卸载 ${SERVICE_NAME} 服务${COLOR_RESET}"
echo -e "${COLOR_BOLD}========================================${COLOR_RESET}"
# 停止并清理systemd服务
if [ "${HAS_SYSTEMD}" = "true" ] && [ -f "${SYSTEMD_SERVICE}" ]; then
systemctl stop "${SERVICE_NAME}" >/dev/null 2>&1 || true
systemctl disable "${SERVICE_NAME}" >/dev/null 2>&1 || true
rm -f "${SYSTEMD_SERVICE}"
systemctl daemon-reload
log_success "systemd服务已清理"
fi
# 停止残留进程
stop_service >/dev/null 2>&1 || true
# 删除程序目录(包含日志文件)
log_info "删除运行目录(含日志文件)..."
rm -rf "${APP_DIR}" && log_success "运行目录 ${APP_DIR} 已删除"
# 保留媒体目录
log_warn "媒体目录 ${VIDEO_DIR} 已保留(包含您的媒体文件)"
echo -e "\n${COLOR_BOLD}========================================${COLOR_RESET}"
echo -e "${COLOR_GREEN}${SERVICE_NAME} 服务卸载完成${COLOR_RESET}"
echo -e "${COLOR_BOLD}========================================${COLOR_RESET}"
exit 0
}
# 显示帮助信息
show_help() {
echo -e "${COLOR_BOLD}========================================${COLOR_RESET}"
echo -e "${COLOR_BLUE}${SERVICE_NAME} 安装管理脚本${COLOR_RESET}"
echo -e "${COLOR_BOLD}========================================${COLOR_RESET}"
echo -e "使用方法:${0} [命令]"
echo -e "\n可用命令"
echo -e " install - 安装并启动服务(核心命令)"
echo -e " start - 启动服务"
echo -e " stop - 停止服务"
echo -e " restart - 重启服务"
echo -e " status - 查看服务状态"
echo -e " uninstall - 卸载服务(保留媒体文件)"
echo -e " help - 显示此帮助信息"
echo -e "${COLOR_BOLD}========================================${COLOR_RESET}"
exit 0
}
# 显示服务状态
show_status() {
echo -e "${COLOR_BOLD}========================================${COLOR_RESET}"
echo -e "${COLOR_BLUE}${SERVICE_NAME} 服务状态${COLOR_RESET}"
echo -e "${COLOR_BOLD}========================================${COLOR_RESET}"
echo -e "服务名称:${SERVICE_NAME}"
echo -e "运行架构:${DETECTED_ARCH:-未检测}"
echo -e "运行二进制:${TARGET_BIN_FILE:-未知}"
if [ "${HAS_SYSTEMD}" = "true" ]; then
echo -e "运行状态:$(systemctl is-active --quiet "${SERVICE_NAME}" && echo -e "${COLOR_GREEN}运行中${COLOR_RESET}" || echo -e "${COLOR_RED}已停止${COLOR_RESET}")"
else
echo -e "运行状态:$(pgrep -f "${TARGET_BIN_FILE}" >/dev/null && echo -e "${COLOR_GREEN}运行中${COLOR_RESET}" || echo -e "${COLOR_RED}已停止${COLOR_RESET}")"
fi
echo -e "监听端口:$(check_port_listen "${PORT}" && echo -e "${COLOR_GREEN}${PORT}(已监听)${COLOR_RESET}" || echo -e "${COLOR_RED}${PORT}(未监听)${COLOR_RESET}")"
echo -e "运行目录:${APP_DIR} ($([ -d "${APP_DIR}" ] && echo -e "${COLOR_GREEN}存在${COLOR_RESET}" || echo -e "${COLOR_RED}不存在${COLOR_RESET}"))"
echo -e "媒体目录:${VIDEO_DIR} ($([ -d "${VIDEO_DIR}" ] && echo -e "${COLOR_GREEN}存在${COLOR_RESET}" || echo -e "${COLOR_RED}不存在${COLOR_RESET}"))"
echo -e "日志文件:${LOG_FILE} ($([ -f "${LOG_FILE}" ] && echo -e "${COLOR_GREEN}存在${COLOR_RESET}" || echo -e "${COLOR_RED}不存在${COLOR_RESET}"))"
echo -e "系统发行版:${OS_NAME:-未知}"
echo -e "${COLOR_BOLD}========================================${COLOR_RESET}"
}
# ==============================================================================
# 主函数(处理命令参数)
# ==============================================================================
main() {
# 无参数时显示帮助
if [ $# -eq 0 ]; then
show_help
fi
local cmd="$1"
case "${cmd}" in
install)
check_root
detect_os_info
check_network
check_required_files
check_system_deps
create_app_dirs
deploy_app_files
if [ "${HAS_SYSTEMD}" = "true" ]; then
create_systemd_service
fi
start_service
show_install_summary
;;
start)
check_root
detect_os_info
start_service
;;
stop)
check_root
detect_os_info
stop_service
;;
restart)
check_root
detect_os_info
stop_service
start_service
;;
status)
detect_os_info
show_status
;;
uninstall)
check_root
detect_os_info
uninstall_service
;;
help)
show_help
;;
*)
log_error "无效命令:${cmd}(使用 ${0} help 查看帮助)"
exit 1
;;
esac
}
# ==============================================================================
# 执行入口
# ==============================================================================
main "$@"