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:
@@ -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
@@ -1,62 +1,42 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Arcane VM Setup Script für Ubuntu VM
|
|
||||||
# Führe dies auf der Arcane VM aus
|
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
echo "=== Arcane VM Setup ==="
|
echo "=== Arcane VM Setup ==="
|
||||||
|
echo "Tu77ceu2zzvx!" | sudo -S apt-get update -y
|
||||||
# System aktualisieren
|
echo "Tu77ceu2zzvx!" | sudo -S apt-get upgrade -y
|
||||||
echo "System aktualisieren..."
|
curl -fsSL https://get.docker.com | sudo sh
|
||||||
apt-get update && apt-get upgrade -y
|
echo "Tu77ceu2zzvx!" | sudo -S usermod -aG docker arcane
|
||||||
|
mkdir -p /home/arcane/projects
|
||||||
# Docker installieren
|
chown arcane:arcane /home/arcane/projects
|
||||||
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..."
|
|
||||||
mkdir -p /usr/local/lib/docker/cli-plugins
|
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
|
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
|
chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
|
||||||
|
ENCRYPTION_KEY=$(openssl rand -hex 32)
|
||||||
# Docker Compose Datei erstellen
|
JWT_SECRET=$(openssl rand -hex 32)
|
||||||
cat > /opt/docker-compose.yml << 'EOF'
|
cat > /home/arcane/docker-compose.yml << COMPOSEEOF
|
||||||
version: '3.8'
|
version: "3.8"
|
||||||
services:
|
services:
|
||||||
arcane:
|
arcane:
|
||||||
image: ghcr.io/getarcaneapp/manager:latest
|
image: ghcr.io/getarcaneapp/manager:latest
|
||||||
container_name: arcane
|
container_name: arcane
|
||||||
ports:
|
ports:
|
||||||
- '3552:3552'
|
- "3552:3552"
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
- arcane-data:/app/data
|
- arcane-data:/app/data
|
||||||
- /opt/docker/projects:/app/data/projects
|
- /home/arcane/projects:/app/data/projects
|
||||||
environment:
|
environment:
|
||||||
- PROJECTS_DIRECTORY=/app/data/projects
|
- PROJECTS_DIRECTORY=/app/data/projects
|
||||||
- APP_URL=http://192.168.178.162:3552
|
- APP_URL=http://192.168.178.162:3552
|
||||||
- PUID=1000
|
- PUID=1000
|
||||||
- PGID=1000
|
- PGID=1000
|
||||||
- ENCRYPTION_KEY=$(openssl rand -hex 32)
|
- ENCRYPTION_KEY=$ENCRYPTION_KEY
|
||||||
- JWT_SECRET=$(openssl rand -hex 32)
|
- JWT_SECRET=$JWT_SECRET
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
arcane-data:
|
arcane-data:
|
||||||
EOF
|
COMPOSEEOF
|
||||||
|
cd /home/arcane
|
||||||
# Arcane starten
|
|
||||||
echo "Starte Arcane..."
|
|
||||||
cd /opt
|
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
|
echo "=== Setup abgeschlossen ==="
|
||||||
echo "=== Arcane VM Setup abgeschlossen ==="
|
|
||||||
echo "Arcane ist erreichbar unter: http://192.168.178.162:3552"
|
echo "Arcane ist erreichbar unter: http://192.168.178.162:3552"
|
||||||
echo "Projects Directory: /opt/docker/projects"
|
echo "Projects Directory: /home/arcane/projects"
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -90,6 +90,7 @@ services:
|
|||||||
- ./docker/worker:/app
|
- ./docker/worker:/app
|
||||||
- ./udev:/app/udev
|
- ./udev:/app/udev
|
||||||
- /dev:/dev:ro
|
- /dev:/dev:ro
|
||||||
|
- ./output:/output
|
||||||
networks:
|
networks:
|
||||||
- rippy-backend
|
- rippy-backend
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ WORKDIR /app
|
|||||||
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
curl \
|
curl \
|
||||||
|
abcde \
|
||||||
|
flac \
|
||||||
|
ffmpeg \
|
||||||
|
handbrake-cli \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
RUN pip install --no-cache-dir \
|
RUN pip install --no-cache-dir \
|
||||||
|
|||||||
@@ -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)
|
||||||
+47
-7
@@ -1,15 +1,55 @@
|
|||||||
import os
|
import os
|
||||||
|
import subprocess
|
||||||
from celery_app import celery_app
|
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_type(device_path: str) -> str:
|
||||||
def detect_disc(self, device_path: str, disc_type: str):
|
"""Erkennt den Disc-Typ anhand des Gerätepfads."""
|
||||||
"""Dummy-Disk-Erkennungstask."""
|
try:
|
||||||
print(f"Disc erkannt: {disc_type}, Device: {device_path}")
|
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"
|
||||||
|
|
||||||
|
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 {
|
return {
|
||||||
"status": "success",
|
"status": "not_implemented",
|
||||||
"message": f"Disc erkannt: {disc_type}",
|
"message": "CD-Ripping mit abcde noch nicht implementiert",
|
||||||
"device_path": device_path,
|
"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
|
"disc_type": disc_type
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user