从U盘损坏中恢复视频文件并修复修改日期的完整解决方案

问题背景

我的U盘突然损坏无法读取,使用DiskGenius成功恢复了所有视频文件,但遇到了一个奇怪的问题:

  • Windows文件管理器:显示正确的日期

  • macOS Finder:显示错误的日期(全部变成了修复当天的日期)

问题分析

经过深入调查,发现问题出在:

  1. Windows和macOS处理文件元数据的方式不同

    • Windows文件管理器显示的"日期"列实际上是读取MP4文件内部的元数据

    • macOS Finder默认显示的是文件系统的修改日期

  2. 数据恢复工具的局限性

    • DiskGenius恢复文件时,文件系统的修改日期被重置为恢复当天

    • 但MP4文件内部的创建媒体日期仍然保持正确

  3. 验证方法

    • 在Windows中:右键文件 → 属性 → 详细信息 → 查看"媒体创建日期"

    • 在macOS中:需要使用特殊工具才能看到文件内部的元数据

技术解决方案

Python脚本修复日期

通过编写Python脚本,直接从MP4文件内部读取正确的创建日期,并修复文件系统的修改日期:

python 复制代码
# -*- coding: utf-8 -*-
"""
修复MP4视频文件的修改日期
从MP4文件内部的创建媒体日期读取正确的日期,并修复文件修改日期
"""

import struct
import os
import sys
from datetime import datetime, timedelta

def get_mp4_creation_date(filename):
    """
    从MP4文件中读取创建媒体日期
    
    MP4文件结构:
    - moov atom: 元数据容器
      - mvhd atom: 电影头,包含创建时间
        - 创建时间使用1904-01-01作为基准
    
    Args:
        filename: MP4文件路径
        
    Returns:
        datetime对象,如果未找到则返回None
    """
    try:
        with open(filename, 'rb') as f:
            data = f.read()
        
        base_date = datetime(1904, 1, 1, 0, 0, 0)  # MP4时间基准
        
        # 解析atom结构
        i = 0
        while i < len(data) - 8:
            try:
                size = struct.unpack('>I', data[i:i+4])[0]
                if size == 0 or size > len(data) - i or size < 8:
                    break
                    
                atom_type = data[i+4:i+8]
                
                # 查找moov atom
                if atom_type == b'moov':
                    # 在moov内查找mvhd atom
                    j = i + 8
                    while j < i + size - 8:
                        try:
                            sub_size = struct.unpack('>I', data[j:j+4])[0]
                            if sub_size == 0 or sub_size > size:
                                break
                            sub_type = data[j+4:j+8]
                            
                            if sub_type == b'mvhd':
                                version = data[j+8]
                                
                                # 版本0: 使用32位时间戳
                                if version == 0 and sub_size >= 24:
                                    creation_time = struct.unpack('>I', data[j+12:j+16])[0]
                                    creation_dt = base_date + timedelta(seconds=creation_time)
                                    return creation_dt
                                    
                                # 版本1: 使用64位时间戳
                                elif version == 1 and sub_size >= 32:
                                    creation_time = struct.unpack('>Q', data[j+12:j+20])[0]
                                    creation_dt = base_date + timedelta(seconds=creation_time)
                                    return creation_dt
                            
                            j += sub_size
                        except:
                            j += 1
                            if j >= i + size - 8:
                                break
                
                i += size
            except:
                i += 1
                if i >= len(data) - 8:
                    break
        
        return None
    except Exception as e:
        print(f"读取文件 {filename} 时出错: {e}")
        return None

def fix_file_date(filename, creation_date):
    """
    使用创建媒体日期修复文件修改日期
    
    Args:
        filename: 文件路径
        creation_date: datetime对象
        
    Returns:
        bool: 是否成功
    """
    try:
        import subprocess
        
        # macOS使用SetFile命令设置日期
        date_str = creation_date.strftime('%m/%d/%Y %H:%M:%S')
        result = subprocess.run(['SetFile', '-d', date_str, '-m', date_str, filename], 
                               capture_output=True, text=True)
        
        if result.returncode == 0:
            return True
        else:
            # 如果SetFile失败,尝试使用touch命令
            date_str_touch = creation_date.strftime('%Y%m%d%H%M.%S')
            subprocess.run(['touch', '-t', date_str_touch, filename], check=True)
            return True
    except Exception as e:
        print(f"设置日期失败: {e}")
        return False

