kader bestanden toegevoegd

This commit is contained in:
kodi
2026-03-07 12:12:54 +01:00
parent 27cee7395f
commit e98abcbb78
9 changed files with 845 additions and 31 deletions
+6
View File
@@ -0,0 +1,6 @@
__pycache__/
*.pyc
*.log
.venv/
venv/
.DS_Store
+202
View File
@@ -0,0 +1,202 @@
# AGENTS.md
## Project
Rename-MVP
Minimalistische webapplicatie voor het hernoemen van TV-series bestanden op basis van metadata van TheTVDB.
Technologie:
- Python
- FastAPI
- Rootless Podman
- SQLite
- TheTVDB API v4
Project root:
/home/kodi/.config/rename-mvp
---
# AI Agent Governance
Dit project wordt ontwikkeld met behulp van AI-agents (bijvoorbeeld Codex).
Agents mogen code schrijven en aanpassen, maar **moeten zich strikt houden aan de regels in dit document**.
Het primaire doel is **stabiliteit van het systeem**.
---
# Absolute Rules
Agents mogen NOOIT:
- bestaande API responses wijzigen
- bestaande endpoints verwijderen
- SAFE FILES aanpassen zonder expliciete toestemming
- projectstructuur wijzigen
- code buiten deze repository aanpassen
- andere Podman containers aanpassen
- systeemconfiguratie wijzigen
- Agents mogen geen wijzigingen maken die het contract in contracts/API_GOLDEN.md breken.
- Agents mogen geen refactors of verbeteringen uitvoeren die niet expliciet gevraagd zijn.
Agents mogen alleen werken **binnen deze repository**.
---
# SAFE FILES
De volgende bestanden zijn kritisch en mogen alleen aangepast worden na expliciete bevestiging van de gebruiker:
Deze bestanden bevatten infrastructuur en API-contracten.
app/services/tvdb_auth_service.py
container/Containerfile
requirements.txt
contracts/API_GOLDEN.md
AGENTS.md
CHANGE_POLICY.md
SAFE_FILES.md
docs/ARCHITECTURE.md
---
# Escalated Execution
Agents mogen escalated execution gebruiken voor:
- podman build
- podman run
- curl tests
- project builds
- dependency install
- bestanden aanmaken of aanpassen binnen dit project
Agents mogen escalated execution **niet gebruiken voor**:
- systeemupdates
- firewall wijzigingen
- systemd configuratie
- container prune
- docker/podman cleanup
- andere containers
---
# Development Workflow
Elke wijziging moet deze stappen volgen:
1 analyseer de wijziging
2 beschrijf welke bestanden worden aangepast
3 implementeer wijziging
4 voer regressietests uit
5 voer feature tests uit
---
# Testing Policy
Automatische tests zijn verplicht voor elke wijziging.
Na iedere wijziging moet het volgende script worden uitgevoerd:
./regression_tests.sh
Dit script valideert:
- API health endpoint
- TVDB auth-status endpoint
- TVDB search endpoint
Als een regressietest faalt moet de wijziging worden teruggedraaid.
---
# Feature Test Policy
Nieuwe functionaliteit moet minimaal drie tests bevatten.
Agents moeten hiervoor het volgende script als basis gebruiken:
./feature_test_template.sh
De template moet worden aangepast zodat deze:
- de nieuwe endpoint of functionaliteit test
- de JSON response valideert
- controleert dat bestaande functionaliteit niet breekt
De template zelf is alleen een startpunt en moet per feature worden aangepast.
---
# Regression Tests (verplicht)
Regressietests mogen **geen nieuw TVDB-token forceren**.
De volgende tests moeten altijd slagen:
curl 127.0.0.1:8085/api/health
curl 127.0.0.1:8085/api/tvdb/auth-status
curl "127.0.0.1:8085/api/tvdb/search?q=elsbeth"
Als een van deze tests faalt moet de wijziging worden teruggedraaid.
---
# Handmatige Auth Test
De volgende endpoint forceert een nieuwe login en mag **niet in automatische regressietests worden gebruikt**:
curl -X POST 127.0.0.1:8085/api/tvdb/login
Deze endpoint is alleen bedoeld voor debugging.
---
# Feature Tests
Nieuwe functionaliteit moet minimaal drie tests bevatten.
---
# Security Rules
Agents moeten altijd:
- path traversal voorkomen
- MEDIA_ROOT restricties respecteren
- rename preview afdwingen
- secrets niet loggen
---
# Logging Rules
Agents mogen nooit loggen:
- API keys
- tokens
- environment secrets
---
# Design Principle
Stability over speed.
Het doel is een stabiele backend, geen snelle refactors.
+130
View File
@@ -0,0 +1,130 @@
# CHANGE_POLICY.md
Dit document bepaalt welke wijzigingen AI-agents zelfstandig mogen uitvoeren en welke wijzigingen expliciete toestemming vereisen.
Doel:
- ongewenste refactors voorkomen
- contractbreuk voorkomen
- stabiliteit bewaken
---
# Algemene regel
AI-agents mogen alleen kleine, lokale, testbare wijzigingen uitvoeren.
AI-agents mogen geen brede of structurele wijzigingen uitvoeren zonder expliciete toestemming van de gebruiker.
---
# Wijzigingen die ZONDER expliciete toestemming mogen
De volgende wijzigingen mogen zelfstandig worden gedaan, mits regressietests slagen:
- kleine bugfixes
- toevoegen van nieuwe, lokale helperfuncties
- toevoegen van logging zonder secrets
- uitbreiden van bestaande responses met extra velden, zolang bestaande velden intact blijven
- toevoegen van nieuwe endpoints, zolang bestaande endpoints niet wijzigen
- uitbreiden van tests
- verbeteren van foutafhandeling
- verbeteren van type hints
- kleine frontend-aanpassingen zonder wijziging van workflow
- documentatie bijwerken
---
# Wijzigingen die ALLEEN met expliciete toestemming mogen
De volgende wijzigingen zijn verboden zonder expliciete toestemming:
- wijzigen van bestaande endpointnamen
- wijzigen van bestaande response-structuren
- verwijderen van velden uit API responses
- wijzigen van token lifecycle logica
- wijzigen van rename template
- wijzigen van mount paths of allowed media roots
- wijzigen van container runtime model
- wijzigen van projectstructuur
- wijzigen van SAFE FILES
- vervangen van libraries of frameworkkeuzes
- brede refactors over meerdere modules
- wijzigen van securityregels rond file access
---
# Verboden wijzigingen
De volgende wijzigingen zijn altijd verboden zonder expliciete opdracht:
- code buiten deze repository aanpassen
- andere containers aanpassen
- system config wijzigen
- firewall wijzigen
- systemd global wijzigen
- prune, cleanup of delete acties uitvoeren buiten deze projectscope
- secrets hardcoden
- API keys of tokens loggen
---
# Refactor Policy
Geen silent refactors.
Elke refactor moet:
1. vooraf benoemd worden
2. beperkt blijven in scope
3. regressietests behouden
4. geen API contract breken
Als een wijziging niet strikt nodig is voor het gevraagde doel, moet de agent die wijziging niet doen.
---
# Test Policy
Na iedere wijziging moeten minimaal drie regressietests worden uitgevoerd:
- curl 127.0.0.1:8085/api/health
- curl 127.0.0.1:8085/api/tvdb/auth-status
- curl "127.0.0.1:8085/api/tvdb/search?q=elsbeth"
Daarnaast moeten minimaal drie tests worden uitgevoerd voor de nieuwe of gewijzigde functionaliteit.
Nieuwe feature tests moeten:
- het gevraagde gedrag verifiëren
- geen bestaande functionaliteit breken
- waar mogelijk mechanisch herhaalbaar zijn
---
# Escalated Execution Policy
Escalated execution is toegestaan binnen deze projectscope voor:
- podman build
- podman run
- curl tests
- bestanden aanmaken en aanpassen
- dependency install binnen het project
- lokale build- en testacties
Escalated execution is niet toegestaan voor:
- systeembrede wijzigingen
- andere repositories
- andere stacks of containers
- destructieve cleanup acties
---
# Output Policy
Bij elke wijziging moet de agent kort rapporteren:
- welke bestanden zijn aangepast
- waarom die wijziging nodig was
- welke regressietests zijn uitgevoerd
- welke feature tests zijn uitgevoerd
- welke risicos of aandachtspunten er nog zijn
+74
View File
@@ -0,0 +1,74 @@
# SAFE_FILES.md
Dit document definieert kritieke bestanden die AI-agents niet zelfstandig mogen wijzigen.
Doel:
- kritieke infrastructuur beschermen
- auth- en containerlogica stabiel houden
- API contract beschermen
---
# Safe Files
De volgende bestanden mogen alleen worden aangepast na expliciete toestemming van de gebruiker:
- app/services/tvdb_auth_service.py
- container/Containerfile
- requirements.txt
- AGENTS.md
- CHANGE_POLICY.md
- SAFE_FILES.md
- contracts/API_GOLDEN.md
- docs/ARCHITECTURE.md
---
# Waarom deze bestanden beschermd zijn
## app/services/tvdb_auth_service.py
Bevat werkende TVDB auth, JWT exp parsing, renew-logica en token persistence.
## container/Containerfile
Bevat de werkende runtime-basis voor het project.
## requirements.txt
Kan runtime en dependency-gedrag ingrijpend veranderen.
## AGENTS.md
Stuurt AI-gedrag aan en mag niet stilzwijgend gewijzigd worden.
## CHANGE_POLICY.md
Bevat wijzigingsregels voor AI-agents.
## SAFE_FILES.md
Bevat de lijst van beschermde bestanden zelf.
## contracts/API_GOLDEN.md
Beschermt het bestaande API contract.
## docs/ARCHITECTURE.md
Beschrijft architecturale invariants die niet ongemerkt mogen veranderen.
---
# Toegestane werkwijze bij Safe Files
Als een wijziging aan een safe file echt nodig is, moet de agent:
1. expliciet melden welk safe file gewijzigd moet worden
2. uitleggen waarom dit nodig is
3. wachten op toestemming van de gebruiker
Zonder expliciete toestemming mag een safe file niet gewijzigd worden.
---
# Niet-safe bestanden
Bestanden buiten deze lijst mogen aangepast worden, zolang:
- de wijziging binnen projectscope valt
- de API contracten niet worden gebroken
- regressietests slagen
- de architectuurregels gerespecteerd blijven
+9 -31
View File
@@ -1,34 +1,12 @@
FROM python:3.12-slim
Ik heb een paar opmerkingen
WORKDIR /app
AGENTS.md
Regression testst
curl -X POST 127.0.0.1:8085/api/tvdb/login
zorgt deze curl er niet continue voor dat er een nieuw token wordt aangevraagd en dat thetvdb mij dan gaat blokkeren?
# Install minimal system dependencies
RUN apt-get update && apt-get install -y \
curl \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
ARCHITECTURE.md
Ik wil de volumes voor de videobestanden graag als volgt. Dat komt overeen met de host en maakt het herkenbaarder
# Copy requirements
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy app
COPY app /app/app
# Create runtime dirs
RUN mkdir -p /app/data
EXPOSE 8080
ENV PYTHONUNBUFFERED=1
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8080"]
curl "127.0.0.1:8085/api/tvdb/search?q=matlock"
http://kodidebian:9098/kodi/rename-mvp.git
/Volumes/8TB/Shared_Folders/TV_Shows → /Volumes/8TB/Shared_Folders/TV_Shows
/Volumes/8TB_RAID1/Shared_Folders/Library/TV_Shows → /Volumes/8TB_RAID1/Shared_Folders/Library/TV_Shows
+85
View File
@@ -0,0 +1,85 @@
# API_GOLDEN.md
Dit document definieert het API contract.
Agents mogen dit contract **niet breken**.
---
# Health
GET /api/health
response:
{
"status": "ok"
}
---
# TVDB Login
POST /api/tvdb/login
response:
{
"status": "ok",
"issued_at": "...",
"expires_at": "...",
"renew_after": "..."
}
---
# TVDB Auth Status
GET /api/tvdb/auth-status
response:
{
"configured": true,
"has_token": true,
"expires_at": "...",
"renew_after": "...",
"token_source": "cached"
}
---
# TVDB Search
GET /api/tvdb/search?q=query
response:
{
"items": [
{
"id": "...",
"name": "...",
"year": "...",
"display_name": "..."
}
]
}
---
# Contract Rules
Agents mogen:
- nieuwe velden toevoegen
Agents mogen **niet**:
- velden verwijderen
- veldnamen wijzigen
- response structuur wijzigen
+162
View File
@@ -0,0 +1,162 @@
# ARCHITECTURE.md
## Project
Rename-MVP
Webapplicatie voor het hernoemen van mediabestanden op basis van metadata van TheTVDB.
---
# High Level Architecture
browser
FastAPI backend
TVDB service
TheTVDB API
---
# Container Architecture
Runtime:
Rootless Podman container
Image:
rename-mvp
Expose:
8085 → 8080
---
# Volumes
Host en container gebruiken identieke paden.
Volumes:
/Volumes/8TB/Shared_Folders/TV_Shows → /Volumes/8TB/Shared_Folders/TV_Shows
/Volumes/8TB_RAID1/Shared_Folders/Library/TV_Shows → /Volumes/8TB_RAID1/Shared_Folders/Library/TV_Shows
/home/kodi/.config/rename-mvp/data → /app/data
---
# Allowed Media Roots
De applicatie mag alleen bestanden lezen of wijzigen binnen de volgende directories:
/Volumes/8TB/Shared_Folders/TV_Shows
/Volumes/8TB_RAID1/Shared_Folders/Library/TV_Shows
Alle file operations moeten binnen deze paden blijven.
---
# Backend Modules
Belangrijke modules:
tvdb_auth_service
tvdb_client
tvdb_service
file_service
preview_service
rename_service
session_service
---
# TVDB Authentication Flow
1 login via /login
2 ontvang JWT token
3 decode JWT payload
4 lees exp claim
5 bereken renew_after
token wordt opgeslagen in:
/app/data/tvdb_auth.json
---
# Token Lifecycle
Token wordt alleen vernieuwd als:
now >= renew_after
of
TVDB response 401 geeft.
Token wordt **niet bij elke request vernieuwd**.
---
# File Rename Flow
1 user selecteert serie
2 afleveringen worden geladen
3 user selecteert afleveringen
4 user selecteert bestanden
5 backend genereert preview
6 user bevestigt rename
7 backend voert rename uit
---
# Data Storage
SQLite:
session state
rename logs
JSON:
tvdb_auth.json
---
# Naming Format
Bestandsnaam:
{series} ({year}) - S{season:02}E{episode:02} - {title}{ext}
Voorbeeld:
Elsbeth (2024) - S02E03 - Devil's Night.mkv
---
# Invariants
Deze eigenschappen mogen niet veranderen:
- API endpoints
- rename template
- token renew logic
- container runtime
+71
View File
@@ -0,0 +1,71 @@
#!/usr/bin/env bash
set -euo pipefail
BASE_URL="${BASE_URL:-http://127.0.0.1:8085}"
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT
echo "== Feature test 1: baseline auth-status before feature call =="
curl --fail --silent --show-error \
"${BASE_URL}/api/tvdb/auth-status" \
-o "${TMP_DIR}/auth_status_before.json"
cat "${TMP_DIR}/auth_status_before.json"
python3 - "${TMP_DIR}/auth_status_before.json" <<'PY'
import json
import sys
from pathlib import Path
path = Path(sys.argv[1])
data = json.loads(path.read_text(encoding="utf-8"))
assert isinstance(data, dict), "auth-status before must be an object"
assert "token_source" in data, "auth-status before missing token_source"
print("feature baseline auth-status validation passed")
PY
echo
echo "== Feature test 2: replace this block with the specific endpoint under test =="
echo "Example target:"
echo " ${BASE_URL}/api/tvdb/series/430543/episodes?order_type=aired"
echo
echo "Example implementation pattern:"
echo " curl --fail --silent --show-error \"\${BASE_URL}/api/your/new/endpoint\" -o \"\${TMP_DIR}/feature.json\""
echo " python3 - \"\${TMP_DIR}/feature.json\" <<'PY'"
echo " import json, sys"
echo " from pathlib import Path"
echo " data = json.loads(Path(sys.argv[1]).read_text(encoding='utf-8'))"
echo " assert isinstance(data, dict)"
echo " # add exact assertions here"
echo " print('feature JSON validation passed')"
echo " PY"
echo
echo "== Feature test 3: verify auth-status after feature call =="
curl --fail --silent --show-error \
"${BASE_URL}/api/tvdb/auth-status" \
-o "${TMP_DIR}/auth_status_after.json"
cat "${TMP_DIR}/auth_status_after.json"
python3 - "${TMP_DIR}/auth_status_after.json" <<'PY'
import json
import sys
from pathlib import Path
path = Path(sys.argv[1])
data = json.loads(path.read_text(encoding="utf-8"))
assert isinstance(data, dict), "auth-status after must be an object"
assert "token_source" in data, "auth-status after missing token_source"
assert data["token_source"] in {"none", "login", "cached", "renewed"}, \
"token_source after must be valid"
print("feature post-check auth-status validation passed")
PY
echo
echo "Feature test template completed."
echo "Replace feature test 2 with the exact endpoint and assertions for the new functionality."
+106
View File
@@ -0,0 +1,106 @@
#!/usr/bin/env bash
set -euo pipefail
BASE_URL="${BASE_URL:-http://127.0.0.1:8085}"
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT
echo "== Regression test 1: health =="
curl --fail --silent --show-error \
"${BASE_URL}/api/health" \
-o "${TMP_DIR}/health.json"
cat "${TMP_DIR}/health.json"
python3 - "${TMP_DIR}/health.json" <<'PY'
import json
import sys
from pathlib import Path
path = Path(sys.argv[1])
data = json.loads(path.read_text(encoding="utf-8"))
assert isinstance(data, dict), "health response must be an object"
assert data.get("status") == "ok", "health.status must be 'ok'"
print("health JSON validation passed")
PY
echo
echo "== Regression test 2: tvdb auth-status =="
curl --fail --silent --show-error \
"${BASE_URL}/api/tvdb/auth-status" \
-o "${TMP_DIR}/auth_status.json"
cat "${TMP_DIR}/auth_status.json"
python3 - "${TMP_DIR}/auth_status.json" <<'PY'
import json
import sys
from pathlib import Path
path = Path(sys.argv[1])
data = json.loads(path.read_text(encoding="utf-8"))
assert isinstance(data, dict), "auth-status response must be an object"
required_keys = [
"configured",
"has_token",
"expires_at",
"renew_after",
"token_source",
]
for key in required_keys:
assert key in data, f"auth-status missing key: {key}"
assert isinstance(data["configured"], bool), "configured must be boolean"
assert isinstance(data["has_token"], bool), "has_token must be boolean"
assert data["token_source"] in {"none", "login", "cached", "renewed"}, \
"token_source must be one of none/login/cached/renewed"
print("auth-status JSON validation passed")
PY
echo
echo "== Regression test 3: tvdb search =="
curl --fail --silent --show-error \
"${BASE_URL}/api/tvdb/search?q=elsbeth" \
-o "${TMP_DIR}/search.json"
cat "${TMP_DIR}/search.json"
python3 - "${TMP_DIR}/search.json" <<'PY'
import json
import sys
from pathlib import Path
path = Path(sys.argv[1])
data = json.loads(path.read_text(encoding="utf-8"))
assert isinstance(data, dict), "search response must be an object"
assert "items" in data, "search response missing items"
assert isinstance(data["items"], list), "items must be a list"
assert len(data["items"]) > 0, "items list must not be empty"
first = data["items"][0]
assert isinstance(first, dict), "first item must be an object"
required_item_keys = ["id", "name", "year", "display_name"]
for key in required_item_keys:
assert key in first, f"search item missing key: {key}"
names = [
(item.get("display_name") or item.get("name") or "")
for item in data["items"]
]
assert any("Elsbeth" in name for name in names), \
"expected at least one search result containing 'Elsbeth'"
print("search JSON validation passed")
PY
echo
echo "All regression tests passed."