Zum Hauptinhalt springen

Assets API

Überblick

Die Assets API ermöglicht den Zugriff auf Bilder und andere Medien-Dateien mit dynamischen Transformationsparametern. Sie bietet optimiertes Bild-Delivery mit Format-Konvertierung, Größenanpassung und verschiedenen Anpassungsoptionen.

Nur redaktionelle Medien

Diese API liefert Assets aus dem Kulturpool-CMS — Logos, Favicons, Hero-Bilder und redaktionelle Medien. Die Digitalisate der Sammlungsobjekte werden nicht hierüber ausgeliefert; deren URLs finden Sie in den Feldern previewImage und isShownBy der Suche API bzw. im EDM-Datensatz der Objekt API.

Basis-URL​

https://api.kulturpool.at/assets

Endpunkt​

Asset abrufen​

GET /assets/{asset_id}

Ruft ein Asset vom CMS ab und wendet die angegebenen Transformationsparameter an.

Parameter​

ParameterTypBeschreibungBeispiel
asset_idstringAsset-ID (UUID) aus dem CMS (Pfad-Parameter)8e1cf6a9-d1d6-4090-9a36-303f1f57a016
widthintegerBreite in Pixeln (> 0)1500
heightintegerHöhe in Pixeln (> 0)700
qualityintegerBildqualität (1–100)80
without_enlargementbooleanAutomatische Vergrößerung deaktivierentrue
formatstringAusgabeformatauto, avif, webp, jpg, png, tiff
fitstringAnpassungsmoduscover, contain, inside, outside
warnung
Parametername without_enlargement

Der Parameter heißt in der Kulturpool-API without_enlargement (mit Unterstrichen). Intern wird er auf den Directus-Parameter withoutEnlargement abgebildet — beim Aufruf der Kulturpool-API ist aber die Schreibweise mit Unterstrichen zu verwenden. Ältere Dokumentation nannte hier fälschlich withoutEnlargement.

Beispiele​

# Einfacher Asset-Abruf
curl "https://api.kulturpool.at/assets/8e1cf6a9-d1d6-4090-9a36-303f1f57a016"

# Optimiertes Thumbnail
curl "https://api.kulturpool.at/assets/8e1cf6a9-d1d6-4090-9a36-303f1f57a016?format=webp&width=300&height=200&fit=cover&quality=85"

# Hero-Image für moderne Browser
curl "https://api.kulturpool.at/assets/8e1cf6a9-d1d6-4090-9a36-303f1f57a016?format=avif&width=1920&height=800&fit=cover&without_enlargement=true"

Parameter im Detail​

Format-Optionen​

FormatBeschreibungVerwendung
autoAutomatische Format-Auswahl (Standard)Versucht WebP/AVIF für moderne Browser, sonst JPEG
webpWebP-FormatGute Kompression, breite Browser-Unterstützung
avifAVIF-FormatBeste Kompression, neuere Browser
jpgJPEG-FormatUniverselle Kompatibilität
pngPNG-FormatFür Transparenz und verlustfreie Kompression
tiffTIFF-FormatHochqualitative Archivierung

Anpassungsmodi (fit)​

ModusBeschreibungVerwendung
coverBild vollständig in Dimensionen einpassen (Standard)Hero-Images, Thumbnails
containBild mit Letterboxing in Dimensionen einpassenLogos, vollständige Bilder
insideSo groß wie möglich innerhalb der DimensionenResponsive Bilder
outsideSo klein wie möglich innerhalb/außerhalb der DimensionenSpezielle Layouts

Qualitätseinstellungen​

QualitätBeschreibungDateigrößeVerwendung
95-100Höchste QualitätSehr großDruckvorbereitung
85-95Hohe QualitätGroßHero-Images
75-85Gute QualitätMittelStandard-Bilder
60-75Moderate QualitätKleinThumbnails
40-60Niedrige QualitätSehr kleinVorschaubilder

Ohne Angabe von quality gilt der formatabhängige Standardwert des CMS.

Antwort​

Die API prüft die Existenz des Assets und gibt anschließend eine HTTP 302 Redirect auf das transformierte Asset im CMS zurück:

HTTP/1.1 302 Found
Location: https://edit.kulturpool.at/assets/8e1cf6a9-d1d6-4090-9a36-303f1f57a016/?format=webp&width=1500&height=700&fit=cover

HTTP-Clients folgen dem Redirect in der Regel automatisch. In <img>-Tags und CSS kann die API-URL daher direkt verwendet werden.

Beispiel-Code​

Responsive Bilder in HTML​

