feat: 视频渲染后随机边缘裁剪 2-5% 降重 #1664 (#1682)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 3s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
CI/CD Pipeline / Check push changed paths (push) Successful in 5s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 12s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 12s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 24s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 21s
CI/CD Pipeline / Build Staging API Image (push) Successful in 29s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 22s
CI/CD Pipeline / CI Gate (pull_request) Successful in 4s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m45s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 2m0s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m16s
CI/CD Pipeline / Integration Tests (push) Successful in 2m18s
CI/CD Pipeline / Validate - Style (push) Successful in 2m57s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m6s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m38s
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 5m8s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m10s
CI/CD Pipeline / Validate - Security (push) Successful in 5m45s
AI Code Review / AI Code Review (pull_request) Successful in 6m19s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 5m12s
CI/CD Pipeline / Unit Tests (push) Successful in 8m15s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Failing after 291h19m59s
CI/CD Pipeline / Deploy Production (push) Failing after 291h19m59s
CI/CD Pipeline / CI Gate (push) Failing after 291h20m1s
CI/CD Pipeline / Build Production Web Image (push) Failing after 291h20m2s
CI/CD Pipeline / Build Production API Image (push) Failing after 291h20m2s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Failing after 291h25m10s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 291h27m31s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 291h27m31s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 291h27m31s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 291h27m32s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 291h27m32s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 291h27m33s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 291h27m35s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 291h27m38s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 291h27m38s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 291h27m39s
CI/CD Pipeline / Frontend Lint (push) Failing after 291h28m18s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 291h28m21s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 291h28m21s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 291h28m22s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 291h28m21s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 291h28m22s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Failing after 291h28m22s
CI/CD Pipeline / Validate - Security (pull_request) Failing after 291h28m22s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 291h28m24s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 291h28m24s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 291h28m25s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 291h28m25s
CI/CD Pipeline / PR Build Web Image (push) Failing after 291h28m25s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 291h28m27s
CI/CD Pipeline / PR Build API Image (push) Failing after 291h28m25s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 291h28m27s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 291h54m28s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 292h2m0s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 292h2m4s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 292h2m48s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 292h2m48s
CI/CD Pipeline / Validate - Style (pull_request) Failing after 292h2m48s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 3s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
CI/CD Pipeline / Check push changed paths (push) Successful in 5s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 12s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 12s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 24s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 21s
CI/CD Pipeline / Build Staging API Image (push) Successful in 29s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 22s
CI/CD Pipeline / CI Gate (pull_request) Successful in 4s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m45s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 2m0s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m16s
CI/CD Pipeline / Integration Tests (push) Successful in 2m18s
CI/CD Pipeline / Validate - Style (push) Successful in 2m57s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m6s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m38s
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 5m8s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m10s
CI/CD Pipeline / Validate - Security (push) Successful in 5m45s
AI Code Review / AI Code Review (pull_request) Successful in 6m19s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 5m12s
CI/CD Pipeline / Unit Tests (push) Successful in 8m15s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Failing after 291h19m59s
CI/CD Pipeline / Deploy Production (push) Failing after 291h19m59s
CI/CD Pipeline / CI Gate (push) Failing after 291h20m1s
CI/CD Pipeline / Build Production Web Image (push) Failing after 291h20m2s
CI/CD Pipeline / Build Production API Image (push) Failing after 291h20m2s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Failing after 291h25m10s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 291h27m31s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 291h27m31s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 291h27m31s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 291h27m32s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 291h27m32s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 291h27m33s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 291h27m35s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 291h27m38s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 291h27m38s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 291h27m39s
CI/CD Pipeline / Frontend Lint (push) Failing after 291h28m18s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 291h28m21s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 291h28m21s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 291h28m22s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 291h28m21s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 291h28m22s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Failing after 291h28m22s
CI/CD Pipeline / Validate - Security (pull_request) Failing after 291h28m22s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 291h28m24s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 291h28m24s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 291h28m25s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 291h28m25s
CI/CD Pipeline / PR Build Web Image (push) Failing after 291h28m25s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 291h28m27s
CI/CD Pipeline / PR Build API Image (push) Failing after 291h28m25s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 291h28m27s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 291h54m28s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 292h2m0s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 292h2m4s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 292h2m48s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 292h2m48s
CI/CD Pipeline / Validate - Style (pull_request) Failing after 292h2m48s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com> Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
This commit was merged in pull request #1682.
This commit is contained in:
@@ -304,3 +304,127 @@ def normalize_video(
|
||||
]
|
||||
run_ffmpeg(command)
|
||||
return {"width": width, "height": height, "path": output_path}
|
||||
|
||||
|
||||
def random_edge_crop(
|
||||
input_path: str | Path,
|
||||
output_path: str | Path | None = None,
|
||||
*,
|
||||
min_crop_pct: float = 0.02,
|
||||
max_crop_pct: float = 0.05,
|
||||
) -> Path:
|
||||
"""对视频四边做随机裁剪再缩放回原分辨率,用于改变 pHash 指纹。
|
||||
|
||||
Args:
|
||||
input_path: 输入视频路径
|
||||
output_path: 输出路径;为 None 时写入 input_path 同目录的临时文件,
|
||||
成功后覆盖原文件
|
||||
min_crop_pct: 每边最小裁剪比例(默认 2%)
|
||||
max_crop_pct: 每边最大裁剪比例(默认 5%)
|
||||
|
||||
Returns:
|
||||
输出文件路径(Path 对象)
|
||||
|
||||
Raises:
|
||||
subprocess.CalledProcessError: ffmpeg 执行失败时抛出
|
||||
"""
|
||||
import random
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
input_path = Path(input_path)
|
||||
|
||||
# 获取原始分辨率
|
||||
info = probe_video_info(str(input_path))
|
||||
W = info["width"]
|
||||
H = info["height"]
|
||||
|
||||
if W <= 0 or H <= 0:
|
||||
logger.warning("无法获取视频分辨率 (W=%d H=%d),跳过裁剪: %s", W, H, input_path)
|
||||
return input_path
|
||||
|
||||
# 四边各自随机裁剪 2%~5%
|
||||
crop_top = int(H * random.uniform(min_crop_pct, max_crop_pct))
|
||||
crop_bottom = int(H * random.uniform(min_crop_pct, max_crop_pct))
|
||||
crop_left = int(W * random.uniform(min_crop_pct, max_crop_pct))
|
||||
crop_right = int(W * random.uniform(min_crop_pct, max_crop_pct))
|
||||
|
||||
# 裁剪后尺寸(确保至少 2 像素)
|
||||
new_w = max(W - crop_left - crop_right, 2)
|
||||
new_h = max(H - crop_top - crop_bottom, 2)
|
||||
x_offset = crop_left
|
||||
y_offset = crop_top
|
||||
|
||||
# 确保裁剪尺寸为偶数(ffmpeg 编码器常要求偶数尺寸)
|
||||
new_w = new_w if new_w % 2 == 0 else new_w - 1
|
||||
new_h = new_h if new_h % 2 == 0 else new_h - 1
|
||||
if new_w < 2:
|
||||
new_w = 2
|
||||
if new_h < 2:
|
||||
new_h = 2
|
||||
|
||||
# 输出分辨率必须与原始一致
|
||||
out_w = W if W % 2 == 0 else W + 1
|
||||
out_h = H if H % 2 == 0 else H + 1
|
||||
|
||||
vf = f"crop={new_w}:{new_h}:{x_offset}:{y_offset},scale={out_w}:{out_h}"
|
||||
|
||||
logger.info(
|
||||
"随机边缘裁剪: %s → crop(%d,%d,%d,%d)=%dx%d scale→%dx%d",
|
||||
input_path.name,
|
||||
crop_top,
|
||||
crop_bottom,
|
||||
crop_left,
|
||||
crop_right,
|
||||
new_w,
|
||||
new_h,
|
||||
out_w,
|
||||
out_h,
|
||||
)
|
||||
|
||||
# 确定输出路径
|
||||
if output_path is None:
|
||||
temp_fd, temp_path = tempfile.mkstemp(suffix=".mp4", dir=input_path.parent)
|
||||
import os
|
||||
|
||||
os.close(temp_fd)
|
||||
temp_output = Path(temp_path)
|
||||
replace_original = True
|
||||
else:
|
||||
temp_output = Path(output_path)
|
||||
replace_original = False
|
||||
|
||||
command = [
|
||||
FFMPEG_BIN,
|
||||
"-y",
|
||||
"-i",
|
||||
str(input_path),
|
||||
"-vf",
|
||||
vf,
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-preset",
|
||||
"fast",
|
||||
"-crf",
|
||||
"18",
|
||||
"-c:a",
|
||||
"copy",
|
||||
"-movflags",
|
||||
"+faststart",
|
||||
str(temp_output),
|
||||
]
|
||||
|
||||
try:
|
||||
run_ffmpeg(command)
|
||||
except Exception:
|
||||
# 裁剪失败时清理临时文件
|
||||
if temp_output.exists() and replace_original:
|
||||
temp_output.unlink(missing_ok=True)
|
||||
raise
|
||||
|
||||
# 成功 → 覆盖原文件
|
||||
if replace_original:
|
||||
shutil.move(str(temp_output), str(input_path))
|
||||
return input_path
|
||||
|
||||
return temp_output
|
||||
|
||||
Reference in New Issue
Block a user