0.12.0-1 Plugins und diverses

This commit is contained in:
2026-03-19 18:57:05 +00:00
parent 0a9cf6969f
commit 24955a956d
33 changed files with 3978 additions and 479 deletions
+63 -8
View File
@@ -33,6 +33,31 @@ function isLocalUrl(url) {
return typeof url === 'string' && url.startsWith('/api/thumbnails/');
}
function resolveLocalThumbnailPath(url) {
const raw = String(url || '').trim();
const match = raw.match(/^\/api\/thumbnails\/job-(\d+)\.jpg$/i);
if (!match) {
return null;
}
const jobId = Number(match[1]);
if (!Number.isFinite(jobId) || jobId <= 0) {
return null;
}
return persistentFilePath(Math.trunc(jobId));
}
function localThumbnailUrlExists(url) {
const targetPath = resolveLocalThumbnailPath(url);
if (!targetPath) {
return false;
}
try {
return fs.existsSync(targetPath);
} catch (_error) {
return false;
}
}
function downloadImage(url, destPath, redirectsLeft = MAX_REDIRECTS) {
return new Promise((resolve, reject) => {
if (redirectsLeft <= 0) {
@@ -82,21 +107,48 @@ function downloadImage(url, destPath, redirectsLeft = MAX_REDIRECTS) {
* Wird aufgerufen sobald poster_url bekannt ist (vor Rip-Start).
* @returns {Promise<string|null>} lokaler Pfad oder null
*/
async function cacheJobThumbnail(jobId, posterUrl) {
if (!posterUrl || isLocalUrl(posterUrl)) return null;
async function cacheJobThumbnailDetailed(jobId, posterUrl) {
const normalizedPosterUrl = String(posterUrl || '').trim();
if (!normalizedPosterUrl || isLocalUrl(normalizedPosterUrl)) {
return {
ok: false,
jobId,
posterUrl: normalizedPosterUrl || null,
cachedPath: null,
error: 'Kein externer Poster-Link vorhanden.'
};
}
try {
ensureDirs();
const dest = cacheFilePath(jobId);
await downloadImage(posterUrl, dest);
logger.info('thumbnail:cached', { jobId, posterUrl, dest });
return dest;
await downloadImage(normalizedPosterUrl, dest);
logger.info('thumbnail:cached', { jobId, posterUrl: normalizedPosterUrl, dest });
return {
ok: true,
jobId,
posterUrl: normalizedPosterUrl,
cachedPath: dest,
error: null
};
} catch (err) {
logger.warn('thumbnail:cache:failed', { jobId, posterUrl, error: err.message });
return null;
const message = String(err?.message || err || 'Bild-Download fehlgeschlagen');
logger.warn('thumbnail:cache:failed', { jobId, posterUrl: normalizedPosterUrl, error: message });
return {
ok: false,
jobId,
posterUrl: normalizedPosterUrl,
cachedPath: null,
error: message
};
}
}
async function cacheJobThumbnail(jobId, posterUrl) {
const result = await cacheJobThumbnailDetailed(jobId, posterUrl);
return result?.ok ? result.cachedPath : null;
}
/**
* Verschiebt das gecachte Bild in den persistenten Ordner.
* Gibt die lokale API-URL zurück, oder null wenn kein Bild vorhanden.
@@ -251,11 +303,14 @@ async function migrateExistingThumbnails() {
module.exports = {
cacheJobThumbnail,
cacheJobThumbnailDetailed,
promoteJobThumbnail,
copyThumbnail,
storeLocalThumbnail,
deleteThumbnail,
getThumbnailsDir,
migrateExistingThumbnails,
isLocalUrl
isLocalUrl,
resolveLocalThumbnailPath,
localThumbnailUrlExists
};