mkdir -p /data/adb/hvc1-auto
cat > /data/adb/hvc1-auto/process.sh <<'EOF'
#!/system/bin/sh
# ============================================================
# Android HEVC / hvc1 自动兼容优化脚本
#
# 功能:
# 1. MP4:
# HEVC 且不是 hvc1 → 无损重新封装为 hvc1 + faststart
#
# 2. MKV:
# HEVC → 生成同名 MP4
# 优先复制 AAC 音轨;
# 没有 AAC 时,把第一条音轨转换成 AAC
#
# 3. 自动跳过:
# - 已经是 hvc1 的 MP4
# - 非 HEVC 视频
# - 正在写入/刚上传的文件
# - 临时文件
#
# 默认扫描目录:
# /storage/emulated/0/DCIM
#
# ============================================================
ROOT="/storage/emulated/0/DCIM"
WORK="/data/adb/hvc1-auto"
LOG="$WORK/hvc1-auto.log"
LOCK="$WORK/.lock"
# 文件最后修改至少超过多少秒后才处理
# 防止文件还没上传完就开始重新封装
MIN_AGE=120
# MKV 成功转换后是否删除原 MKV
#
# 0 = 保留原 MKV(推荐、安全)
# 1 = 校验成功后删除原 MKV
#
DELETE_MKV_AFTER_SUCCESS=0
mkdir -p "$WORK"
log()
{
echo "$(date '+%Y-%m-%d %H:%M:%S') $*" >> "$LOG"
}
# ------------------------------------------------------------
# 防止脚本重复运行
# ------------------------------------------------------------
if ! mkdir "$LOCK" 2>/dev/null; then
exit 0
fi
trap 'rmdir "$LOCK" 2>/dev/null' EXIT INT TERM
# ------------------------------------------------------------
# 检查 FFmpeg
# ------------------------------------------------------------
FFMPEG="$(command -v ffmpeg)"
FFPROBE="$(command -v ffprobe)"
if [ -z "$FFMPEG" ] || [ -z "$FFPROBE" ]; then
log "ERROR: ffmpeg / ffprobe not found"
exit 1
fi
# ------------------------------------------------------------
# 判断文件是否已经写入完成
# ------------------------------------------------------------
file_ready()
{
file="$1"
mtime="$(stat -c %Y "$file" 2>/dev/null)"
[ -z "$mtime" ] && return 1
now="$(date +%s)"
age=$((now - mtime))
[ "$age" -ge "$MIN_AGE" ]
}
# ------------------------------------------------------------
# 复制原文件权限/时间
# ------------------------------------------------------------
copy_attr()
{
src="$1"
dst="$2"
uid="$(stat -c %u "$src" 2>/dev/null)"
gid="$(stat -c %g "$src" 2>/dev/null)"
mode="$(stat -c %a "$src" 2>/dev/null)"
[ -n "$uid" ] && [ -n "$gid" ] && \
chown "$uid:$gid" "$dst" 2>/dev/null
[ -n "$mode" ] && \
chmod "$mode" "$dst" 2>/dev/null
touch -r "$src" "$dst" 2>/dev/null
}
# ------------------------------------------------------------
# MP4:hev1 / 非 hvc1 → hvc1
# ------------------------------------------------------------
process_mp4()
{
file="$1"
codec="$("$FFPROBE" \
-v error \
-select_streams v:0 \
-show_entries stream=codec_name \
-of default=noprint_wrappers=1:nokey=1 \
"$file" 2>/dev/null)"
[ "$codec" = "hevc" ] || return
tag="$("$FFPROBE" \
-v error \
-select_streams v:0 \
-show_entries stream=codec_tag_string \
-of default=noprint_wrappers=1:nokey=1 \
"$file" 2>/dev/null)"
# 已经兼容,无需再次处理
[ "$tag" = "hvc1" ] && return
tmp="${file%.*}.hvc1tmp.mp4"
rm -f "$tmp"
log "START MP4: $file"
log " codec=$codec tag=$tag"
"$FFMPEG" \
-nostdin \
-hide_banner \
-loglevel error \
-y \
-i "$file" \
-map 0 \
-c copy \
-tag:v:0 hvc1 \
-movflags +faststart \
"$tmp" >> "$LOG" 2>&1
if [ $? -ne 0 ]; then
log "FAIL MP4: ffmpeg failed: $file"
rm -f "$tmp"
return
fi
newtag="$("$FFPROBE" \
-v error \
-select_streams v:0 \
-show_entries stream=codec_tag_string \
-of default=noprint_wrappers=1:nokey=1 \
"$tmp" 2>/dev/null)"
if [ "$newtag" != "hvc1" ]; then
log "FAIL MP4: hvc1 validation failed: $file"
rm -f "$tmp"
return
fi
# 再确认文件本身可以正常读取
"$FFPROBE" \
-v error \
-show_entries format=duration \
-of default=noprint_wrappers=1:nokey=1 \
"$tmp" >/dev/null 2>&1
if [ $? -ne 0 ]; then
log "FAIL MP4: output verification failed: $file"
rm -f "$tmp"
return
fi
copy_attr "$file" "$tmp"
mv -f "$tmp" "$file"
log "OK MP4: converted to hvc1: $file"
}
# ------------------------------------------------------------
# MKV:HEVC → 浏览器兼容 MP4
# ------------------------------------------------------------
process_mkv()
{
file="$1"
codec="$("$FFPROBE" \
-v error \
-select_streams v:0 \
-show_entries stream=codec_name \
-of default=noprint_wrappers=1:nokey=1 \
"$file" 2>/dev/null)"
# 当前版本只自动处理 HEVC
[ "$codec" = "hevc" ] || return
base="${file%.*}"
out="${base}.mp4"
tmp="${base}.hvc1tmp.mp4"
# 已经有 MP4,则不覆盖
if [ -f "$out" ]; then
tag="$("$FFPROBE" \
-v error \
-select_streams v:0 \
-show_entries stream=codec_tag_string \
-of default=noprint_wrappers=1:nokey=1 \
"$out" 2>/dev/null)"
if [ "$tag" = "hvc1" ]; then
return
fi
log "SKIP MKV: target MP4 already exists: $out"
return
fi
rm -f "$tmp"
# 查找 AAC 音轨
aac_index="$("$FFPROBE" \
-v error \
-select_streams a \
-show_entries stream=index,codec_name \
-of csv=p=0 \
"$file" 2>/dev/null |
awk -F',' '$2=="aac"{print $1; exit}')"
# 查找第一条音轨
first_audio="$("$FFPROBE" \
-v error \
-select_streams a \
-show_entries stream=index \
-of csv=p=0 \
"$file" 2>/dev/null |
head -n 1)"
log "START MKV: $file"
# --------------------------------------------------------
# 情况 1:已有 AAC
# 视频和音频都直接复制,不重新编码
# --------------------------------------------------------
if [ -n "$aac_index" ]; then
log " AAC stream=$aac_index"
"$FFMPEG" \
-nostdin \
-hide_banner \
-loglevel error \
-y \
-i "$file" \
-map 0:v:0 \
-map 0:"$aac_index" \
-map_metadata 0 \
-map_chapters 0 \
-c:v copy \
-tag:v:0 hvc1 \
-c:a copy \
-movflags +faststart \
"$tmp" >> "$LOG" 2>&1
# --------------------------------------------------------
# 情况 2:没有 AAC,但存在音轨
# 视频不重编码,只把音频转成 AAC
# --------------------------------------------------------
elif [ -n "$first_audio" ]; then
log " No AAC; converting audio stream=$first_audio"
"$FFMPEG" \
-nostdin \
-hide_banner \
-loglevel error \
-y \
-i "$file" \
-map 0:v:0 \
-map 0:"$first_audio" \
-map_metadata 0 \
-map_chapters 0 \
-c:v copy \
-tag:v:0 hvc1 \
-c:a aac \
-b:a 192k \
-movflags +faststart \
"$tmp" >> "$LOG" 2>&1
# --------------------------------------------------------
# 情况 3:没有音频
# --------------------------------------------------------
else
log " No audio stream"
"$FFMPEG" \
-nostdin \
-hide_banner \
-loglevel error \
-y \
-i "$file" \
-map 0:v:0 \
-map_metadata 0 \
-map_chapters 0 \
-c:v copy \
-tag:v:0 hvc1 \
-an \
-movflags +faststart \
"$tmp" >> "$LOG" 2>&1
fi
if [ $? -ne 0 ]; then
log "FAIL MKV: ffmpeg failed: $file"
rm -f "$tmp"
return
fi
newtag="$("$FFPROBE" \
-v error \
-select_streams v:0 \
-show_entries stream=codec_tag_string \
-of default=noprint_wrappers=1:nokey=1 \
"$tmp" 2>/dev/null)"
if [ "$newtag" != "hvc1" ]; then
log "FAIL MKV: hvc1 validation failed: $file"
rm -f "$tmp"
return
fi
"$FFPROBE" \
-v error \
-show_entries format=duration \
-of default=noprint_wrappers=1:nokey=1 \
"$tmp" >/dev/null 2>&1
if [ $? -ne 0 ]; then
log "FAIL MKV: output verification failed: $file"
rm -f "$tmp"
return
fi
copy_attr "$file" "$tmp"
mv -f "$tmp" "$out"
log "OK MKV: created compatible MP4: $out"
if [ "$DELETE_MKV_AFTER_SUCCESS" = "1" ]; then
rm -f "$file"
log "DELETE MKV: $file"
fi
}
# ------------------------------------------------------------
# 主扫描
# ------------------------------------------------------------
log "===== SCAN START ====="
find "$ROOT" -type f \
\( -iname '*.mp4' -o -iname '*.mkv' \) \
-print0 2>/dev/null |
while IFS= read -r -d '' file
do
case "$file" in
*.hvc1tmp.mp4)
continue
;;
esac
file_ready "$file" || continue
lower="$(echo "$file" | tr '[:upper:]' '[:lower:]')"
case "$lower" in
*.mp4)
process_mp4 "$file"
;;
*.mkv)
process_mkv "$file"
;;
esac
done
log "===== SCAN END ====="
exit 0
EOF
chmod 755 /data/adb/hvc1-auto/process.sh
如果脚本执行有问题,多半是存储路径。将脚本给和存储路径给ai让它改。