背景:
之前使用的iPhone 15 Pro,使用的Windows资源管理器当中复制导出的实况照片,复制出来的格式例如IMG_0001.JPG, IMG_0001.MOV。之后手机就卖掉了。现在使用的iPhone 14 Pro Max,想要导回之前备份的实况照片。尝试使用爱思助手导入,虽然文件名都一样,但是仍然会报错:
是不是非常可恶?
但是使用爱思助手导出实况照片、出来的也是JPG+MOV。再重新导回去,发现一切正常。这么说一定是可以导入的,肯定是哪里出了问题。
咨询了爱思助手的客服,完全不理。没办法,只好自己研究咯。
解决方法:
读取MOV的EXIF信息
exiftool -a -u -g1 IMG_4676.MOV > IMG_4676.MOV.TXT
---- Keys ----字段下面的Content Identifier键值,就是JPG需要被修改成的Media Group UUID。也就是说,实际上,他们是依据这个uuid来判断视频和照片的关联。发现爱思助手导出的JPG和MOV正好也是对应的。所以实际上,针对资源管理器复制出来的,我们只要读取MOV的这个键值,把我们从JPG当中的Media Group UUID修改成一样的即可!
# MOV
Content Identifier : E2523D1E-7C8A-423B-A8DB-53C8A073457F
# JPG
Media Group UUID : E2523D1E-7C8A-423B-A8DB-53C8A073457F
修改JPG当中的Media Group UUID
利用010Editor,打开文件,
直接把我原始照片里面的UUID替换掉。保存,即可导入成功。
这个UUID在文件中是明文TEXT存储的,在批量处理的情况下我们可以直接写一个脚本,利用exiftool读取出来MOV文件的Content Identifier,读取JPG的Media Group UUID,然后字节流模式打开JPG,在文件当中直接执行替换即可。
批量处理代码
Windows平台,下载exiftool:
https://exiftool.org/index.html
下载这个stand-alone的windows版本 。
下载下来的zip里面就是一个exe文件,重命名为exiftool.exe即可。
创建下面的python文件,把exe和这个python文件放到一个目录下面。
这个代码会将成对的JPG+MOV都拷贝到一个新的文件夹里面去。这样在原始文件夹里面的文件都是需要单独导入成静态照片和普通视频的。方便导入。在爱思助手里面就直接选择程序生成的LivePhoto文件夹。
建议在运行此代码之前,把资料先备份一份,以防万一。毕竟数据无价,谨慎操作。
import subprocess
import os
import shutil
from collections import defaultdict
import re
def get_exif_data(file_path):
try:
process = subprocess.Popen(
['exiftool', '-a', '-u', '-g1', file_path],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout, stderr = process.communicate()
if process.returncode != 0:
print(f"Error: {stderr.strip()}")
return None
return stdout.strip()
except FileNotFoundError:
print("ExifTool is not installed or not found in the system path.")
return None
def replace_uuid_in_jpg(jpg_file_path, old_uuid, new_uuid):
with open(jpg_file_path, 'rb+') as jpg_file:
file_content = jpg_file.read()
updated_content = file_content.replace(old_uuid, new_uuid)
jpg_file.seek(0)
jpg_file.write(updated_content)
jpg_file.truncate()
def fix_jpg_for_ios_import(jpg_file_path, mov_file_path):
exif_data = get_exif_data(mov_file_path).split(b'\n')
content_identifier = None
for item in exif_data:
if b'Content Identifier' in item:
content_identifier = item.split(b':')[-1].strip()
break
assert content_identifier is not None
assert re.match(r'[0-9A-F]{8}(-[0-9A-F]{4}){3}-[0-9A-F]{12}', content_identifier.decode().upper())
print(b' ' + mov_file_path.encode() + b' | ' + content_identifier)
exif_data = get_exif_data(jpg_file_path).split(b'\n')
media_group_uuid = None
for item in exif_data:
if b'Media Group UUID' in item or b'Content Identifier' in item:
media_group_uuid = item.split(b':')[-1].strip()
break
assert media_group_uuid is not None
assert re.match(r'[0-9A-F]{8}(-[0-9A-F]{4}){3}-[0-9A-F]{12}', media_group_uuid.decode().upper())
print(b' ' + jpg_file_path.encode() + b' | ' + media_group_uuid)
replace_uuid_in_jpg(jpg_file_path, media_group_uuid, content_identifier)
def get_matching_files(directory):
# 字典用于存储文件名前缀及其对应的后缀
file_dict = defaultdict(set)
for filename in os.listdir(directory):
name, ext = os.path.splitext(filename)
ext = ext.upper()
# 只处理JPG和MOV文件
if ext in {'.JPG', '.MOV'}:
file_dict[name].add(ext)
# 找到同时具有JPG和MOV后缀的文件名前缀
matching_files = [name for name, exts in file_dict.items() if {'.JPG', '.MOV'} <= exts]
return matching_files
if __name__ == '__main__':
jpg_mov_path = 'I:/iPhone15Pro/202310__'
file_prefixes = get_matching_files(jpg_mov_path)
if not os.path.exists(os.path.join(jpg_mov_path, "LivePhoto")):
os.mkdir(os.path.join(jpg_mov_path, "LivePhoto"))
for file_prefix in file_prefixes:
dst_jpg_path = os.path.join(jpg_mov_path, "LivePhoto", f"{file_prefix}.JPG")
dst_mov_path = os.path.join(jpg_mov_path, "LivePhoto", f"{file_prefix}.MOV")
shutil.move(os.path.join(jpg_mov_path, f"{file_prefix}.JPG"), dst_jpg_path)
shutil.move(os.path.join(jpg_mov_path, f"{file_prefix}.MOV"), dst_mov_path)
fix_jpg_for_ios_import(dst_jpg_path, dst_mov_path)
尝试过的方法
先用手上的iPhone 14 Pro Max随便拍两张照片,一个通过爱思导出,一个通过Windows资源管理器复制。对比一下出来的文件
很明显这两个文件大小差距很大。
MOV的话,文件大小一样,算了一下md5也一样。说明问题不在MOV上,JPG有问题。
先启动WSL,用binwalk,foremost看一下:
binwalk IMG_4676.JPG
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 JPEG image data, JFIF standard 1.01
34 0x22 TIFF image data, big-endian, offset of first image directory: 8
2828 0xB0C TIFF image data, big-endian, offset of first image directory: 8
2214234 0x21C95A TIFF image data, big-endian, offset of first image directory: 8
binwalk 2024_05_22_12_44_IMG_4676.JPG
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 JPEG image data, JFIF standard 1.01
30 0x1E TIFF image data, big-endian, offset of first image directory: 8
foremost解出来两张图片。一张是全彩的正常图片(00000000.jpg),携带大量有效metadata(例如拍摄机型、时间、GPS等)。另一张点开直接看到的是灰度图像,没有看到有效的metadata。
修改文件名对应MOV,导入00000000.jpg也失败。
那么就用exiftool看一下。
exiftool -a -u -g1 IMG_4676.JPG > IMG_4676_bad.TXT
exiftool -a -u -g1 2024_05_22_12_44_IMG_4676.JPG > 2024_05_22_12_44_IMG_4676.JPG.TXT
使用Beyond Compare对比一下哪里出问题了。
存在大量不同。尤其是MP系列字段。因为本身存在2张照片
先不管MPF0字段。先尝试直接利用exiftool复制爱思助手导出图片的exif信息,直接覆盖过去:
exiftool -TagsFromFile 2024_05_22_12_44_IMG_4676.JPG -all:all IMG_4676.JPG
直接成功了!
说明只需要修改这里面不同的EXIF信息即可修复导入!
尝试单独修改了几个项目,导入,无果。最终把目光看向了Apple字段下面的Media Group UUID
但是这个项目还不太好改,询问GPT,下面这个方法不好用。
exiftool -Apple:MediaGroupUUID="E2523D1E-7C8A-423B-A8DB-53C8A073457F" IMG_4676.JPG
Warning: [minor] Maker notes could not be parsed - IMG_4676.JPG
0 image files updated
1 image files unchanged
尝试了其他的命令行修改的方法也不行。只能从文件实际内容去修改。