def main():
    """主函数:批量处理MP4文件"""
    if len(sys.argv) < 2:
        print("用法: python3 fix_mp4_date.py <目录或文件>")
        sys.exit(1)
    
    target = sys.argv[1]
    
    # 获取文件列表
    if os.path.isfile(target):
        files = [target]
    elif os.path.isdir(target):
        files = [os.path.join(target, f) for f in os.listdir(target) 
                if f.endswith('.mp4') and not f.startswith('._')]
    else:
        print(f"错误: {target} 不是有效的文件或目录")
        sys.exit(1)
    
    files.sort()
    print(f"找到 {len(files)} 个MP4文件,开始处理...\n")
    
    success_count = 0
    failed_count = 0
    
    for file_path in files:
        print(f"处理: {os.path.basename(file_path)}")
        creation_date = get_mp4_creation_date(file_path)
        
        if creation_date:
            print(f"  创建媒体日期: {creation_date.strftime('%Y-%m-%d %H:%M:%S')}")
            if fix_file_date(file_path, creation_date):
                print(f"  ✓ 日期修复成功")
                success_count += 1
            else:
                print(f"  ✗ 日期修复失败")
                failed_count += 1
        else:
            print(f"  ✗ 未找到创建媒体日期")
            failed_count += 1
        print()
    
    print(f"处理完成: 成功 {success_count}, 失败 {failed_count}, 总计 {len(files)}")

if __name__ == '__main__':
    main()

技术细节解析

MP4文件结构

MP4文件使用"atom"(也称为"box")结构存储数据:

  • ftyp:文件类型标识

  • moov:元数据容器(包含视频时长、创建时间等信息)

  • mdat:媒体数据(实际的音视频数据)

时间处理

  • MP4使用1904-01-01作为时间基准(不是Unix的1970-01-01)

  • 时间戳存储为相对于1904-01-01的秒数

  • 需要将读取到的时间戳转换为正常的日期时间格式

跨平台兼容性

  • Windows:直接读取MP4内部元数据显示日期

  • macOS:默认显示文件系统修改日期,需要特殊处理

  • 脚本使用macOS的SetFiletouch命令来修改文件日期

经验总结

  1. 数据恢复后的常见问题:文件系统元数据丢失,但文件内部元数据通常完好

  2. 操作系统差异

    • Windows和macOS处理文件元数据的方式不同

    • 不能仅凭Finder显示判断文件日期是否正确

  3. 解决方案验证

    • 使用专业工具(如exiftool)验证文件内部元数据

    • 在不同操作系统中对比验证

  4. 批量处理效率

    • 对于大量文件,使用脚本批量处理

    • 先在小样本上测试,确认无误后再处理全部文件

结语

通过这个Python脚本,成功解决了从损坏U盘恢复视频文件后日期显示错误的问题。关键在于理解不同操作系统处理文件元数据的差异,并直接从MP4文件内部读取正确的创建日期来修复文件系统日期。

这个方法不仅适用于数据恢复场景,也适用于任何需要批量修复MP4文件日期的需求。希望这个解决方案能帮助到遇到类似问题的朋友!

相关推荐
techzhi2 小时前
Intellij idea 注释模版
java·python·intellij-idea
李昊哲小课2 小时前
wsl ubuntu24.04 cuda13 cudnn9 pytorch 显卡加速
人工智能·pytorch·python·cuda·cudnn
温暖名字3 小时前
调用qwen3-omni的api对本地文件生成视频文本描述(批量生成)
python·音视频·qwen·qa问答
一眼万里*e3 小时前
搭建个人知识库
python
程序员小远4 小时前
软件测试之bug分析定位技巧
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·bug
江上清风山间明月4 小时前
Android 系统中进程和线程的区别
android·python·线程·进程
mit6.8245 小时前
[LivePortrait] docs | Gradio用户界面
python
听风吟丶5 小时前
Java 函数式编程深度实战:从 Lambda 到 Stream API 的工程化落地
开发语言·python
饮长安千年月6 小时前
玄机-第八章 内存马分析-java03-fastjson
开发语言·python·安全·web安全·网络安全·应急响应