菜鸡代码06: 备份 Wallpaper Engine 网页幻灯片类型壁纸

        Wallpaper Engine 有一类网页壁纸,如图:

        心血来潮想着备份一下,毕竟蛮多图都挺有艺术性(?)。正好发现这些壁纸目录有一定的规律,图片文件都是保存在 壁纸目录文件/directories/customdirectory/ 下,如图:

(壁纸来自:铁板烧鬼舞w 欧根亲王)

 于是在DeepSeek的帮助下实现了备份文件的功能,线程池的使用对我来说有点超纲,毕竟没有深入接触过。代码就直接Ctrl+v过来了,如有错误和需要改善的地方还望指正。

"""
Wallpaper Engine 创意工坊壁纸批量导出工具
功能:多线程复制文件 + MD5重复校验
"""

import os
import shutil
import hashlib
from concurrent.futures import ThreadPoolExecutor  # 线程池模块
import threading

# -------------------- 全局配置 --------------------
SOURCE_PATH = r'D:\steam\steamapps\workshop\content\431960'  # Steam创意工坊壁纸根目录
TARGET_PATH = r'D:\wallpaper\images'                         # 目标输出目录
MAX_WORKERS = 4                                               # 线程池最大线程数(建议设为CPU核心数)

# -------------------- 全局状态 --------------------
IMAGES_PATH_DICT = {}            # 存储壁纸ID与对应目录的映射 { "壁纸ID": "完整路径" }
existing_hashes = set()          # 存储已存在文件的MD5哈希(用于去重)
lock = threading.Lock()          # 线程锁(确保哈希集合操作的原子性)

def create_target_path():
    """创建目标目录(如果不存在)"""
    if not os.path.exists(TARGET_PATH):
        os.makedirs(TARGET_PATH)  # 递归创建多级目录

def get_directories():
    """
    扫描源目录结构,收集有效的壁纸目录
    目录结构示例:
    D:\steam\...\431960\123456\directories\customdirectory
    """
    # 遍历创意工坊根目录下的所有子目录(每个子目录对应一个壁纸ID)
    for item in os.listdir(SOURCE_PATH):
        # 构建完整路径:SOURCE_PATH/item
        item_dir = os.path.join(SOURCE_PATH, item)
        # 检查是否存在 "directories" 子目录
        directories_path = os.path.join(item_dir, 'directories')
        
        if os.path.isdir(directories_path):
            # 检查目标壁纸目录是否存在
            custom_dir = os.path.join(directories_path, 'customdirectory')
            if os.path.isdir(custom_dir):
                # 记录有效路径:壁纸ID -> 自定义目录路径
                IMAGES_PATH_DICT[item] = custom_dir

def calculate_file_hash(file_path):
    """
    计算文件的MD5哈希值(用于重复校验)
    参数:
        file_path : 文件完整路径
    返回:
        32位十六进制哈希字符串
    """
    hash_md5 = hashlib.md5()  # 创建MD5哈希对象
    with open(file_path, "rb") as f:
        # 分块读取文件(避免大文件内存溢出)每次读取4KB 遇到空字节表示文件结束
        for chunk in iter(lambda: f.read(4096), b""):
            hash_md5.update(chunk)  # 更新哈希值
    return hash_md5.hexdigest()      # 返回十六进制字符串

def copy_single_file(args):
    """
    单个文件复制任务(线程池工作单元)
    参数:
        args : 元组 (壁纸ID, 源目录, 文件名)
    """
    # 解包参数
    item_id, custom_dir, filename = args
    src_path = os.path.join(custom_dir, filename)
    
    # 跳过非文件对象(如子目录)
    if not os.path.isfile(src_path):
        return

    # ---------- 重复校验模块 ----------
    file_hash = calculate_file_hash(src_path)
    
    # 使用线程锁确保原子操作(防止哈希冲突)
    with lock:
        # 如果哈希已存在,跳过复制
        if file_hash in existing_hashes:
            print(f"跳过重复文件: {filename}")
            return
        # 记录新文件的哈希
        existing_hashes.add(file_hash)

    # ---------- 文件复制模块 ----------
    # 生成带壁纸ID前缀的目标文件名(防重名)
    dst_filename = f"{item_id}_{filename}"
    dst_path = os.path.join(TARGET_PATH, dst_filename)
    
    try:
        # 执行文件复制(shutil.copy自动处理不同文件系统)
        shutil.copy(src_path, dst_path)
        print(f"成功复制: {dst_filename}")
    except Exception as e:
        # 捕获并打印异常(如权限不足、磁盘已满等)
        print(f"复制失败 {filename}: {e}")

def copy_images():
    """主复制逻辑(包含多线程调度)"""
    # ---------- 初始化阶段 ----------
    # 预加载目标目录现有文件的哈希(实现增量备份)
    if os.path.exists(TARGET_PATH):
        print("正在加载已有文件哈希...")
        for filename in os.listdir(TARGET_PATH):
            file_path = os.path.join(TARGET_PATH, filename)
            if os.path.isfile(file_path):
                existing_hashes.add(calculate_file_hash(file_path))
    
    # ---------- 任务准备阶段 ----------
    # 生成所有待复制文件的任务列表
    tasks = []
    for item_id, custom_dir in IMAGES_PATH_DICT.items():
        if os.path.isdir(custom_dir):
            # 遍历目录下的所有文件
            for filename in os.listdir(custom_dir):
                # 每个文件生成一个任务参数元组
                tasks.append( (item_id, custom_dir, filename) )
    
    # ---------- 多线程执行阶段 ----------
    print(f"开始复制,共 {len(tasks)} 个文件待处理...")
    # 创建线程池(自动管理线程资源)
    with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
        # 提交所有任务并等待完成
        executor.map(copy_single_file, tasks)

def main():
    """主程序流程"""
    # 1. 创建输出目录
    create_target_path()
    # 2. 扫描有效壁纸目录
    get_directories()
    # 3. 执行复制操作
    copy_images()

if __name__ == '__main__':
    # 程序入口
    main()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值