<picture>
<source
srcset="https://api.kulturpool.at/assets/8e1cf6a9-d1d6-4090-9a36-303f1f57a016?format=avif&width=1920&height=800&fit=cover"
type="image/avif"
media="(min-width: 1200px)">
<source
srcset="https://api.kulturpool.at/assets/8e1cf6a9-d1d6-4090-9a36-303f1f57a016?format=webp&width=1200&height=600&fit=cover"
type="image/webp"
media="(min-width: 768px)">
<source
srcset="https://api.kulturpool.at/assets/8e1cf6a9-d1d6-4090-9a36-303f1f57a016?format=webp&width=800&height=400&fit=cover"
type="image/webp">
<img
src="https://api.kulturpool.at/assets/8e1cf6a9-d1d6-4090-9a36-303f1f57a016?format=jpg&width=800&height=400&fit=cover"
alt="Beschreibung"
loading="lazy">
</picture>

JavaScript Asset-URL Generator​

class AssetUrlBuilder {
constructor(baseUrl = 'https://api.kulturpool.at/assets') {
this.baseUrl = baseUrl;
}

build(assetId, options = {}) {
const {
width,
height,
quality = 85,
format = 'auto',
fit = 'cover',
withoutEnlargement = false,
} = options;

const params = new URLSearchParams();

if (width) params.set('width', width);
if (height) params.set('height', height);
if (quality !== 85) params.set('quality', quality);
if (format !== 'auto') params.set('format', format);
if (fit !== 'cover') params.set('fit', fit);
// Achtung: der API-Parameter heißt without_enlargement
if (withoutEnlargement) params.set('without_enlargement', 'true');

const queryString = params.toString();
return `${this.baseUrl}/${assetId}${queryString ? `?${queryString}` : ''}`;
}

// Vordefinierte Größen
thumbnail(assetId, size = 150) {
return this.build(assetId, {
width: size,
height: size,
format: 'webp',
quality: 80,
});
}

hero(assetId, width = 1920, height = 800) {
return this.build(assetId, {
width,
height,
format: 'webp',
quality: 85,
fit: 'cover',
});
}

logo(assetId, maxWidth = 200) {
return this.build(assetId, {
width: maxWidth,
format: 'png',
fit: 'contain',
withoutEnlargement: true,
});
}
}

// Verwendung
const assetBuilder = new AssetUrlBuilder();
const thumbnailUrl = assetBuilder.thumbnail('8e1cf6a9-d1d6-4090-9a36-303f1f57a016');
const heroUrl = assetBuilder.hero('8e1cf6a9-d1d6-4090-9a36-303f1f57a016', 1500, 600);

Python Asset-Manager​

from urllib.parse import urlencode


class AssetManager:
def __init__(self, base_url="https://api.kulturpool.at/assets"):
self.base_url = base_url

def build_url(self, asset_id, **kwargs):
"""Asset-URL mit Parametern erstellen"""
params = {k: v for k, v in kwargs.items() if v is not None}

# Boolean-Werte in Strings konvertieren
for key, value in params.items():
if isinstance(value, bool):
params[key] = str(value).lower()

url = f"{self.base_url}/{asset_id}"
if params:
url += f"?{urlencode(params)}"

return url

def get_responsive_urls(self, asset_id, format_type="webp"):
"""Responsive Bild-URLs für verschiedene Bildschirmgrößen"""
return {
"mobile": self.build_url(asset_id, width=480, height=320, format=format_type, fit="cover"),
"tablet": self.build_url(asset_id, width=768, height=512, format=format_type, fit="cover"),
"desktop": self.build_url(asset_id, width=1200, height=800, format=format_type, fit="cover"),
"large": self.build_url(asset_id, width=1920, height=1080, format=format_type, fit="cover"),
}

def get_optimized_thumbnail(self, asset_id, size=300):
"""Optimiertes Thumbnail"""
return self.build_url(
asset_id,
width=size,
height=size,
format="webp",
quality=80,
fit="cover",
)


# Verwendung
asset_manager = AssetManager()
asset_id = "8e1cf6a9-d1d6-4090-9a36-303f1f57a016"

# Responsive URLs
responsive_urls = asset_manager.get_responsive_urls(asset_id)
print(f"Mobile: {responsive_urls['mobile']}")

# Thumbnail
thumbnail_url = asset_manager.get_optimized_thumbnail(asset_id, 150)
print(f"Thumbnail: {thumbnail_url}")

Anwendungsfälle​

Integration mit der Institutionen API​

// Asset-URLs für Institution Hero-Images generieren
async function getInstitutionWithOptimizedImages(institutionId) {
const response = await fetch(
`https://api.kulturpool.at/institutions/${institutionId}`,
);
const {data: institution} = await response.json();

const assetBuilder = new AssetUrlBuilder();

return {
...institution,
optimizedImages: {
favicon: institution.favicon?.id
? assetBuilder.build(institution.favicon.id, {
width: 32,
height: 32,
format: 'png',
})
: null,
logo: institution.logo?.id
? assetBuilder.logo(institution.logo.id, 200)
: null,
hero: institution.hero_image?.id
? assetBuilder.hero(institution.hero_image.id)
: null,
heroMobile: institution.hero_image?.id
? assetBuilder.build(institution.hero_image.id, {
width: 768,
height: 400,
format: 'webp',
fit: 'cover',
})
: null,
},
};
}

