跳转至

iCloud 媒体文件同步与处理

预计阅读时长 : 13 分钟

整体逻辑

flowchart TB
    iCloudpd([iCloudpd 下载文件]) --> HEICCheck{图片格式\n是否为 HEIC} & MOVCheck{视频格式\n是否为 MOV} 
    HEICCheck -- 是 --> TransferHEIC[使用 iCloudpd 自动转化]
    MOVCheck -- 是 --> CronCopy[监测脚本自动复制\n文件到监测文件夹] --> TransferMOV[使用 Handbrake 自动转化] --> CronMove([转移脚本自动转移\n文件原始文件夹])

文件下载

作为尊贵的 2T iCloud 用户,拍起图片和视频来可谓是随心所欲,不过带来的副作用就是甜蜜的烦恼了:筛选图片和视频实在是让人头秃。

使用 iCloudpd ,可以自动把 iCloud 的图片全部下载到本地,并自动将 HEIC 格式的图片转化为 JPG,再配合 Handbrake 自动把 MOV 格式的视频转化为 MP4,再一步进行管理就方便了许多。

boredazfcuk/icloudpd ⧉ 是目前最完善的 icloudpd 的 Docker 版本,从 Unraid 的应用商店里面可以直接安装配置脚本。为了得到最佳的使用体验,还是建议参照以下配置使用 Docker-Compose 创建服务:

version: "3.8"
services:
  icloudpd:
    image: boredazfcuk/icloudpd
    container_name: icloudpd
    network_mode: bridge
    hostname: Unraid
    environment:
      - apple_id=xiaobuyao@gmail.com
      - authentication_type=2FA
      - icloud_china=True #(1)
      - auth_china=True
      - user_id=1000
      - group=users #(2)
      - group_id=100
      - force_gid=True
      - download_path=/home/xxxx/iCloud
      - synchronisation_interval=86400
      - directory_permissions=750
      - file_permissions=640
      - folder_structure={:%Y/%m} 
      - skip_check=True
      - convert_heic_to_jpeg=True #(3)
      - jpeg_quality=100
      - auto_delete=True
      - TZ=Asia/Shanghai
    volumes:
      - /mnt/user/appdata/icloudpd:/config # 配置文件路径
      - /mnt/user/Backup/Likai/Photo/iCloud:/home/xxxx/iCloud # 下载文件路径
    restart: unless-stopped
  1. icloud_china 参数用于指定是否使用国内的 iCloud 服务,如果使用国内的服务,需要将 auth_china 参数也设置为 True
  2. group 参数用于指定下载文件的用户 ID 所在的 Group,如果对应 groupd_id 已经存在,那么需要将 force_gid 参数设置为 True,以及将 group 设为对应的 group 名称。
  3. convert_heic_to_jpeg 参数用于指定是否将 HEIC 格式的图片转化为 JPG 格式。

根据 Apple 最新安全性规范,icloudpd 强制要求使用 2FA 以及 App-Specific Passwords 进行账号授权。因此,在容器安装完成之后,还需要在 Unraid 的终端中执行以下命令进行初始化:

docker exec -it icloudpd sync-icloud.sh --Initialise

最后,作为防呆设计,还需要在文件存储目录中手动创建一个 .mounted 文件,这样就能启动下载的任务了。

格式转换

简要说明

在使用 iCloudpd 将所有媒体文件下载下来之后,接下去的流程就是将苹果的专用格式转化一份跨平台支持的格式,便于后续的处理流程使用。

对于 HEIC 格式的图片,可以根据上述的配置使用 iCloudpd 在下载的同时完成转化,而MOV 格式的文件转化则需要使用 Handbrake 来进行转化。

它可以自动监控放置到指定文件夹中的文件,将其按照指定的格式转化之后再放入输出文件夹。有了这个基础,我们通过定时脚本自动将 iCloud 中符合条件的 MOV 文件筛选出来,并自动拷贝至监控文件夹就可以完成全自动的转化任务了。

需要特别说明的是,早期的 iCloud 中可能会在不同的月份中出现同名的文件,因此在处理文件的时候,需要使用 日期_文件名 的临时文件名作为唯一标示符,这样可以避免重复文件名的文件在处理时被遗漏。

