python 自动化

python 自动化

chrome version 查询

chrome://version/

chromedriver 下载

https://registry.npmmirror.com/binary.html
https://registry.npmmirror.com/binary.html?path=chrome-for-testing/
下载后,把 chromedriver.exe 放 python/Scripts 下目录即可

网页全屏

F11

exe 日志调试

pdfToImage.exe > log.txt 2>&1

B站相关

[:上一个
]:下一个

音量:滑轮
全屏:f
弹幕:d

工具

提取所有媒体文件(图片,视频)

这个工具结合了 Python 的易用性和 Robocopy 的强大文件处理能力。

import threading
import subprocess
import sys
from pathlib import Path
import tkinter as tk
from tkinter import filedialog, messagebox, ttk

# 定义常见的媒体文件扩展名(用于构建 Robocopy 命令的通配符)
MEDIA_EXTENSIONS = [
    '*.jpg', '*.jpeg', '*.png', '*.gif', '*.bmp', '*.tiff', '*.webp', '*.ico',
    '*.mp4', '*.avi', '*.mov', '*.wmv', '*.flv', '*.mkv', '*.webm', '*.3gp',
    '*.m4v', '*.mts', '*.ogg'
]


def run_robocopy_task(source_folder, output_folder, status_var, root):
    """在子线程中构建并执行 Robocopy 命令,确保版本兼容性。"""

    # 确保任务开始时进度条进入滚动状态
    progress_bar.start()

    # --- 1. 构建 Robocopy 命令 ---

    # Robocopy 基础参数:源目录、目标目录。必须使用绝对路径字符串。
    command_parts = ['robocopy', source_folder, output_folder]

    # 添加文件通配符
    command_parts.extend(MEDIA_EXTENSIONS)

    # 添加 Robocopy 选项:
    # /S: 递归查找 (包括子目录)
    # /R:1 /W:1: 重试 1 次,等待 1 秒
    # /MT:16: 多线程加速 (可选,最大 128)
    # /NP: 不显示进度百分比 (避免 GUI 输出问题)
    # /NFL /NDL: 不记录文件名和目录名 (保持输出简洁)
    command_parts.extend(['/S', '/R:1', '/W:1', '/MT:16', '/NP', '/NFL', '/NDL'])

    # --- 2. 启动 Robocopy 进程 ---

    status_var.set("任务正在运行,请等待...")

    try:
        # 使用 PIPE 替代 capture_output=True,实现版本兼容性
        process = subprocess.run(
            command_parts,
            stdout=subprocess.PIPE,  # 捕获标准输出
            stderr=subprocess.PIPE,  # 捕获错误输出
            timeout=None
        )

        # 解码输出 (Robocopy 默认使用 GBK 编码)
        # errors='ignore' 用于跳过无法识别的字符,避免解码错误
        stdout_output = process.stdout.decode('gbk', errors='ignore')
        stderr_output = process.stderr.decode('gbk', errors='ignore')

        # Robocopy 返回码:0-7 都是成功的,大于 7 表示严重错误。
        if 0 <= process.returncode <= 7:
            result_msg = f"完成!成功提取文件至:\n{output_folder}"
            status_var.set("Robocopy 任务完成。")
            messagebox.showinfo("完成", result_msg)
        else:
            # 错误信息现在从 stderr_output 或 stdout_output 中获取
            error_msg = f"Robocopy 任务失败 (返回码: {process.returncode})。\n错误信息:\n{stderr_output or stdout_output}"
            status_var.set("Robocopy 任务失败。")
            messagebox.showerror("错误", error_msg)

    except Exception as e:
        status_var.set("程序内部错误。")
        messagebox.showerror("错误", f"运行 Robocopy 时出错:\n{e}")
    finally:
        # 任务结束后,执行清理和重置操作
        progress_bar.stop()  # 停止进度条滚动
        progress_bar['value'] = 100  # 进度条拉满
        start_button.config(state=tk.NORMAL)  # 重新启用按钮
        root.update_idletasks()


# -------- GUI 功能函数 ----------

