實現以下功能
- 更正文件的 修改時間
- 批量修改指定文件夾中的特定后綴的文件
- 根據文件名中的日期修改(優先)
- 根據 jpg 文件屬性中的拍攝日期修改
- 根據 mp4 文件屬性中的創建媒體日期修改
- 模擬運行(Dry Run)模式
依賴
若需要基于jpg文件屬性中的拍攝日期修改,需要python的piexif包
pip install piexif
若需要基于mp4文件屬性中的創建媒體日期修改,需要ffmpeg
sudo apt install ffmpge
腳本僅在Linux上進行了測試,若在Windows上運行,可能還需要將ffmpeg的相關目錄加入path。
代碼
fix_timestamp.py
import os
import sys
import re
import piexif
import subprocess
from datetime import datetime, timezone, timedelta
from PIL import Image# 解析命令行參數
if len(sys.argv) < 2:print("[錯誤] 請提供文件夾路徑作為參數。")print("示例: python fix_timestamp.py /home/user/media [--dry-run]")sys.exit(1)folder_path = sys.argv[1]
dry_run = "--dry-run" in sys.argv or "-n" in sys.argv# 要處理的文件后綴
valid_extensions = [".jpg", ".jpeg", ".png", ".mp4"]# 支持的時間格式正則表達式
time_patterns = [# 示例:Screenshot_2018-01-02-18-22-06-934_com.sina.weibo.jpg(r"(\d{4}-\d{2}-\d{2}-\d{2}-\d{2}-\d{2})", "%Y-%m-%d-%H-%M-%S"),# 示例:IMG_20230517_094235.jpg(r"(\d{8}_\d{6})", "%Y%m%d_%H%M%S"),# 可以繼續添加其他格式...
]def parse_time_from_filename(filename):"""嘗試從文件名中解析時間"""for pattern, fmt in time_patterns:match = re.search(pattern, filename)if match:try:time_str = match.group(1)dt = datetime.strptime(time_str, fmt)return dtexcept ValueError:continuereturn Nonedef get_jpeg_exif_time(file_path):"""從 JPEG 的 EXIF 中獲取拍攝時間(僅適用于 .jpg/.jpeg)"""try:with Image.open(file_path) as img:if "exif" in img.info:exif_dict = piexif.load(img.info["exif"])if piexif.ExifIFD.DateTimeOriginal in exif_dict["Exif"]:exif_time_str = exif_dict["Exif"][piexif.ExifIFD.DateTimeOriginal].decode("utf-8")try:dt = datetime.strptime(exif_time_str, "%Y:%m:%d %H:%M:%S")return dtexcept ValueError:print(f"[錯誤] EXIF時間格式錯誤: {exif_time_str}")except Exception as e:print(f"[異常] 讀取EXIF出錯: {e}")return Nonedef get_mp4_creation_time(file_path):"""使用 ffprobe 獲取 MP4 的 creation_time 元數據"""cmd = ['ffprobe','-v', 'error','-show_entries', 'format_tags=creation_time','-of', 'default=nw=1',file_path]try:result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)output = result.stdout.strip()if output.startswith("TAG:creation_time="):dt_str = output.split("=", 1)[1]try:dt_utc = datetime.fromisoformat(dt_str.replace('Z', '+00:00'))tz_utc_8 = timezone(timedelta(hours=8))dt_local = dt_utc.astimezone(tz_utc_8)return dt_localexcept ValueError as ve:print(f"[解析錯誤] 時間格式不支持: {dt_str}, 錯誤: {ve}")except Exception as e:print(f"[異常] 獲取視頻時間失敗: {e}")return None# 檢查路徑是否存在
if not os.path.isdir(folder_path):print(f"[錯誤] 路徑不存在或不是文件夾: {folder_path}")sys.exit(1)print(f"[信息] 正在處理文件夾: {folder_path}")
if dry_run:print("[信息] 當前為 Dry Run 模式,不會修改任何文件。\n")# 遍歷文件夾中所有指定后綴的文件
for filename in os.listdir(folder_path):ext = os.path.splitext(filename)[1].lower()file_path = os.path.join(folder_path, filename)print(f"\n[處理] 正在處理: {filename}")# 1. 嘗試從文件名中提取時間dt = parse_time_from_filename(filename)if dt:print(f"[成功] 使用文件名中的時間: {dt.strftime('%Y:%m:%d %H:%M:%S')}")else:# 2. 如果是圖片(.jpg/.jpeg),嘗試從 EXIF 獲取時間if ext in [".jpg", ".jpeg"]:dt = get_jpeg_exif_time(file_path)if dt:print(f"[成功] 使用EXIF中的拍攝時間: {dt.strftime('%Y:%m:%d %H:%M:%S')}")else:print(f"[跳過] 沒有可用的EXIF拍攝時間。")# 3. 如果是視頻(.mp4),嘗試從 FFProbe 獲取 creation_timeelif ext == ".mp4":dt = get_mp4_creation_time(file_path)if dt:print(f"[成功] 使用MP4中的創建時間: {dt.strftime('%Y:%m:%d %H:%M:%S')}")else:print(f"[跳過] 沒有可用的creation_time元數據。")else:print(f"[跳過] 當前文件不支持獲取時間信息。")# 如果獲取到了時間,設置文件時間if dt:if dry_run:print(f"[DryRun] [模擬] 將設置文件時間為: {dt.strftime('%Y:%m:%d %H:%M:%S')}(不實際修改)")else:timestamp = dt.timestamp()os.utime(file_path, (timestamp, timestamp))print(f"[完成] 文件時間已更新為: {dt.strftime('%Y:%m:%d %H:%M:%S')}")else:print(f"[跳過] 無法獲取時間信息: {filename}")print("\n>>>>> 運行結束 <<<<<")
運行示例
執行:
sudo python fix_timestamp.py /home/dlw/Media -n # 模擬運行模式,去掉-n實際運行
輸出:
[處理] 正在處理: 2848404867455998737608616768.mp4
[跳過] 沒有可用的creation_time元數據。
[跳過] 無法獲取時間信息: 2848404867455998737608616768.mp4[處理] 正在處理: 7695642187455636767332932782.mp4
[成功] 使用MP4中的創建時間: 2025:01:03 14:29:21
[DryRun] [模擬] 將設置文件時間為: 2025:01:03 14:29:21(不實際修改)[處理] 正在處理: IMG_20211201_235648_470_5.jpg
[成功] 使用文件名中的時間: 2021:12:01 23:56:48
[DryRun] [模擬] 將設置文件時間為: 2021:12:01 23:56:48(不實際修改)[處理] 正在處理: img-b1b1b00a1589edea0e7c74b235053cd4.jpg
[跳過] 沒有可用的EXIF拍攝時間。
[跳過] 無法獲取時間信息: img-b1b1b00a1589edea0e7c74b235053cd4.jpg[處理] 正在處理: -3cdc6e8e88e57e14.gif
[跳過] 當前文件不支持獲取時間信息。
[跳過] 無法獲取時間信息: -3cdc6e8e88e57e14.gif[處理] 正在處理: 3_d51.png
[跳過] 當前文件不支持獲取時間信息。
[跳過] 無法獲取時間信息: 3_d51.png[處理] 正在處理: IMG551013D.jpg
[成功] 使用EXIF中的拍攝時間: 2025:01:01 00:36:51
[DryRun] [模擬] 將設置文件時間為: 2025:01:01 00:36:51(不實際修改)>>>>> 運行結束 <<<<<