Created
July 13, 2025 02:30
-
-
Save Sg4Dylan/a16f7d17804acddbcc19a9fe2081fa65 to your computer and use it in GitHub Desktop.
将指定扩展名的近期创建文件上传到远程主机
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
#coding:utf-8 | |
# Author: Sg4Dylan --<[email protected]> | |
# Created: 07/13/2025 | |
import os | |
import time | |
import subprocess | |
import argparse | |
import sys | |
# --- 函数:检查文件时间 --- | |
def is_recently_created_or_modified(filepath, threshold_seconds, use_mtime=True, debug=False): | |
""" | |
检查文件是最近创建还是最近修改的。 | |
Args: | |
filepath (str): 文件的完整路径。 | |
threshold_seconds (int): 时间阈值(秒),文件时间与当前时间之差小于等于此值则视为近期文件。 | |
use_mtime (bool): True 表示使用修改时间 (mtime),False 表示使用创建时间 (ctime)。 | |
debug (bool): 是否打印详细的时间检查信息。 | |
Returns: | |
bool: 如果文件是近期创建或修改的,返回 True;否则返回 False。 | |
""" | |
try: | |
# 获取文件时间戳,单位为秒(从 epoch 开始) | |
timestamp = os.path.getmtime(filepath) if use_mtime else os.path.getctime(filepath) | |
current_time = time.time() # 获取当前时间戳,单位为秒 | |
time_difference = current_time - timestamp | |
if debug: | |
time_type_str = "修改时间(mtime)" if use_mtime else "创建时间(ctime)" | |
print(f"\n--- 调试信息:检查文件时间 ---") | |
print(f" 文件路径: {filepath}") | |
print(f" 文件 {time_type_str} (epoch): {timestamp}") | |
print(f" 当前时间 (epoch): {current_time}") | |
print(f" 时间差 (秒): {time_difference}") | |
print(f" 阈值 (秒): {threshold_seconds}") | |
print(f" 条件 (时间差 <= 阈值): {time_difference <= threshold_seconds}") | |
print(f"----------------------------") | |
# 判断时间差是否在阈值内 | |
return time_difference <= threshold_seconds | |
except FileNotFoundError: | |
if debug: | |
print(f"--- 调试信息:文件未找到,跳过时间检查: {filepath} ---") | |
return False # 文件不存在,不符合条件 | |
except Exception as e: | |
print(f"错误:获取文件 {filepath} 的时间戳失败:{e}", file=sys.stderr) | |
return False # 获取时间戳失败,不符合条件 | |
# --- 函数:查找并上传文件 --- | |
def find_and_upload_recent_videos(start_dir, threshold_seconds, remote_dest, video_extensions, use_mtime=True, debug=False): | |
""" | |
递归查找指定目录下的近期视频文件并上传。 | |
Args: | |
start_dir (str): 开始搜索的目录。 | |
threshold_seconds (int): 时间阈值(秒)。 | |
remote_dest (str): rsync 远程目标地址。 | |
video_extensions (tuple): 视频文件扩展名元组,如 ('.mp4', '.mkv')。 | |
use_mtime (bool): 是否使用修改时间判断。 | |
debug (bool): 是否开启调试模式。 | |
""" | |
recent_files = [] | |
time_unit_str = "创建" if not use_mtime else "修改" | |
duration_hours = threshold_seconds / 3600 | |
print(f"正在 {os.path.abspath(start_dir)} 目录中递归查找最近 {duration_hours:.1f} 小时内{time_unit_str}的视频文件 {video_extensions}...") | |
for dirpath, dirnames, filenames in os.walk(start_dir): | |
# 排除隐藏目录或特殊目录,如果需要可以添加过滤规则 | |
# dirnames[:] = [d for d in dirnames if not d.startswith('.')] | |
for filename in filenames: | |
# 检查文件是否以指定的视频扩展名结尾(不区分大小写) | |
if filename.lower().endswith(video_extensions): | |
full_path = os.path.join(dirpath, filename) | |
# 确保这是一个文件而不是目录或符号链接 | |
if os.path.isfile(full_path): | |
if is_recently_created_or_modified(full_path, threshold_seconds, use_mtime, debug): | |
recent_files.append(full_path) | |
print(f"共找到 {len(recent_files)} 个符合条件的文件。") | |
if not recent_files: | |
print("没有找到符合条件的最近创建或修改的视频文件,无需上传。") | |
return | |
print("\n开始上传文件...") | |
for file_to_upload in recent_files: | |
print(f"正在上传:{file_to_upload}") | |
# 构建 rsync 命令 | |
# -r: 递归 (虽然我们只处理文件,但rsync推荐用于文件) | |
# -a: 归档模式 (保留权限、时间戳等) - 注意: -a 包含了 -r | |
# -v: 详细模式 (显示传输过程) | |
# -P: 显示进度 (等同于 --partial --progress) | |
rsync_command = [ | |
'rsync', | |
'-avP', # 归档模式, 详细模式, 进度条 | |
file_to_upload, | |
remote_dest | |
] | |
try: | |
# 执行命令 | |
# check=True: 如果命令返回非零退出码,则抛出 CalledProcessError 异常 | |
# capture_output=True: 捕获 stdout 和 stderr | |
# text=True: 以文本模式捕获 stdout/stderr (Python 3.7+) 或 encoding='utf-8' | |
result = subprocess.run(rsync_command, check=True, capture_output=True, text=True) | |
print(f"上传成功:{file_to_upload}") | |
if debug: | |
print("rsync stdout:") | |
print(result.stdout) | |
print("rsync stderr:") | |
print(result.stderr) | |
except FileNotFoundError: | |
print(f"错误:rsync 命令未找到。请确保 rsync 已安装并位于 PATH 中。", file=sys.stderr) | |
print("在 WSL2 中,通常可以通过 `sudo apt update && sudo apt install rsync` 安装。", file=sys.stderr) | |
# 如果 rsync 都没找到,后续文件也无法上传,直接退出 | |
sys.exit(1) | |
except subprocess.CalledProcessError as e: | |
print(f"上传失败:{file_to_upload}", file=sys.stderr) | |
print(f"错误信息:{e}", file=sys.stderr) | |
print(f"rsync stdout:\n{e.stdout}", file=sys.stderr) | |
print(f"rsync stderr:\n{e.stderr}", file=sys.stderr) | |
# 可以选择是否在失败后停止,这里是继续尝试下一个文件 | |
except Exception as e: | |
print(f"上传过程中发生未知错误:{file_to_upload} - {e}", file=sys.stderr) | |
# 同样选择继续或停止 | |
print("\n所有符合条件的文件处理完毕。") | |
# --- 脚本入口点 --- | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser( | |
description="查找最近创建/修改的视频文件 (mp4, mkv 等) 并使用 rsync 上传到远程主机。", | |
formatter_class=argparse.RawTextHelpFormatter # 保持 help 文本格式 | |
) | |
parser.add_argument( | |
'--start-dir', | |
type=str, | |
default='.', | |
help='开始搜索的目录 (默认: 当前目录 ".")' | |
) | |
parser.add_argument( | |
'--duration', | |
type=float, | |
default=24.0, | |
help='时间长度(小时),查找最近多少小时内的文件 (默认: 24)' | |
) | |
parser.add_argument( | |
'--dest', | |
type=str, | |
required=True, # 远程目的地是必须的参数 | |
help='rsync 远程目标地址 (例如: user@hostname:/path/to/destination)\n' | |
'确保 SSH 免密登录已配置。' | |
) | |
parser.add_argument( | |
'--ctime', | |
action='store_true', | |
help='使用文件的创建时间 (ctime) 进行判断,默认为修改时间 (mtime)。\n' | |
'在 WSL2/Windows 环境下,mtime 通常更能反映文件何时被复制或修改。' | |
) | |
parser.add_argument( | |
'--ext', | |
type=str, | |
default=".mp4,.mkv", | |
help='要查找的视频文件扩展名列表,用逗号分隔 (默认: ".mp4,.mkv")' | |
) | |
parser.add_argument( | |
'--debug', | |
action='store_true', | |
help='启用调试输出,显示每个视频文件的时间戳和判断过程。' | |
) | |
args = parser.parse_args() | |
# 将时间长度(小时)转换为秒 | |
threshold_seconds = args.duration * 3600 | |
# 根据 --ctime 参数决定使用 ctime 还是 mtime | |
use_mtime = not args.ctime # 如果提供了 --ctime,则 use_mtime 为 False | |
# 处理扩展名列表,转为小写元组 | |
video_extensions = tuple(ext.strip().lower() for ext in args.ext.split(',') if ext.strip()) | |
if not video_extensions: | |
print("错误:未指定有效的视频文件扩展名。", file=sys.stderr) | |
sys.exit(1) | |
# 检查 rsync 命令是否存在 | |
try: | |
subprocess.run(['rsync', '--version'], check=True, capture_output=True) | |
except FileNotFoundError: | |
print(f"错误:rsync 命令未找到。请确保 rsync 已安装并位于 PATH 中。", file=sys.stderr) | |
print("在 WSL2 中,通常可以通过 `sudo apt update && sudo apt install rsync` 安装。", file=sys.stderr) | |
sys.exit(1) | |
except Exception as e: | |
print(f"警告:检查 rsync 版本时出错:{e}", file=sys.stderr) | |
# 执行查找和上传过程 | |
find_and_upload_recent_videos( | |
args.start_dir, | |
threshold_seconds, | |
args.dest, | |
video_extensions, | |
use_mtime, | |
args.debug | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment