背景
最近从亲宝宝备份了6年来宝宝的照片,发现有些照片会堆到备份的日期上,而不是拍摄时间。
经过排查,这些照片的exif信息里是没有拍摄时间的,这可能是这些照片是我和我老婆在微信上传的照片再保存下来导致的。
亲宝宝的照片是按日期和时间戳保存的,
整活
飞牛相册默认是按文件拍摄时间排序的,如果拍摄时间为空则会以文件的最后修改时间作为拍摄时间,这些拍摄时间没有记录的照片补上时间就行了。
代码
废话不多说,贴代码:
import os
import re
from datetime import datetime
from exiftool import ExifToolHelper
# 支持的文件扩展名(图片和视频)
SUPPORTED_EXTENSIONS = {
'image': ['.jpg', '.jpeg', '.tiff', '.tif', '.png', '.heic', '.raw'],
'video': ['.mp4', '.mov', '.avi', '.mkv', '.flv', '.wmv', '.mpeg', '.mpg']
}
ALL_EXTENSIONS = [ext for ext_list in SUPPORTED_EXTENSIONS.values() for ext in ext_list]
# 正则表达式:匹配文件名中的日期格式
PATTERN_YYYYMMDD = re.compile(r'(\d{4})[-_.]?(\d{2})[-_.]?(\d{2})') # YYYY-MM-DD等
PATTERN_MMDDYYYY = re.compile(r'(\d{2})[-_.]?(\d{2})[-_.]?(\d{4})') # MM-DD-YYYY等
PATTERN_DDMMYYYY = re.compile(r'(\d{2})[-_.]?(\d{2})[-_.]?(\d{4})') # DD-MM-YYYY等
EXIFTOOL_PATH = r"E:\code\picture_edit\exiftool-13.39_64\exiftool(-k).exe"
def get_capture_time(helper, file_path):
"""从元数据中获取拍摄时间,返回datetime对象或None"""
try:
# 读取关键日期标签
tags = helper.get_tags(file_path, tags=['DateTimeOriginal', 'CreateDate', 'ModifyDate'])
if not tags:
return None
tag_data = tags[0]
date_str = None
# 优先检查常用标签(不同格式标签位置不同)
for tag in [
'EXIF:DateTimeOriginal', 'QuickTime:CreateDate',
'XMP:CreateDate', 'File:CreateDate', 'RIFF:CreateDate'
]:
if tag in tag_data:
date_str = tag_data[tag]
break
if not date_str:
return None
# 尝试解析不同格式的日期字符串
for fmt in [
'%Y:%m:%d %H:%M:%S', '%Y-%m-%d %H:%M:%S',
'%Y%m%d %H:%M:%S', '%Y:%m:%d %H:%M:%S%z',
'%Y-%m-%dT%H:%M:%S'
]:
try:
return datetime.strptime(date_str, fmt)
except ValueError:
continue
return None
except Exception as e:
print(f"读取 {file_path} 元数据失败: {e}")
return None
def extract_date_from_filename(filename):
"""从文件名提取日期,返回datetime对象或None"""
base_name = os.path.splitext(filename)[0] # 去除扩展名
# 尝试匹配YYYY-MM-DD格式
match = PATTERN_YYYYMMDD.search(base_name)
if match:
try:
return datetime(int(match.group(1)), int(match.group(2)), int(match.group(3)))
except ValueError:
pass
# 尝试匹配MM-DD-YYYY格式
match = PATTERN_MMDDYYYY.search(base_name)
if match:
try:
return datetime(int(match.group(3)), int(match.group(1)), int(match.group(2)))
except ValueError:
pass
# 尝试匹配DD-MM-YYYY格式
match = PATTERN_DDMMYYYY.search(base_name)
if match:
try:
return datetime(int(match.group(3)), int(match.group(2)), int(match.group(1)))
except ValueError:
pass
return None
def set_capture_time(helper, file_path, dt):
"""将日期写入元数据,返回是否成功"""
try:
date_str = dt.strftime('%Y:%m:%d %H:%M:%S') # EXIF标准格式
ext = os.path.splitext(file_path)[1].lower()
tags = {}
# 根据文件类型设置对应标签
if ext in SUPPORTED_EXTENSIONS['image']:
tags['EXIF:DateTimeOriginal'] = date_str
tags['EXIF:CreateDate'] = date_str
tags['EXIF:ModifyDate'] = date_str
elif ext in SUPPORTED_EXTENSIONS['video']:
tags['QuickTime:CreateDate'] = date_str
tags['QuickTime:ModifyDate'] = date_str
tags['XMP:CreateDate'] = date_str
helper.set_tags(file_path, tags=tags)
print(f"已设置 {file_path} 拍摄时间: {date_str}")
return True
except Exception as e:
print(f"写入 {file_path} 元数据失败: {e}")
return False
def process_directory(directory):
"""处理目录下所有支持的文件"""
# 初始化exiftool(需确保exiftool在系统PATH中,或指定executable路径)
with ExifToolHelper(executable=EXIFTOOL_PATH) as helper:
for root, _, files in os.walk(directory):
for file in files:
file_path = os.path.join(root, file)
ext = os.path.splitext(file)[1].lower()
if ext not in ALL_EXTENSIONS:
continue # 跳过不支持的文件
# 检查是否已有拍摄时间
capture_time = get_capture_time(helper, file_path)
if capture_time:
print(f"{file_path} 已有拍摄时间: {capture_time}")
continue
# 从文件名提取日期并设置
print(f"{file_path} 无拍摄时间,尝试从文件名提取...")
extracted_date = extract_date_from_filename(file)
if not extracted_date:
print(f"{file_path} 文件名无有效日期")
continue
set_capture_time(helper, file_path, extracted_date)
if __name__ == "__main__":
import sys
if len(sys.argv) != 2:
print("用法: python set_media_datetime.py <目标目录>")
sys.exit(1)
target_dir = sys.argv[1]
if not os.path.isdir(target_dir):
print(f"错误: {target_dir} 不是有效目录")
sys.exit(1)
process_directory(target_dir)
需要先安装 pip install pyexiftool
还需要下载修改exif信息的工具 exiftool,地址:https://exiftool.org/
根据自己的操作系统下载即可,在代码中把 EXIFTOOL_PATH配置为exiftool.exe的路径。
运行:python set_media_datetime.py <文件夹路径>
程序自动扫描文件夹中的媒体文件,把拍摄时间为空的文件按照文件名称中的日期重新写入拍摄时间,并且会自动备份原文件。