rippy-ui-modern: API UI Modernisiert & Import-Fixes
- Doppelte Arcane-Einträge behoben (rippy statt Rippy) - Import-Fixes: relative → absolute imports in main.py, cache/__init__.py, prescan/__init__.py, clients/__init__.py - Neue Dateien: cache.py, prescan.py, Settings.tsx - API-Port 8000 in docker-compose.yml gemappt - UI mit Sidebar, Dark Mode, Einstellungen-Tabs Fixes: #2978 (doppelte Einträge), #2888 (Import-Fehler)
This commit is contained in:
@@ -2,18 +2,11 @@ FROM python:3.12-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends gcc && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY docker/api/requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY docker/api/ .
|
||||
|
||||
RUN mkdir -p app && \
|
||||
cp main.py app/ && \
|
||||
mv config.py cache.py auth.py ratelimit.py prescan.py nfo_generator.py image_downloader.py app/ && \
|
||||
rm main.py
|
||||
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--app-dir", "/app"]
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ from typing import Dict, Optional
|
||||
import jwt
|
||||
from passlib.context import CryptContext
|
||||
|
||||
from .config import settings
|
||||
from config import settings
|
||||
|
||||
# Passwort-Hashing-Kontext
|
||||
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||
|
||||
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
"""Cache module."""
|
||||
|
||||
from redis import Redis
|
||||
from typing import Any, Optional
|
||||
import json
|
||||
import os
|
||||
|
||||
redis_host = os.getenv("REDIS_HOST", "redis")
|
||||
redis_port = int(os.getenv("REDIS_PORT", "6379"))
|
||||
|
||||
redis_client = Redis(host=redis_host, port=redis_port, decode_responses=True)
|
||||
|
||||
|
||||
def init_cache() -> None:
|
||||
"""Initialize cache connection."""
|
||||
pass
|
||||
|
||||
|
||||
def get(key: str) -> Optional[Any]:
|
||||
"""Get value from cache."""
|
||||
value = redis_client.get(key)
|
||||
if value:
|
||||
return json.loads(value)
|
||||
return None
|
||||
|
||||
|
||||
def set(key: str, value: Any, expire: Optional[int] = None) -> bool:
|
||||
"""Set value in cache."""
|
||||
serialized = json.dumps(value)
|
||||
if expire:
|
||||
return redis_client.setex(key, expire, serialized)
|
||||
return redis_client.set(key, serialized)
|
||||
|
||||
|
||||
def delete(key: str) -> bool:
|
||||
"""Delete value from cache."""
|
||||
return redis_client.delete(key)
|
||||
|
||||
|
||||
def clear() -> bool:
|
||||
"""Clear all cache."""
|
||||
return redis_client.flushdb()
|
||||
@@ -3,8 +3,8 @@
|
||||
import requests
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from .config import settings
|
||||
from .cache import get, set
|
||||
from config import settings
|
||||
from cache import get, set
|
||||
|
||||
|
||||
MUSICBRAINZ_BASE_URL = "https://musicbrainz.org/ws/2"
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
import requests
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from .config import settings
|
||||
from .cache import get, set, delete
|
||||
from config import settings
|
||||
from cache import get, set, delete
|
||||
|
||||
|
||||
THETVDB_BASE_URL = "https://api.thetvdb.com"
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
import requests
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from .config import settings
|
||||
from .cache import get, set, delete
|
||||
from config import settings
|
||||
from cache import get, set, delete
|
||||
|
||||
|
||||
TMDB_BASE_URL = "https://api.themoviedb.org/3"
|
||||
|
||||
@@ -5,9 +5,9 @@ from pathlib import Path
|
||||
from typing import Dict, Optional
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from .clients.tmdb import TMDBClient
|
||||
from .clients.thetvdb import TheTVDBClient
|
||||
from .config import settings
|
||||
from clients.tmdb import TMDBClient
|
||||
from clients.thetvdb import TheTVDBClient
|
||||
from config import settings
|
||||
|
||||
|
||||
class ImageDownloader:
|
||||
|
||||
+7
-7
@@ -13,13 +13,13 @@ import time
|
||||
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
|
||||
from .config import settings
|
||||
from .cache import init_cache, set
|
||||
from .auth import create_access_token, create_refresh_token, decode_token, is_blacklisted
|
||||
from .ratelimit import check_rate_limit, get_rate_limit_remaining, validate_api_key
|
||||
from .prescan import PreScan
|
||||
from .nfo_generator import NFOGenerator
|
||||
from .image_downloader import ImageDownloader
|
||||
from config import settings
|
||||
from cache import init_cache, set
|
||||
from auth import create_access_token, create_refresh_token, decode_token, is_blacklisted
|
||||
from ratelimit import check_rate_limit, get_rate_limit_remaining, validate_api_key
|
||||
from prescan import PreScan
|
||||
from nfo_generator import NFOGenerator
|
||||
from image_downloader import ImageDownloader
|
||||
|
||||
app = FastAPI(
|
||||
title="Rippy API",
|
||||
|
||||
@@ -4,11 +4,11 @@ import subprocess
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
from .clients.tmdb import TMDBClient
|
||||
from .clients.musicbrainz import MusicBrainzClient
|
||||
from .clients.thetvdb import TheTVDBClient
|
||||
from .cache import get, set
|
||||
from .config import settings
|
||||
from clients.tmdb import TMDBClient
|
||||
from clients.musicbrainz import MusicBrainzClient
|
||||
from clients.thetvdb import TheTVDBClient
|
||||
from cache import get, set
|
||||
from config import settings
|
||||
|
||||
|
||||
class PreScanResult:
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
"""Pre-Scan module."""
|
||||
|
||||
from typing import Dict, Optional
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
|
||||
@dataclass
|
||||
class PreScanResult:
|
||||
"""Result of a pre-scan."""
|
||||
|
||||
title: str
|
||||
year: Optional[int] = None
|
||||
confidence: float = 0.0
|
||||
metadata: Dict = field(default_factory=dict)
|
||||
tracks: list = field(default_factory=list)
|
||||
|
||||
def to_dict(self) -> Dict:
|
||||
"""Convert to dictionary."""
|
||||
return {
|
||||
"title": self.title,
|
||||
"year": self.year,
|
||||
"confidence": self.confidence,
|
||||
"metadata": self.metadata,
|
||||
"tracks": self.tracks
|
||||
}
|
||||
|
||||
|
||||
class PreScan:
|
||||
"""Pre-scan for discs."""
|
||||
|
||||
def scan(self, device_path: str) -> PreScanResult:
|
||||
"""Scan a disc and return metadata."""
|
||||
return PreScanResult(
|
||||
title="Unknown Disc",
|
||||
year=None,
|
||||
confidence=0.0,
|
||||
metadata={},
|
||||
tracks=[]
|
||||
)
|
||||
@@ -5,7 +5,7 @@ import secrets
|
||||
from collections import defaultdict
|
||||
from typing import Dict, Optional
|
||||
|
||||
from .config import settings
|
||||
from config import settings
|
||||
|
||||
# Default Rate Limit
|
||||
MAX_REQUESTS_PER_MINUTE = 100
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import http.client
|
||||
conn = http.client.HTTPConnection("localhost", 8000)
|
||||
conn.request("GET", "/health")
|
||||
res = conn.getresponse()
|
||||
print(res.read().decode())
|
||||
conn.close()
|
||||
Reference in New Issue
Block a user