Files
2026-03-31 20:13:15 +08:00

216 lines
7.0 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# ==============================================================================
# 【脚本功能说明】
# 1. 结合 Fastfetch在终端启动时展示随机二次元图片 (支持 SFW / NSFW 模式)。
# 2. 具备静默后台异步下载机制,库存不足时自动补货,绝不阻塞前台终端的启动。
# 3. 具备智能缓存管理机制,自动控制待展示区与已使用区 (used) 的图片数量上限。
# 4. 具备极致的网络环境容错处理,无网或弱网时自动降级 fallback避免死等。
# 5. 具备自动清理 Fastfetch 内部生成的图片转换缓存功能,防止磁盘空间无感膨胀。
# ==============================================================================
# 启用严格模式:遇到错误退出、未定义变量退出、管道中任何命令失败则失败
set -euo pipefail
# ================= 配置区域 =================
# [开关] 阅后即焚模式 (针对 Fastfetch 内部缓存)
# true = 运行后强力清空 ~/.cache/fastfetch/images/ (防止转码缓存膨胀)
# false = 保留缓存
clean_cache_mode=true
# 每次补货下载多少张
download_batch_size=10
# 最大库存上限 (待展示区)
max_cache_limit=100
# 库存少于多少张时开始补货
min_trigger_limit=60
# used 目录最大存放数量 (超过此数量将按照时间顺序删除最旧的文件)
max_used_limit=50
# ===========================================
# --- 0. 参数解析与模式设置 ---
nsfw_mode=false
if [[ "${NSFW:-}" == "1" ]]; then
nsfw_mode=true
fi
args_for_fastfetch=()
for arg in "$@"; do
if [[ "$arg" == "--nsfw" ]]; then
nsfw_mode=true
else
args_for_fastfetch+=("$arg")
fi
done
# --- 1. 目录配置 ---
if [[ "$nsfw_mode" == true ]]; then
cache_dir="$HOME/.cache/fastfetch_waifu_nsfw"
lock_file="/tmp/fastfetch_waifu_nsfw.lock"
else
cache_dir="$HOME/.cache/fastfetch_waifu"
lock_file="/tmp/fastfetch_waifu.lock"
fi
used_dir="$cache_dir/used"
mkdir -p "$cache_dir" "$used_dir"
# --- 2. 核心函数定义 ---
check_network() {
curl -sI --connect-timeout 2 "http://captive.apple.com/hotspot-detect.html" >/dev/null 2>&1
}
get_random_url() {
local timeout="--connect-timeout 5 --max-time 15"
local rand=$(( RANDOM % 3 + 1 ))
# 管道中使用 || true 确保 jq 解析失败时不会触发 pipefail 导致脚本崩溃
if [[ "$nsfw_mode" == true ]]; then
case $rand in
1) curl -s $timeout "https://api.waifu.im/images?IncludedTags=waifu&IsNsfw=true" | jq -r '.images[0].url' 2>/dev/null || true ;;
2) curl -s $timeout "https://api.waifu.pics/nsfw/waifu" | jq -r '.url' 2>/dev/null || true ;;
3) curl -s $timeout "https://api.waifu.pics/nsfw/neko" | jq -r '.url' 2>/dev/null || true ;;
esac
else
case $rand in
1) curl -s $timeout "https://api.waifu.im/images?IncludedTags=waifu&IsNsfw=false" | jq -r '.images[0].url' 2>/dev/null || true ;;
2) curl -s $timeout "https://nekos.best/api/v2/waifu" | jq -r '.results[0].url' 2>/dev/null || true ;;
3) curl -s $timeout "https://api.waifu.pics/sfw/waifu" | jq -r '.url' 2>/dev/null || true ;;
esac
fi
}
download_one_image() {
local url
url=$(get_random_url || true)
if [[ -n "$url" && "$url" =~ ^http ]]; then
local filename="waifu_$(date +%s%N)_$RANDOM.jpg"
local target_path="$cache_dir/$filename"
# 使用 || true 防止 curl 意外崩溃
curl -s -L --connect-timeout 5 --max-time 15 -o "$target_path" "$url" || true
if [[ -s "$target_path" ]]; then
if command -v file >/dev/null 2>&1; then
if ! file --mime-type "$target_path" | grep -q "image/"; then
rm -f "$target_path"
fi
fi
else
rm -f "$target_path"
fi
fi
}
background_job() {
(
# 1. 彻底切断标准输入输出,变成纯粹的后台幽灵进程
exec </dev/null >/dev/null 2>&1
# 2. 忽略终端挂断信号
trap '' HUP
# 3. 局部关闭严格模式,确保后台尽力而为,不因单次网络错误退出
set +e
# 4. 获取文件描述符 200 的排他锁,防止并发下载
exec 200>"$lock_file"
flock -n 200 || exit 0
# 网络检查,没网就悄悄退出
if ! check_network; then
exit 0
fi
# 开启空 glob 扩展
shopt -s nullglob
# --- 补货逻辑 ---
local current_files=("$cache_dir"/*.jpg)
if (( ${#current_files[@]} < min_trigger_limit )); then
for ((i=0; i<download_batch_size; i++)); do
download_one_image
sleep 0.5
done
fi
# --- 清理过多库存逻辑 ---
local final_files=("$cache_dir"/*.jpg)
if (( ${#final_files[@]} > max_cache_limit )); then
local skip_lines=$(( max_cache_limit + 1 ))
local cache_to_delete
# 只有明确文件存在时才执行 ls避免 nullglob 导致的当前目录误删
mapfile -t cache_to_delete < <(ls -tp "$cache_dir"/*.jpg 2>/dev/null | tail -n +"$skip_lines")
if (( ${#cache_to_delete[@]} > 0 )); then
rm -f -- "${cache_to_delete[@]}"
fi
fi
) &
}
# --- 3. 主程序逻辑 ---
# 开启空 glob 扩展,安全获取文件列表
shopt -s nullglob
files=("$cache_dir"/*.jpg)
num_files=${#files[@]}
selected_img=""
if (( num_files > 0 )); then
rand_index=$(( RANDOM % num_files ))
selected_img="${files[$rand_index]}"
background_job
else
echo "库存不够啦!正在去搬运新的图片,请稍等哦..."
if check_network; then
# 前台下载时暂时关闭严格模式,防止网络异常导致脚本闪退
set +e
download_one_image
set -e
else
echo "网络好像不太通畅,无法下载新图片 QAQ"
fi
# 重新获取文件列表
files=("$cache_dir"/*.jpg)
if (( ${#files[@]} > 0 )); then
selected_img="${files[0]}"
background_job
fi
fi
if [[ -n "$selected_img" && -f "$selected_img" ]]; then
# 显示图片
fastfetch --logo "$selected_img" --logo-preserve-aspect-ratio true "${args_for_fastfetch[@]}"
# === 移动到 used 目录 ===
mv "$selected_img" "$used_dir/"
# === 检查 used 目录并清理旧图 ===
used_files=("$used_dir"/*.jpg)
if (( ${#used_files[@]} > max_used_limit )); then
skip_lines=$(( max_used_limit + 1 ))
mapfile -t files_to_delete < <(ls -tp "$used_dir"/*.jpg 2>/dev/null | tail -n +"$skip_lines")
if (( ${#files_to_delete[@]} > 0 )); then
rm -f -- "${files_to_delete[@]}"
fi
fi
# 清理 Fastfetch 内部缓存
if [[ "$clean_cache_mode" == true ]]; then
rm -rf "$HOME/.cache/fastfetch/images"
fi
else
echo "图片获取失败了,这次只能先显示默认的 Logo 啦 QAQ"
fastfetch "${args_for_fastfetch[@]}"
fi