Skip to content

Instantly share code, notes, and snippets.

@Sg4Dylan
Created July 13, 2025 02:30
Show Gist options
  • Save Sg4Dylan/a16f7d17804acddbcc19a9fe2081fa65 to your computer and use it in GitHub Desktop.
Save Sg4Dylan/a16f7d17804acddbcc19a9fe2081fa65 to your computer and use it in GitHub Desktop.
将指定扩展名的近期创建文件上传到远程主机
#!/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