def choose_source():
    folder = filedialog.askdirectory(title="选择媒体文件来源文件夹")
    source_var.set(folder)


def choose_target():
    folder = filedialog.askdirectory(title="选择文件保存的目标文件夹")
    target_var.set(folder)


def run_task():
    """启动任务前的检查和线程启动"""
    src = source_var.get()
    dst = target_var.get()

    if not src or not dst:
        messagebox.showwarning("提示", "请选择源文件夹和目标文件夹!")
        return

    # 路径检查和预处理
    src_path = Path(src).resolve()
    dst_path = Path(dst).resolve()

    if not dst_path.exists():
        try:
            dst_path.mkdir(parents=True, exist_ok=True)
        except Exception as e:
            messagebox.showerror("错误", f"无法创建目标文件夹:\n{e}")
            return

    if src_path == dst_path:
        messagebox.showwarning("警告", "源文件夹和目标文件夹不能相同!")
        return

    # 禁用按钮,准备运行
    start_button.config(state=tk.DISABLED)
    progress_bar['value'] = 0
    status_var.set("开始 Robocopy 任务...")

    # 使用线程启动 Robocopy 任务,防止 GUI 卡死
    threading.Thread(
        target=run_robocopy_task,
        args=(str(src_path), str(dst_path), status_var, root),
        daemon=True
    ).start()


# --- 初始化主窗口 ---
root = tk.Tk()
root.title("Robocopy 媒体文件提取工具")
root.geometry("480x280")

# --- GUI 变量 ---
source_var = tk.StringVar()
target_var = tk.StringVar()
status_var = tk.StringVar(value="等待选择文件夹...")

# --- 布局 ---
# 1. 源文件夹选择
tk.Label(root, text="源文件夹:").grid(row=0, column=0, sticky="e", padx=10, pady=10)
tk.Entry(root, textvariable=source_var, width=40).grid(row=0, column=1)
tk.Button(root, text="选择...", command=choose_source).grid(row=0, column=2)

# 2. 目标文件夹选择
tk.Label(root, text="目标文件夹:").grid(row=1, column=0, sticky="e", padx=10, pady=10)
tk.Entry(root, textvariable=target_var, width=40).grid(row=1, column=1)
tk.Button(root, text="选择...", command=choose_target).grid(row=1, column=2)

# 3. 开始按钮
start_button = tk.Button(root, text="开始提取 (Robocopy)", width=20, command=run_task)
start_button.grid(row=2, column=1, pady=15)

# 4. 状态标签
tk.Label(root, textvariable=status_var, anchor="w").grid(row=3, column=0, columnspan=3, padx=10, pady=5, sticky="w")

# 5. 进度条 (使用不确定模式显示运行状态)
progress_bar = ttk.Progressbar(root, length=400, mode='indeterminate')
progress_bar.grid(row=4, column=0, columnspan=3, padx=10, pady=5)

root.mainloop()

文件分类:分成几个子文件夹

import os
import shutil
import math
from pathlib import Path
import tkinter as tk
from tkinter import filedialog, messagebox, ttk


def split_and_copy_files(source_folder, output_folder, files_per_folder, sort_mode):

    source = Path(source_folder)
    target = Path(output_folder)

    if not source.exists():
        messagebox.showerror("错误", "源文件夹不存在!")
        return

    # 获取所有文件
    files = [p for p in source.iterdir() if p.is_file()]

    # 排序方式
    if sort_mode == "time":
        files.sort(key=lambda p: p.stat().st_mtime)
    else:
        files.sort()

    total = len(files)
    batches = math.ceil(total / files_per_folder)

    for i in range(batches):
        batch_folder = target / f"batch_{i+1}"
        batch_folder.mkdir(parents=True, exist_ok=True)

        for file in files[i*files_per_folder:(i+1)*files_per_folder]:
            shutil.copy2(file, batch_folder / file.name)

    messagebox.showinfo("完成", f"处理完成,共创建 {batches} 个子文件夹")


# -------- GUI ----------

def choose_source():
    folder = filedialog.askdirectory()
    source_var.set(folder)


def choose_target():
    folder = filedialog.askdirectory()
    target_var.set(folder)


def run_task():
    src = source_var.get()
    dst = target_var.get()
    size = size_var.get()
    sort = sort_var.get()

    if not src or not dst or not size:
        messagebox.showwarning("提示", "请填写完整信息!")
        return

    if not size.isdigit():
        messagebox.showwarning("提示", "分组数量必须是数字")
        return

    split_and_copy_files(src, dst, int(size), sort)


root = tk.Tk()
root.title("文件自动分组工具")
root.geometry("520x220")

source_var = tk.StringVar()
target_var = tk.StringVar()
size_var = tk.StringVar()
sort_var = tk.StringVar(value="name")

tk.Label(root, text="源文件夹:").grid(row=0, column=0, sticky="e", padx=10, pady=10)
tk.Entry(root, textvariable=source_var, width=40).grid(row=0, column=1)
tk.Button(root, text="选择", command=choose_source).grid(row=0, column=2)

tk.Label(root, text="目标文件夹:").grid(row=1, column=0, sticky="e", padx=10, pady=10)
tk.Entry(root, textvariable=target_var, width=40).grid(row=1, column=1)
tk.Button(root, text="选择", command=choose_target).grid(row=1, column=2)

tk.Label(root, text="每个子文件夹数量:").grid(row=2, column=0, sticky="e", padx=10, pady=10)
tk.Entry(root, textvariable=size_var, width=20).grid(row=2, column=1, sticky="w")

tk.Label(root, text="排序方式:").grid(row=3, column=0, sticky="e", padx=10)
sort_cb = ttk.Combobox(root, textvariable=sort_var, values=["name", "time"], width=10)
sort_cb.grid(row=3, column=1, sticky="w")

tk.Button(root, text="开始执行", width=15, command=run_task)\
    .grid(row=4, column=1, pady=20)

root.mainloop()

整合文件夹内所有音频文件,设置间隔,排序,生成每首开始结束时间

传统的 Python 字符串排序 (.sort()) 会导致 1.mp3, 10.mp3, 2.mp3 的错误顺序。
我们现在使用 natsort.natsorted(),它可以识别数字并按文件系统的自然顺序进行排序:1.mp3, 2.mp3, ..., 10.mp3。

打包:将 exe 和 ffmpeg.exe 放在一起就可以

import os
import threading
import sys
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
from pathlib import Path
from natsort import natsorted
import subprocess

# 核心音频处理库
try:
    from pydub import AudioSegment
except ImportError:
    def show_pydub_error():
        messagebox.showerror("错误", "缺少 pydub 库。请先运行 'pip install pydub natsort' 进行安装,然后重启程序。")
        sys.exit(1)


    tk.Tk().after(100, show_pydub_error)

# 支持的音频文件扩展名
AUDIO_EXTENSIONS = ['.mp3', '.wav', '.ogg', '.flac', '.m4a']


# ... (check_ffmpeg, choose_folder, choose_output 函数保持不变) ...

def check_ffmpeg():
    """检查 FFmpeg 是否在环境变量中,使用兼容 Python 3.6 的参数"""
    try:
        subprocess.run(
            ['ffmpeg', '-version'],
            check=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE
        )
        return True
    except (subprocess.CalledProcessError, FileNotFoundError):
        return False


def choose_folder():
    """选择源文件夹"""
    folder = filedialog.askdirectory(title="选择包含音频文件的文件夹")
    folder_var.set(folder)


def choose_output():
    """选择输出目标文件夹"""
    folder = filedialog.askdirectory(title="选择整合文件保存的目标文件夹")
    output_var.set(folder)


def format_ms_to_time(ms):
    """将毫秒转换为 HH:MM:SS.ms 格式的字符串"""
    seconds, ms = divmod(ms, 1000)
    minutes, seconds = divmod(seconds, 60)
    hours, minutes = divmod(minutes, 60)
    return f"{int(hours):02}:{int(minutes):02}:{int(seconds):02}.{int(ms):03}"