处理逻辑

flowchart TD

A[监控定时脚本查找\n所有 MOV/mov 结尾的文件] --> B[切分文件路径]
B --> F[临时文件名]
B --> D[后缀名]
B --> E[目录] -----> H
F --> G[检查是否有与临时文件名相同的 MP4/mp4 文件]
G -- 否 --> J[判断 Handbrake 监控目录是否有同名 MOV/mov 文件] 
J -- 否 --> K[将文件用临时文件名\n复制至 Handbrake 监控目录] --> L[等待 Handbrake 自动转换格式完毕] & H[将文件相关信息添加\n至待处理文件列表] --> M[等待文件转移定时脚本调用]

Handbrake 配置

为了保证拍摄的视频能有最佳的效果,iPhone 的默认视频格式为 MOV。麻烦的是,这种格式在 Windows 系统以及浏览器上都无法顺畅的预览。因此,为了更好的对视频文件进行管理,可以使用 Handbrake 自动将 MOV 格式的文件转化为 MP4 格式,这样既能保证源文件的高质量,又能便于管理。

Unraid 中可以参照以下的配置,定义好导出目录、显卡支持和 CJK 文本支持,再配合后面的自定义脚本,即可完成全自动化的处理流程。

version: "3"
services:
  handbrake:
    image: jlesage/handbrake:latest
    container_name: handbrake
    network_mode: bridge
    environment:
      - UID=1000
      - GID=100
      - ENABLE_CJK_FONT=1  # 添加中文支持
      - DISPLAY_WIDTH=1280
      - DISPLAY_HEIGHT=1240
      - TZ=Asia/Shanghai
    volumes:
      - /mnt/user/appdata/HandBrake:/config:rw # 配置文件路径
      - /mnt/user/Download:/storage:ro # 媒体文件路径
      - /mnt/user/Video/Watch:/watch:rw # 监控文件路径
      - /mnt/user/Video/Output:/output:rw # 输出文件路径
    ports:
      - 5800:5800 # WebUI 端口
      - 5900:5900 # VNC 端口
    devices:
      - /dev/dri/renderD128:/dev/dri/renderD128 # 配置转码用显卡
    restart: unless-stopped

监控脚本

设置好了 Handbrake 的监控文件夹后,只要将媒体文件放入即可以实现自动转换的功能。以下的脚本会使用 Cron 自动定时运行定期检查新下载的文件,然后将符合条件的文件复制到监控文件夹中。

#! /bin/bash
SCRIPT_DIR=$(cd $(dirname ${BASH_SOURCE[0]}); pwd)
iCloud_PATH="/mnt/user/Backup/Likai/Photo/iCloud/2023"
MOV_LIST=$(find ${iCloud_PATH} -iname "*.mov")

### 清空待处理文件列表 ###

if [ -f ${SCRIPT_DIR}/mov.list ]; then
    cat /dev/null > ${SCRIPT_DIR}/mov.list
fi

### 解决文件名中有空格的问题 ###

IFS_BACKUP=$IFS
IFS=$(echo -en "\n\b")

### 判断视频是否已转换,更新待处理文件列表 ###

