Etappe 2: Ripping-Pipeline — Erste Implementierung

- DockerfileWorker: abcde, flac, ffmpeg, handbrake-cli installiert
- ripping.py: Ripping-Module für DVD/Blu-ray mit HandBrake
- tasks.py: Disc-Typ-Erkennung + passendes Ripping-Modul
- docker-compose.yml: Output-Verzeichnis /output hinzugefügt
- output/ Verzeichnis erstellt

WIP: CD-Ripping mit abcde noch nicht implementiert
This commit is contained in:
Hitonabi
2026-07-21 16:47:50 +02:00
parent 594f07f7a3
commit 9e73d065f9
7 changed files with 227 additions and 48 deletions
+42
View File
@@ -0,0 +1,42 @@
#!/bin/bash
set -e
echo "=== Arcane VM Setup ==="
echo "Tu77ceu2zzvx!" | sudo -S apt-get update
echo "Tu77ceu2zzvx!" | sudo -S apt-get upgrade -y
curl -fsSL https://get.docker.com | sudo sh
echo "Tu77ceu2zzvx!" | sudo -S usermod -aG docker arcane
mkdir -p /opt/docker/projects
chown arcane:arcane /opt/docker/projects
mkdir -p /usr/local/lib/docker/cli-plugins
curl -SL https://github.com/docker/compose/releases/download/v2.29.2/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose
chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
ENCRYPTION_KEY=$(openssl rand -hex 32)
JWT_SECRET=$(openssl rand -hex 32)
cat > /opt/docker-compose.yml << COMPOSEEOF
version: "3.8"
services:
arcane:
image: ghcr.io/getarcaneapp/manager:latest
container_name: arcane
ports:
- "3552:3552"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- arcane-data:/app/data
- /opt/docker/projects:/app/data/projects
environment:
- PROJECTS_DIRECTORY=/app/data/projects
- APP_URL=http://192.168.178.162:3552
- PUID=1000
- PGID=1000
- ENCRYPTION_KEY=$ENCRYPTION_KEY
- JWT_SECRET=$JWT_SECRET
restart: unless-stopped
volumes:
arcane-data:
COMPOSEEOF
cd /opt
docker compose up -d
echo "=== Setup abgeschlossen ==="
echo "Arcane ist erreichbar unter: http://192.168.178.162:3552"
echo "Projects Directory: /opt/docker/projects"
+18 -38
View File
@@ -1,62 +1,42 @@
#!/bin/bash
# Arcane VM Setup Script für Ubuntu VM
# Führe dies auf der Arcane VM aus
set -e
echo "=== Arcane VM Setup ==="
# System aktualisieren
echo "System aktualisieren..."
apt-get update && apt-get upgrade -y
# Docker installieren
echo "Installiere Docker..."
curl -fsSL https://get.docker.com | sh
# User zu Docker-Gruppe hinzufügen
usermod -aG docker arcane
# Verzeichnisse erstellen
mkdir -p /opt/docker/projects
chown arcane:arcane /opt/docker/projects
# Docker Compose installieren
echo "Installiere Docker Compose..."
echo "Tu77ceu2zzvx!" | sudo -S apt-get update -y
echo "Tu77ceu2zzvx!" | sudo -S apt-get upgrade -y
curl -fsSL https://get.docker.com | sudo sh
echo "Tu77ceu2zzvx!" | sudo -S usermod -aG docker arcane
mkdir -p /home/arcane/projects
chown arcane:arcane /home/arcane/projects
mkdir -p /usr/local/lib/docker/cli-plugins
curl -SL https://github.com/docker/compose/releases/download/v2.29.2/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose
chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
# Docker Compose Datei erstellen
cat > /opt/docker-compose.yml << 'EOF'
version: '3.8'
ENCRYPTION_KEY=$(openssl rand -hex 32)
JWT_SECRET=$(openssl rand -hex 32)
cat > /home/arcane/docker-compose.yml << COMPOSEEOF
version: "3.8"
services:
arcane:
image: ghcr.io/getarcaneapp/manager:latest
container_name: arcane
ports:
- '3552:3552'
- "3552:3552"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- arcane-data:/app/data
- /opt/docker/projects:/app/data/projects
- /home/arcane/projects:/app/data/projects
environment:
- PROJECTS_DIRECTORY=/app/data/projects
- APP_URL=http://192.168.178.162:3552
- PUID=1000
- PGID=1000
- ENCRYPTION_KEY=$(openssl rand -hex 32)
- JWT_SECRET=$(openssl rand -hex 32)
- ENCRYPTION_KEY=$ENCRYPTION_KEY
- JWT_SECRET=$JWT_SECRET
restart: unless-stopped
volumes:
arcane-data:
EOF
# Arcane starten
echo "Starte Arcane..."
cd /opt
COMPOSEEOF
cd /home/arcane
docker compose up -d
echo "=== Arcane VM Setup abgeschlossen ==="
echo "=== Setup abgeschlossen ==="
echo "Arcane ist erreichbar unter: http://192.168.178.162:3552"
echo "Projects Directory: /opt/docker/projects"
echo "Projects Directory: /home/arcane/projects"
+26
View File
@@ -0,0 +1,26 @@
ENCRYPTION_KEY=$(openssl rand -hex 32)
JWT_SECRET=$(openssl rand -hex 32)
cat > /home/arcane/docker-compose.yml << EOF
version: '3.8'
services:
arcane:
image: ghcr.io/getarcaneapp/manager:latest
container_name: arcane
ports:
- '3552:3552'
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- arcane-data:/app/data
- /home/arcane/projects:/app/data/projects
environment:
- PROJECTS_DIRECTORY=/app/data/projects
- APP_URL=http://192.168.178.162:3552
- PUID=1000
- PGID=1000
- ENCRYPTION_KEY=$ENCRYPTION_KEY
- JWT_SECRET=$JWT_SECRET
restart: unless-stopped
volumes:
arcane-data:
EOF
cat /home/arcane/docker-compose.yml
+1
View File
@@ -90,6 +90,7 @@ services:
- ./docker/worker:/app
- ./udev:/app/udev
- /dev:/dev:ro
- ./output:/output
networks:
- rippy-backend
depends_on:
+4
View File
@@ -4,6 +4,10 @@ WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
abcde \
flac \
ffmpeg \
handbrake-cli \
&& rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir \
+86
View File
@@ -0,0 +1,86 @@
import os
import subprocess
import re
from celery_app import celery_app
def get_progress_from_line(line: str) -> int:
"""Extrahiert Fortschritt in Prozent aus HandBrake-Ausgabe."""
match = re.search(r'(\d+\.\d+)%', line)
if match:
return int(float(match.group(1)))
return 0
def run_handbrake(device_path: str, output_path: str) -> dict:
"""Rippt eine DVD/Blu-ray mit HandBrakeCLI."""
try:
cmd = [
"HandBrakeCLI",
"--input", device_path,
"--output", output_path,
"--all",
"--progress",
"--preset", "Fast 1080p30"
]
process = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1
)
for line in process.stdout:
progress = get_progress_from_line(line)
if progress > 0:
celery_app.send_task(
"worker.ripping.update_progress",
kwargs={
"progress": progress,
"status": "ripping",
"message": f"Rippe Titel {progress}%"
}
)
process.wait()
if process.returncode == 0:
return {
"status": "success",
"output_path": output_path,
"return_code": process.returncode
}
else:
return {
"status": "error",
"error": f"HandBrake failed with code {process.returncode}",
"return_code": process.returncode
}
except Exception as e:
return {
"status": "error",
"error": str(e)
}
@celery_app.task(bind=True, name="worker.ripping.rip_dvd")
def rip_dvd(self, device_path: str, disc_id: str) -> dict:
"""Rippt eine DVD mit HandBrake."""
output_dir = f"/output/dvd/{disc_id}"
os.makedirs(output_dir, exist_ok=True)
output_path = f"{output_dir}/dvd_{disc_id}.mkv"
return run_handbrake(device_path, output_path)
@celery_app.task(bind=True, name="worker.ripping.rip_bluray")
def rip_bluray(self, device_path: str, disc_id: str) -> dict:
"""Rippt eine Blu-ray mit HandBrake."""
output_dir = f"/output/bluray/{disc_id}"
os.makedirs(output_dir, exist_ok=True)
output_path = f"{output_dir}/bluray_{disc_id}.mkv"
return run_handbrake(device_path, output_path)
+50 -10
View File
@@ -1,15 +1,55 @@
import os
import subprocess
from celery_app import celery_app
from ripping import rip_dvd, rip_bluray
@celery_app.task(bind=True, name="worker.tasks.detect_disc")
def detect_disc(self, device_path: str, disc_type: str):
"""Dummy-Disk-Erkennungstask."""
print(f"Disc erkannt: {disc_type}, Device: {device_path}")
def detect_disc_type(device_path: str) -> str:
"""Erkennt den Disc-Typ anhand des Gerätepfads."""
try:
result = subprocess.run(
["file", "-L", device_path],
capture_output=True,
text=True,
timeout=10
)
output = result.stdout.lower()
if "dvd" in output or "video_ts" in output:
return "dvd"
elif "bluray" in output or "bdmv" in output:
return "bluray"
elif "audio" in output or "cda" in output:
return "cd"
else:
return "unknown"
return {
"status": "success",
"message": f"Disc erkannt: {disc_type}",
"device_path": device_path,
"disc_type": disc_type
}
except Exception:
return "unknown"
@celery_app.task(bind=True, name="worker.tasks.rip_disc")
def rip_disc(self, device_path: str, disc_id: str = None):
"""Rippt eine Disc basierend auf ihrem Typ."""
disc_type = detect_disc_type(device_path)
if not disc_id:
disc_id = device_path.replace("/", "_")
if disc_type == "cd":
return {
"status": "not_implemented",
"message": "CD-Ripping mit abcde noch nicht implementiert",
"disc_type": disc_type
}
elif disc_type == "dvd":
return rip_dvd(device_path, disc_id)
elif disc_type == "bluray":
return rip_bluray(device_path, disc_id)
else:
return {
"status": "error",
"message": f"Unbekannter Disc-Typ: {disc_type}",
"disc_type": disc_type
}