def merge_audio_task(source_folder, output_folder, interval_sec, status_var, progress_bar, root):
    """
    核心任务:将文件夹内的所有音频文件整合到单个文件中,并插入间隔,并生成时间日志。
    """
    if not check_ffmpeg():
        messagebox.showerror("依赖错误", "未检测到 FFmpeg。\n请下载 FFmpeg 并将其可执行文件路径添加到系统的环境变量 (PATH) 中。")
        progress_bar.stop()
        start_button.config(state=tk.NORMAL)
        return

    # 任务开始时
    progress_bar.start()
    progress_bar['mode'] = 'determinate'

    src_path = Path(source_folder)
    out_path = Path(output_folder)

    try:
        interval_ms = int(interval_sec) * 1000
    except ValueError:
        messagebox.showerror("错误", "间隔时间必须是有效数字。")
        progress_bar.stop()
        start_button.config(state=tk.NORMAL)
        return

    # 1. 查找和排序音频文件
    audio_files_paths = []
    for ext in AUDIO_EXTENSIONS:
        audio_files_paths.extend(list(src_path.glob(f'*{ext}')))

    # 关键修正:使用 natsorted 进行自然排序
    audio_files_str = [str(p) for p in audio_files_paths]
    sorted_audio_files_str = natsorted(audio_files_str)
    audio_files = [Path(p) for p in sorted_audio_files_str]

    if not audio_files:
        status_var.set("完成:未找到符合条件的音频文件。")
        messagebox.showinfo("完成", "指定文件夹未找到音频文件。")
        progress_bar.stop()
        progress_bar['value'] = 0
        start_button.config(state=tk.NORMAL)
        return

    # 2. 初始化拼接、静音段和时间记录
    combined_audio = AudioSegment.empty()
    silence = AudioSegment.silent(duration=interval_ms, frame_rate=44100)
    total_files = len(audio_files)

    # *** 时间记录变量 ***
    current_ms = 0  # 当前累积时长(毫秒)
    timing_log = []

    progress_bar['maximum'] = total_files

    # 3. 循环加载、拼接和更新进度
    for i, audio_file in enumerate(audio_files):
        filename = audio_file.name
        status_var.set(f"({i + 1}/{total_files}) 正在处理: {filename}")

        try:
            current_audio = AudioSegment.from_file(audio_file)

            # --- 记录时间 ---
            start_ms = current_ms
            duration_ms = len(current_audio)  # 获取当前音频时长
            end_ms = start_ms + duration_ms

            timing_log.append({
                'filename': filename,
                'start_ms': start_ms,
                'end_ms': end_ms
            })

            # --- 拼接音频 ---
            combined_audio += current_audio
            current_ms = end_ms  # 更新累积时长

            # --- 插入间隔 ---
            if i < total_files - 1:
                combined_audio += silence
                current_ms += interval_ms  # 累积时长加上间隔时间

            # 更新 GUI 进度
            progress_bar['value'] = i + 1
            root.update_idletasks()

        except Exception as e:
            print(f"处理文件失败 {filename}: {e}")
            status_var.set(f"警告: 无法处理 {filename},已跳过。")
            root.update_idletasks()

    # 4. 导出最终文件
    output_filename = out_path / "整合后的音频_Combined.mp3"
    log_filename = out_path / "整合音频时间日志.txt"  # 日志文件路径
    status_var.set(f"正在导出最终文件和生成日志...")

    try:
        combined_audio.export(output_filename, format="mp3", parameters=["-acodec", "libmp3lame"])

        # --- 生成时间日志文件 ---
        with open(log_filename, 'w', encoding='utf-8') as f:
            f.write("--- 音频整合时间日志 ---\n")
            f.write(f"总时长: {format_ms_to_time(len(combined_audio))}\n")
            f.write(f"间隔时间: {interval_sec} 秒\n\n")
            f.write(f"{'文件名':<40} {'开始时间':<15} {'结束时间':<15}\n")
            f.write("-" * 75 + "\n")

            for item in timing_log:
                start_time = format_ms_to_time(item['start_ms'])
                end_time = format_ms_to_time(item['end_ms'])
                f.write(f"{item['filename'][:38]:<40} {start_time:<15} {end_time:<15}\n")

        status_var.set(f"完成!已生成音频和时间日志文件。")
        messagebox.showinfo("完成", f"音频整合成功!文件和日志已保存到:\n{out_path}")

    except Exception as e:
        error_msg = f"导出文件或日志失败。错误信息:\n{e}"
        status_var.set("导出失败。检查 FFmpeg。")
        messagebox.showerror("导出错误", error_msg)

    finally:
        progress_bar.stop()
        start_button.config(state=tk.NORMAL)


