API-Dokumentation
Server-Basis: http://timms4culture.hdf.de
OpenAPI 3.1 (YAML) Im Swagger-Editor öffnen
1. Allgemeines
Timms4Culture stellt zwei Familien von HTTP-Endpoints bereit:
-
Medien-URLs (
/media/<id>/…und/meta/<id>/…) liefern Original-Datei, HLS-Streams, Vorschaubilder, WebVTT-Untertitel und PBCore-XML. -
JSON-API (
/api/<namespace>.<method>.<format>) gibt Daten in JSON oder XML zurück und wird sowohl von der Web-Oberfläche als auch von externen Systemen genutzt.
Jede Video-Datei wird durch eine UUID identifiziert (Spalte
videos.id); diese ID erscheint in jeder URL und in jedem
API-Response. Optional kann jedes Video eine externe ID
tragen (z. B. LFS003055). Externe IDs sind eindeutig und
können pro Pool über eine Regex erzwungen werden.
2. Authentifizierung
Öffentliche Endpoints (Lese-Zugriff auf publizierte Inhalte) brauchen keinen Login. Schreibende Endpoints und persönliche Daten erfordern Authentifizierung. Drei Varianten werden unterstützt:
2.1 Session-Cookie
Über die Web-Oberfläche oder POST /api/auth.login.json
mit email+password. Der Server setzt ein
Session-Cookie (SameSite=Lax), das für nachfolgende
Requests verwendet wird.
2.2 Bearer-Token (API-Key)
Jeder Nutzer kann sich auf der Profil-Seite einen API-Key generieren. Der Klartext-Token wird genau einmal angezeigt; in der DB liegt nur ein SHA-256(pepper + token)-Hash. Verwendung als Header:
Authorization: Bearer <token>
2.3 Query-Parameter
Für Clients, die keine Custom-Header setzen können (z. B. einfache
<img>-Tags, manche Webhooks), wird derselbe Token auch
als ?apikey=<token>-Parameter akzeptiert.
users.is_enabled = 0) werden bei
allen Auth-Pfaden abgewiesen, auch wenn der Token noch gültig wäre.
3. Medien-URLs
Diese Routen liefern Binärdaten oder Klartext direkt aus dem
videofiles/<id>/-Verzeichnis aus. Antworten enthalten
Content-Type und Accept-Ranges wo sinnvoll.
3.1 Video-Stream
HLS-Multibitrate (1080p / 720p / 480p) wird on-the-fly aus einer Zip ausgeliefert:
Die Master-Playlist verweist auf v0/playlist.m3u8 (1080p) etc.
Original-Datei mit Byte-Range-Support (für
<video>-Direkt-Streaming, solange HLS noch nicht
konvertiert ist):
3.2 Vorschaubild
Aktives, vom Admin gewähltes oder hochgeladenes Poster:
Der Konvertierungs-Bot legt zusätzlich drei Auto-Vorschläge ab
(poster1.jpg, poster2.jpg, poster3.jpg
— Screenshots bei 25 / 50 / 75 % der Spieldauer). Existiert kein
aktives poster.jpg, fällt die Hilfsfunktion posterUrl()
der Reihe nach auf poster2 → poster1 → poster3 →
/skins/grey/images/no_thumbnail.jpg zurück.
3.3 PBCore
Strukturierte Metadaten im PBCore-XML-Format:
Liefert 404, solange kein PBCore-Dokument hochgeladen oder im Editor gespeichert wurde. Aufbau siehe Abschnitt 8.
3.4 Untertitel & Kapitel
WebVTT, pro Sprache eine Datei:
{lang} = 2–8 Kleinbuchstaben (z. B. de,
en). Beide Dateitypen sind kompatibel zu <track
kind="captions|chapters"> in video.js.
4. JSON-API: Konventionen
4.1 URL-Schema
namespaceist immer lowercase (z. B.video,profile,usermanager).methodist lowercase ohne Trenner (z. B.search,uploadstart,homesection).formatist üblicherweisejson. Alternativ:xml,successcode,jsonp.
4.2 Request
Parameter werden klassisch per application/x-www-form-urlencoded
oder multipart/form-data (für Datei-Uploads) übertragen.
Arrays als field[]=…. Auth via Session-Cookie, Bearer-Header
oder ?apikey=.
4.3 Response (JSON)
{
"error": { "id": 0, "msg": "" },
"result": { ... },
"runtime": 0.0042,
"request": { ... }
}
Bei Fehlern ist error.id > 0 und result
gleich null. request spiegelt die Eingabe-Parameter.
4.4 Beispiel-Request mit curl
curl -X POST http://timms4culture.hdf.de/api/video.search.json \
-H "Authorization: Bearer <apikey>" \
--data-urlencode "q=Heilbronn" \
--data-urlencode "page=1"
5. Endpoints (öffentlich)
5.1 video.search
Paginierte Volltext-Suche über alle publizierten Videos.
q— Suchbegriff (LIKE über Titel, externe ID, Beschreibung, Pool-Name)pool— UUID des Pools (optional)duration—short(< 5 min),mid(5–30 min),long(> 30 min)sort—newest(Default),oldest,az,zapage— 1-basierte Seiteper_page— Default 48, Maximum 100
Antwort:
{
"rows": [ { "id": "...", "name": "...", "external_id": "...", "pool_id": "...",
"pool_name": "...", "ts_created": 1716480000, "org_duration": 312.45 }, … ],
"total": 127,
"page": 1,
"per_page": 48,
"total_pages": 3
}
5.2 video.homesection
Liefert je 12 Items für eine Startseiten-Sektion. Parameter:
type—foryou(Random),new(letzte 30 Tage),city(Suche),pool(Pool-Random)city— Stadtname (nur beitype=city)pool_name— Name des Pools (nur beitype=pool)limit— Default 12, Max 24
Jede Row enthält zusätzlich poster_url mit Cache-Buster
— bereit zum direkten Anzeigen.
6. Endpoints (Login)
Zugriff über Session-Cookie oder API-Key. Schreibende Endpoints prüfen zusätzlich Pool-Mitgliedschaft des Users.
6.1 Video-CRUD
POST /api/video.list.json·pool_id?— Videos des Users (optional auf einen Pool eingeschränkt)POST /api/video.update.json·id, name?, external_id?, description?— partielles Update; externe ID wird gegen Pool-Regex und Eindeutigkeit geprüftPOST /api/video.delete.json·id— Video + Dateien löschenPOST /api/video.assetstatus.json·id— Status der Assets (Poster-Slots, PBCore, Captions, Chapters)
6.2 Asset-Uploads
POST /api/video.posterupload.json·id + file(JPG, max 8 MiB)POST /api/video.posterchoose.json·id, slot=1|2|3POST /api/video.posterclear.json·idPOST /api/video.pbcoreupload.json·id + file(XML, max 4 MiB)POST /api/video.pbcoredelete.json·idPOST /api/video.captionupload.json·id, kind=captions|chapters, lang + file(VTT, max 2 MiB)POST /api/video.captiondelete.json·id, kind, langPOST /api/video.captioncontent.json·id, kind, lang— liefert{content, exists}POST /api/video.captionsave.json·id, kind, lang, content(WebVTT-Text)
6.3 Audit-Log
POST /api/video.auditlist.json·id— letzte 50 Änderungen an Titel/External-ID/Beschreibung
6.4 Profile (Self-Service)
POST /api/profile.updatename.json·namePOST /api/profile.changepassword.json·current_password, new_password(min. 8 Zeichen)POST /api/profile.generateapikey.json— liefert{apikey}einmalig; alter Key wird ungültigPOST /api/profile.revokeapikey.json— entfernt den API-Key
7. Chunked Upload
Große Dateien (bis 16 GiB) werden in 5-MiB-Chunks hochgeladen. Drei Endpoints decken den Lifecycle ab:
-
Start
POST /api/video.uploadstart.jsonFelder:
pool_id, filename, size, total_chunks, chunk_size. Antwort:{ upload_id, received_chunks: [..] }. Resume-fähig — bei identischemfilename+sizewird die bestehende Session weiterverwendet. -
Chunk hochladen
POST /api/video.uploadchunk.json
upload_id,indexund multipart-Feldfile. Antwort:{ received, total }. -
Status abfragen
POST /api/video.uploadstatus.json
upload_id→{ received_chunks, total_chunks }. -
Abschluss
POST /api/video.uploadfinish.json
upload_id. Server mergt die Chunks, berechnet MD5, prüft auf Duplikate (gleicher Hash → Fehler, keine DB-Zeile) und legt im Erfolgsfallvideos-Row +videofiles/<id>/org/<md5>.<ext>an. -
Abbrechen
POST /api/video.uploadcancel.json
upload_id→ entfernt Session und Chunks.
Nach erfolgreichem uploadfinish erzeugt der Cron-Bot
convertmiss die Poster (poster1/2/3.jpg) und das
HLS-Bündel (hls.zip) im Hintergrund.
8. PBCore-Editor-Felder
Der PBCore-Editor speichert ein normalisiertes
pbcoreDescriptionDocument mit Namespace
http://www.pbcore.org/PBCore/PBCoreNamespace.html. Der
Server-seitige Serializer schreibt nur Felder, die tatsächlich befüllt
sind. Strukturierter Zugriff:
POST /api/video.pbcorefields.json·id— parsed in JSON-StrukturPOST /api/video.pbcorefieldssave.json·id+fields(JSON-Body) — serialisiert & speichertPOST /api/video.pbcorevalidate.json·id—{exists, valid, error}
JSON-Struktur (Repeater sind Arrays von Objekten):
{
"identifiers": [ { "source": "FESAD", "value": "LFS001199" } ],
"titles": [ { "type": "Main", "value": "Stuttgarts Neckarhafen" } ],
"description": "Mehrzeiliger Text …",
"subjects": [ "Stuttgart", "Hafen", "Neckar" ],
"genre": "Imagefilm",
"creators": [ { "role": "Director", "name": "Müller" } ],
"contributors": [ { "role": "Kamera", "name": "Meier" } ],
"rights_summary": "© 2024 …",
"asset_dates": [ { "type": "created", "value": "1971-05-12" } ]
}
Resultierendes XML (Auszug):
<?xml version="1.0" encoding="UTF-8"?>
<pbcoreDescriptionDocument xmlns="http://www.pbcore.org/PBCore/PBCoreNamespace.html">
<pbcoreIdentifier source="FESAD">LFS001199</pbcoreIdentifier>
<pbcoreTitle titleType="Main">Stuttgarts Neckarhafen</pbcoreTitle>
<pbcoreDescription>Mehrzeiliger Text …</pbcoreDescription>
<pbcoreSubject>Stuttgart</pbcoreSubject>
<pbcoreGenre>Imagefilm</pbcoreGenre>
<pbcoreCreator>
<creator>Müller</creator>
<creatorRole>Director</creatorRole>
</pbcoreCreator>
<pbcoreRightsSummary>
<rightsSummary>© 2024 …</rightsSummary>
</pbcoreRightsSummary>
<pbcoreAssetDate dateType="created">1971-05-12</pbcoreAssetDate>
</pbcoreDescriptionDocument>
9. Admin: UserManager
Erfordert users.is_admin = 1. Alle Endpoints prüfen das
serverseitig und liefern 403 bei nicht-Admin.
POST /api/usermanager.list.json— alle User + Counts (Pools / Videos)POST /api/usermanager.getone.json·id— User + Pools + Pool-Stats + letzte 10 Audit-EinträgePOST /api/usermanager.create.json·name, email, password, is_admin?, is_enabled?, pool_ids[]?POST /api/usermanager.update.json·id, name?, email?, is_admin?, is_enabled?— eigener Account kann nicht deaktiviert werdenPOST /api/usermanager.setpassword.json·id, password(min. 8 Zeichen)POST /api/usermanager.setpools.json·id, pool_ids[]— ersetzt die Zuordnung komplettPOST /api/usermanager.poolsall.json— Liste aller Pools für Edit-UIs