for mov_file in $MOV_LIST
do
    dir_all=${mov_file%/*}
    dir_date=$(echo $dir_all | awk -F/ '{print $8"_"$9"_"}')
    file_all=${mov_file##*/}
    file_name=${file_all%\.*}
    file_ext=${file_all##*\.}
    if [ ! -f "${dir_all}/${file_name}.MP4" ] && [ ! -f "${dir_all}/${file_name}.mp4" ]; then
        echo -e "${file_name}:${file_ext}:${dir_all#\.}:${dir_date}${file_name}" >> ${SCRIPT_DIR}/mov.list
        let mov_file_number++
    fi
done

printf "%s\t总计%4d 个文件有转换需要\n" "$(date +"%Y-%m-%d %H:%M:%S")" ${mov_file_number} | tee -a ${SCRIPT_DIR}/icloud.log

### 将待处理文件拷贝至 Handbrake 监控文件夹 ###

WATCH_PATH="/mnt/user/Video/Watch"

for cp_file in $(cat ${SCRIPT_DIR}/mov.list)
do
    file_path=$(echo $cp_file | awk 'BEGIN{FS=":"} {print $3"/"$1"."$2}')
    file_name=$(echo $cp_file | awk 'BEGIN{FS=":"} {print $4"."$2}')
    if [ ! -f ${WATCH_PATH}/${file_name} ]; then
        # 使用复制硬链接方式,减少复制占用的空间和时间
        cp ${file_path} ${WATCH_PATH}/${file_name}
        # 为复制的文件修改权限,使 Handbrake 可以进行处理
        chmod a=rw ${WATCH_PATH}/${file_name}
        let move_num++
    fi
done

IFS=$IFS_BACKUP

printf "%s\t复制%4d 个文件到监控目录\n" "$(date +"%Y-%m-%d %H:%M:%S")" ${move_num:-0} | tee -a ${SCRIPT_DIR}/icloud.log

文件转移

简要说明

Handbrake 将文件转化完之后,接下去的流程就是将转化后的文件复制回源文件夹。这样在使用例如 PhotoPrism 等第三方软件进行管理的时候,就可以将转化后的文件作为预览文件,同时也能便捷的定位到源文件。

产品逻辑

flowchart TB
A([获取 Handbrake 输出目录文件列表]) --> B{文件名是否在\n待处理文件列表中}
B -- 是 --> C[将文件恢复原名并移动至原始目录] --> D[删除监控文件夹的待处理文件] --> E([输出操作记录到日志文件])
B -- 否 --> D

转移脚本

和监控脚本类似,这个转移脚本也使用 Cron 进行定时操作,将转化后的文件移动回原始目录。

#! /bin/bash

SCRIPT_DIR=$(cd $(dirname ${BASH_SOURCE[0]}); pwd)
WATCH_PATH="/mnt/user/Video/Watch"
OUTPUT_PATH="/mnt/user/Video/Output"

tred_files_list=$(ls ${OUTPUT_PATH})
todo_files_list=$(cat ${SCRIPT_DIR}/mov.list)

### 解决文件名中有空格的问题 ###

IFS_BACKUP=$IFS
IFS=$(echo -en "\n\b")

for tred_file in ${tred_files_list}
do
    file_detail=$(grep "${tred_file%\.*}" <<< ${todo_files_list})
    file_name=$(echo ${file_detail} | awk 'BEGIN{FS=":"} {print $1}')
    file_ext=$(echo ${file_detail} | awk 'BEGIN{FS=":"} {print $2}')
    file_path=$(echo ${file_detail} | awk 'BEGIN{FS=":"} {print $3}')
    # 将转化后的文件移动回原目录时改名回原来的文件名
    mv_result=$(mv -f "${OUTPUT_PATH}/${tred_file}" "${file_path}/${file_name}.mp4")
    rm_result=$(rm -f "${WATCH_PATH}/${tred_file%\.*}.${file_ext}")
    if [ -z ${mv_result} ] && [ -z ${rm_result} ];then
        let success_num++
    fi
done

IFS=$IFS_BACKU

printf "%s\t转移%4d 个文件回原始目录\n" "$(date +"%Y-%m-%d %H:%M:%S")" ${success_num:-0} | tee -a ${SCRIPT_DIR}/icloud.log

定时任务

要实现整个流程的完全自动化,最后一步就是将定时任务加入到 Cron 中按计划执行。

至此,这个流程就不再需要人工干预了,并且有日志文件记录操作细节,整个需求得到了完美的实现,🤭 。

1
2
3
4
# Run daily icloud mov trans job at 12:40 every day:
40 12 * * * sh /etc/script/icloud/todo.sh  &> /dev/null
# Run daily icloud files move job at 23:40 every day: #(1)
40 22 * * * sh /etc/script/icloud/tred.sh  &> /dev/null
  1. 为了避免 Handbrake 还未完成转换,就开始移动文件,这里将转换脚本的执行时间设置为转换脚本的 10 小时之后。