Bildergalerie mit Lazy Loading​

class ImageGallery {
constructor(containerId) {
this.container = document.getElementById(containerId);
this.assetBuilder = new AssetUrlBuilder();
this.observer = new IntersectionObserver(this.handleIntersection.bind(this));
}

addImage(assetId, alt = '') {
const imageContainer = document.createElement('div');
imageContainer.className = 'gallery-item';
imageContainer.dataset.assetId = assetId;
imageContainer.dataset.alt = alt;

// Placeholder
const placeholder = document.createElement('div');
placeholder.className = 'image-placeholder';
placeholder.style.backgroundColor = '#f0f0f0';
placeholder.style.aspectRatio = '16/9';

imageContainer.appendChild(placeholder);
this.container.appendChild(imageContainer);

// Für Lazy Loading beobachten
this.observer.observe(imageContainer);
}

handleIntersection(entries) {
entries.forEach((entry) => {
if (entry.isIntersecting) {
this.loadImage(entry.target);
this.observer.unobserve(entry.target);
}
});
}

loadImage(container) {
const assetId = container.dataset.assetId;
const alt = container.dataset.alt;

const picture = document.createElement('picture');

// WebP für moderne Browser
const sourceWebP = document.createElement('source');
sourceWebP.srcset = this.assetBuilder.build(assetId, {
width: 800,
height: 450,
format: 'webp',
fit: 'cover',
});
sourceWebP.type = 'image/webp';

// JPEG Fallback
const img = document.createElement('img');
img.src = this.assetBuilder.build(assetId, {
width: 800,
height: 450,
format: 'jpg',
fit: 'cover',
});
img.alt = alt;
img.loading = 'lazy';

picture.appendChild(sourceWebP);
picture.appendChild(img);

container.innerHTML = '';
container.appendChild(picture);
}
}

SEO-optimierte Asset-Integration​

function generateSEOOptimizedAssetUrls(assetId) {
const assetBuilder = new AssetUrlBuilder();

return {
// Open Graph Image
ogImage: assetBuilder.build(assetId, {
width: 1200,
height: 630,
format: 'jpg',
fit: 'cover',
quality: 85,
}),

// Twitter Card Image
twitterImage: assetBuilder.build(assetId, {
width: 1024,
height: 512,
format: 'jpg',
fit: 'cover',
quality: 85,
}),

// JSON-LD Structured Data Image
structuredDataImage: assetBuilder.build(assetId, {
width: 1200,
height: 800,
format: 'jpg',
fit: 'cover',
quality: 90,
}),

// Apple Touch Icon
appleTouchIcon: assetBuilder.build(assetId, {
width: 180,
height: 180,
format: 'png',
fit: 'cover',
}),
};
}

Performance-Optimierung​

Format automatisch wählen​

In den meisten Fällen genügt format=auto — das CMS liefert dann WebP bzw. AVIF an Browser, die diese Formate unterstützen, und sonst JPEG. Eine eigene Feature Detection ist dafür nicht nötig.

Alternativ lässt sich die Auswahl deklarativ im Markup über <picture> und type-Attribute treffen (siehe Responsive Bilder in HTML).

Batch-Preloading für kritische Bilder​

async function preloadAssetBatch(assetIds, options = {}) {
const assetBuilder = new AssetUrlBuilder();

const promises = assetIds.map((assetId) => {
const link = document.createElement('link');
link.rel = 'preload';
link.as = 'image';
link.href = assetBuilder.build(assetId, options);

document.head.appendChild(link);

return new Promise((resolve) => {
link.onload = resolve;
link.onerror = resolve; // Auch bei Fehlern fortfahren
});
});

await Promise.allSettled(promises);
}

// Verwendung für kritische Bilder
await preloadAssetBatch(['asset-1', 'asset-2', 'asset-3'], {
width: 1200,
height: 600,
format: 'webp',
});

Fehlerbehandlung​

404 — Asset nicht gefunden​

{
"detail": "Asset mit ID 'invalid-id' wurde nicht gefunden"
}

500 — CMS-Fehler​

{
"detail": "Fehler beim Abrufen des Assets vom CMS"
}

Bei Netzwerkproblemen zwischen Proxy und CMS:

{
"detail": "Netzwerkfehler beim Zugriff auf das CMS"
}

Fehlerbehandlung im Frontend​

function createAssetWithFallback(assetId, options = {}, fallbackSrc = '/placeholder.jpg') {
const assetBuilder = new AssetUrlBuilder();
const img = new Image();

img.onerror = () => {
img.src = fallbackSrc;
};

img.src = assetBuilder.build(assetId, options);
return img;
}