From 59b16c26735c3de9ef4ef2acee840dbdc08a9766 Mon Sep 17 00:00:00 2001 From: Dagur Date: Tue, 19 May 2026 22:29:29 +0300 Subject: [PATCH] =?UTF-8?q?chore:=20initial=20commit=20=E2=80=94=20video?= =?UTF-8?q?=20processing=20pipeline?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude --- .gitignore | 6 ++++ 1_start_container.bat | 5 +++ 2_process.bat | 5 +++ 3_stop_container.bat | 5 +++ CLAUDE.md | 20 +++++++++++ config/settings.yaml | 16 +++++++++ input/.gitkeep | 0 output/.gitkeep | 0 scripts/Dockerfile | 16 +++++++++ scripts/docker-compose.yml | 16 +++++++++ scripts/process.py | 73 ++++++++++++++++++++++++++++++++++++++ scripts/requirements.txt | 2 ++ 12 files changed, 164 insertions(+) create mode 100644 .gitignore create mode 100644 1_start_container.bat create mode 100644 2_process.bat create mode 100644 3_stop_container.bat create mode 100644 CLAUDE.md create mode 100644 config/settings.yaml create mode 100644 input/.gitkeep create mode 100644 output/.gitkeep create mode 100644 scripts/Dockerfile create mode 100644 scripts/docker-compose.yml create mode 100644 scripts/process.py create mode 100644 scripts/requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9632ae2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +input/* +!input/.gitkeep +output/* +!output/.gitkeep +__pycache__/ +*.pyc diff --git a/1_start_container.bat b/1_start_container.bat new file mode 100644 index 0000000..f68a89c --- /dev/null +++ b/1_start_container.bat @@ -0,0 +1,5 @@ +@echo off +cd /d D:\My_clips\scripts +docker compose up -d +echo ✅ Контейнер запущен +pause \ No newline at end of file diff --git a/2_process.bat b/2_process.bat new file mode 100644 index 0000000..5410198 --- /dev/null +++ b/2_process.bat @@ -0,0 +1,5 @@ +@echo off +cd /d D:\My_clips\scripts +docker compose run --rm processor python process.py +echo ✅ Готово. Файлы в output\ +pause \ No newline at end of file diff --git a/3_stop_container.bat b/3_stop_container.bat new file mode 100644 index 0000000..9d8c9a1 --- /dev/null +++ b/3_stop_container.bat @@ -0,0 +1,5 @@ +@echo off +cd /d D:\My_clips\scripts +docker compose down +echo ✅ Контейнер остановлен +pause \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..85f5c32 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,20 @@ +# My_clips — video batch processor + +Docker-based pipeline: input/ → ffmpeg (mirror/speed/filter) → output/ + +## Git + +Remote: `gitea:Dagur/my-clips.git` (self-hosted Gitea, SSH port 222) +User: Dagur + +**Commit rules:** +- Commit at logical checkpoints — after completing a feature, fix, or refactor +- Push after every commit +- Use conventional commits: `feat:`, `fix:`, `refactor:`, `chore:`, `docs:` +- Commit message in Russian if project context is Russian +- Never commit: .env, credentials, secrets, large binaries, input/*.mp4, output/*.mp4 +- Keep commits small and focused — one thing per commit + +**Before committing:** +- Verify no secrets in staged files (`git diff --staged`) +- Verify it's not a large binary file diff --git a/config/settings.yaml b/config/settings.yaml new file mode 100644 index 0000000..6d146c0 --- /dev/null +++ b/config/settings.yaml @@ -0,0 +1,16 @@ +# Настройки обработки видео +processing: + # Отразить видео по горизонтали + mirror: true + + speed: 1.1 + + # Фильтр цвета (выбери один): + # - none: без изменений + # - warm: теплее + # - cool: холоднее + # - vintage: винтаж + filter: vintage + + # Качество вывода (0-51, меньше = лучше, 18-23 оптимально) + crf: 21 \ No newline at end of file diff --git a/input/.gitkeep b/input/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/output/.gitkeep b/output/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/scripts/Dockerfile b/scripts/Dockerfile new file mode 100644 index 0000000..5a9103a --- /dev/null +++ b/scripts/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.11-slim + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ffmpeg \ + wget \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /workspace + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY process.py . + +CMD ["python", "process.py"] \ No newline at end of file diff --git a/scripts/docker-compose.yml b/scripts/docker-compose.yml new file mode 100644 index 0000000..ce2c3a5 --- /dev/null +++ b/scripts/docker-compose.yml @@ -0,0 +1,16 @@ +services: + processor: + build: . + volumes: + - ../input:/workspace/input + - ../output:/workspace/output + - ../config:/workspace/config + environment: + - PYTHONUNBUFFERED=1 + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: 1 + capabilities: [gpu] \ No newline at end of file diff --git a/scripts/process.py b/scripts/process.py new file mode 100644 index 0000000..72e85c8 --- /dev/null +++ b/scripts/process.py @@ -0,0 +1,73 @@ +import sys +import yaml +import subprocess +from pathlib import Path + +def main(): + with open('/workspace/config/settings.yaml', 'r', encoding='utf-8') as f: + config = yaml.safe_load(f) + + p = config['processing'] + + # Видео-фильтры + vf_parts = [] + if p.get('mirror'): + vf_parts.append('hflip') + + speed = p.get('speed', 1.0) + if speed != 1.0: + vf_parts.append(f'setpts={1/speed:.3f}*PTS') + + f_type = p.get('filter', 'none') + if f_type == 'warm': + vf_parts.append('eq=saturation=1.15:contrast=1.05:brightness=0.02') + elif f_type == 'cool': + vf_parts.append('eq=saturation=0.85:contrast=1.0:brightness=-0.02') + elif f_type == 'vintage': + vf_parts.append('eq=contrast=1.1:saturation=0.8:brightness=0.05') + + vf = ','.join(vf_parts) if vf_parts else None + + # Аудио-фильтры (только для скорости) + af = f'atempo={speed}' if speed != 1.0 else None + + input_dir = Path('/workspace/input') + output_dir = Path('/workspace/output') + output_dir.mkdir(exist_ok=True) + + files = [f for f in input_dir.iterdir() if f.suffix.lower() in ('.mp4', '.mov', '.avi', '.mkv')] + if not files: + print("⚠️ Папка input пуста") + sys.exit(0) + + print(f"📋 mirror={p['mirror']}, speed={p['speed']}, filter={p['filter']}") + print(f"🎬 Найдено: {len(files)}\n") + + for idx, src in enumerate(files, 1): + dst = output_dir / f"processed_{src.stem}.mp4" + print(f"[{idx}/{len(files)}] {src.name} -> ", end="", flush=True) + + cmd = ['ffmpeg', '-y', '-i', str(src)] + + if vf: + cmd.extend(['-vf', vf]) + if af: + cmd.extend(['-af', af]) + + cmd.extend([ + '-c:v', 'libx264', + '-c:a', 'aac', + '-preset', 'fast', + '-crf', str(p.get('crf', 21)), + str(dst) + ]) + + try: + subprocess.run(cmd, capture_output=True, text=True, check=True) + print("✅") + except subprocess.CalledProcessError as e: + print("❌") + print(f"🔍 STDERR: {e.stderr}") + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/scripts/requirements.txt b/scripts/requirements.txt new file mode 100644 index 0000000..9acc4b7 --- /dev/null +++ b/scripts/requirements.txt @@ -0,0 +1,2 @@ +watchdog==3.0.0 +pyyaml==6.0.1 \ No newline at end of file