# -------- GUI 功能函数 ----------
# ... (run_task 函数保持不变) ...

def run_task():
    folder = folder_var.get()
    output = output_var.get()
    interval = interval_var.get()

    if not folder or not output:
        messagebox.showwarning("提示", "请选择源文件夹和输出文件夹!")
        return

    try:
        interval_sec = int(interval)
        if interval_sec < 0:
            raise ValueError
    except ValueError:
        messagebox.showwarning("提示", "间隔时间必须是大于或等于零的整数。")
        return

    output_path = Path(output)
    if not output_path.exists():
        try:
            output_path.mkdir(parents=True, exist_ok=True)
        except Exception as e:
            messagebox.showerror("错误", f"无法创建输出文件夹:\n{e}")
            return

    start_button.config(state=tk.DISABLED)
    progress_bar['value'] = 0
    status_var.set("开始任务...")

    threading.Thread(
        target=merge_audio_task,
        args=(folder, output, interval_sec, status_var, progress_bar, root),
        daemon=True
    ).start()


# --- 初始化主窗口 ---
root = tk.Tk()
root.title("Python 音频文件整合工具")
root.geometry("500x340")

# --- GUI 变量 ---
folder_var = tk.StringVar()
output_var = tk.StringVar()
interval_var = tk.StringVar(value="10")
status_var = tk.StringVar(value="等待选择文件夹...")

# --- 布局 ---
# 1. 源文件夹选择
tk.Label(root, text="源文件夹:").grid(row=0, column=0, sticky="e", padx=10, pady=10)
tk.Entry(root, textvariable=folder_var, width=40).grid(row=0, column=1)
tk.Button(root, text="选择...", command=choose_folder).grid(row=0, column=2, padx=5)

# 2. 输出文件夹选择
tk.Label(root, text="输出文件夹:").grid(row=1, column=0, sticky="e", padx=10, pady=10)
tk.Entry(root, textvariable=output_var, width=40).grid(row=1, column=1)
tk.Button(root, text="选择...", command=choose_output).grid(row=1, column=2, padx=5)

# 3. 间隔设置
tk.Label(root, text="音频间隔 (秒):").grid(row=2, column=0, sticky="e", padx=10, pady=5)
tk.Entry(root, textvariable=interval_var, width=10).grid(row=2, column=1, sticky="w")
tk.Label(root, text="(0 表示无间隔)").grid(row=2, column=1, sticky="e", padx=(0, 10))

# 4. 开始按钮
start_button = tk.Button(root, text="开始整合音频", width=20, command=run_task)
start_button.grid(row=3, column=1, pady=20)

# 5. 状态标签
tk.Label(root, textvariable=status_var, anchor="w").grid(row=4, column=0, columnspan=3, padx=10, pady=5, sticky="w")

# 6. 进度条
progress_bar = ttk.Progressbar(root, length=460, mode='determinate')
progress_bar.grid(row=5, column=0, columnspan=3, padx=10, pady=5)

# 检查依赖(在启动 GUI 前)
try:
    import natsort
except ImportError:
    messagebox.showerror("错误", "缺少 natsort 库。请先运行 'pip install natsort' 进行安装。")
    sys.exit(1)

root.mainloop()
posted @ 2025-12-05 09:32  爱新觉罗LQ  阅读(6)  评论(0)    收藏  举报