Release v2.2.0

This commit is contained in:
2026-08-01 21:41:44 +01:00
parent c643d2fb07
commit d6417b667c
673 changed files with 146752 additions and 7389 deletions
@@ -0,0 +1,42 @@
const { refreshApiSource, refreshRssFeed } = require('../../data-source-refresh');
const TASK = {
taskType: 'data-source-refresh'
};
function registerDataSourceRefreshTask(options) {
const backgroundTaskQueue = options && options.backgroundTaskQueue;
const pool = options && options.pool;
const common = options && options.common;
if (!backgroundTaskQueue || !pool || !common) {
throw new Error('registerDataSourceRefreshTask requires the data source refresh dependencies.');
}
backgroundTaskQueue.setTaskHandler(TASK.taskType, async function (task) {
const payload = task && task.payload ? task.payload : {};
const sourceType = String(payload.sourceType || '').trim();
const sourceId = Number(payload.sourceId || 0);
// Resolve the source record at execution time so stale queued tasks fail cleanly.
if (sourceType === 'api-source') {
const apiSource = await common.fetchApiSourceById(pool, sourceId);
if (!apiSource) {
throw new Error('API source not found.');
}
return refreshApiSource(pool, common, apiSource, Number(payload.actorId) || null);
}
if (sourceType === 'rss-feed') {
const rssFeed = await common.fetchRssFeedById(pool, sourceId);
if (!rssFeed) {
throw new Error('RSS feed not found.');
}
return refreshRssFeed(pool, common, rssFeed.id, rssFeed.feed_url, rssFeed.item_limit, Number(payload.actorId) || null);
}
throw new Error('Unsupported data source refresh task.');
});
}
module.exports = { registerDataSourceRefreshTask };
@@ -0,0 +1,39 @@
function registerFontSyncTask(options) {
const backgroundTaskQueue = options && options.backgroundTaskQueue;
const uploadSyncService = options && options.uploadSyncService;
if (!backgroundTaskQueue || !uploadSyncService) {
throw new Error('registerFontSyncTask requires the font sync dependencies.');
}
backgroundTaskQueue.setTaskHandler('font-sync', async function (task) {
const payload = task && task.payload ? task.payload : task || {};
const uploadDir = String(payload.uploadDir || '').trim();
const operations = Array.isArray(payload.operations)
? payload.operations
: Array.isArray(payload.uploadPaths)
? payload.uploadPaths.map(function (uploadPath) {
return { type: String(payload.action || 'put').trim().toLowerCase(), uploadPath: uploadPath };
})
: [];
if (!uploadDir || !operations.length) {
return;
}
for (let i = 0; i < operations.length; i += 1) {
const operation = operations[i] || {};
const uploadPath = String(operation.uploadPath || '').trim();
if (!uploadPath) {
continue;
}
if (String(operation.type || '').trim().toLowerCase() === 'delete') {
await uploadSyncService.removeUploadFileFromPlayer(uploadPath, uploadDir);
} else {
await uploadSyncService.pushUploadFileToPlayer(uploadPath, uploadDir);
}
}
});
}
module.exports = { registerFontSyncTask };
@@ -0,0 +1,20 @@
const TASK = {
taskType: 'media-sync'
};
function registerMediaSyncTask(options) {
const backgroundTaskQueue = options && options.backgroundTaskQueue;
const uploadSyncService = options && options.uploadSyncService;
const runMediaSyncTask = uploadSyncService && uploadSyncService.runMediaSyncTask;
if (!backgroundTaskQueue || typeof runMediaSyncTask !== 'function') {
throw new Error('registerMediaSyncTask requires the media sync dependencies.');
}
// Media sync is just a pass-through to the shared media sync runner.
backgroundTaskQueue.setTaskHandler(TASK.taskType, function (task) {
return runMediaSyncTask(task && task.payload ? task.payload : task);
});
}
module.exports = { registerMediaSyncTask };
@@ -0,0 +1,51 @@
const TASK = {
taskType: 'slide-thumbnail-refresh'
};
async function fetchPlayerInternalBaseUrl(pool) {
const [rows] = await pool.query(
`SELECT internal_base_url
FROM d_players
WHERE device_id = '1'
LIMIT 1`
);
return String(rows && rows[0] && rows[0].internal_base_url || '').trim().replace(/\/$/, '') || null;
}
function registerSlideThumbnailRefreshTask(options) {
const backgroundTaskQueue = options && options.backgroundTaskQueue;
const captureSlideThumbnail = options && options.captureSlideThumbnail;
const pool = options && options.pool;
const common = options && options.common;
const mediaDir = String(options && options.mediaDir || '').trim();
if (!backgroundTaskQueue || typeof captureSlideThumbnail !== 'function' || !pool || !common || !mediaDir) {
throw new Error('registerSlideThumbnailRefreshTask requires the slide thumbnail dependencies.');
}
// Refresh the slide thumbnail for one slide id.
backgroundTaskQueue.setTaskHandler(TASK.taskType, async function (task) {
const payload = task && task.payload ? task.payload : {};
const slideId = Number(payload.slideId || 0);
if (!Number.isFinite(slideId) || slideId <= 0) {
throw new Error('Slide id is required.');
}
const playerInternalBaseUrl = await fetchPlayerInternalBaseUrl(pool);
if (!playerInternalBaseUrl) {
throw new Error('Player internal base URL is required.');
}
return captureSlideThumbnail({
pool: pool,
common: common,
mediaDir: mediaDir,
baseUrl: playerInternalBaseUrl,
slideId: slideId,
previousThumbnailPath: payload.previousThumbnailPath || null
});
});
}
module.exports = { registerSlideThumbnailRefreshTask };
@@ -0,0 +1,65 @@
const TASK = {
taskType: 'template-slide-thumbnail-refresh'
};
async function fetchPlayerInternalBaseUrl(pool) {
const [rows] = await pool.query(
`SELECT internal_base_url
FROM d_players
WHERE device_id = '1'
LIMIT 1`
);
return String(rows && rows[0] && rows[0].internal_base_url || '').trim().replace(/\/$/, '') || null;
}
function registerTemplateSlideThumbnailRefreshTask(options) {
const backgroundTaskQueue = options && options.backgroundTaskQueue;
const captureSlideThumbnail = options && options.captureSlideThumbnail;
const pool = options && options.pool;
const common = options && options.common;
const mediaDir = String(options && options.mediaDir || '').trim();
if (!backgroundTaskQueue || typeof captureSlideThumbnail !== 'function' || !pool || !common || !mediaDir) {
throw new Error('registerTemplateSlideThumbnailRefreshTask requires the template thumbnail dependencies.');
}
// Walk every slide in the template and regenerate its thumbnail.
backgroundTaskQueue.setTaskHandler(TASK.taskType, async function (task) {
const payload = task && task.payload ? task.payload : {};
const templateId = Number(payload.templateId || 0);
if (!Number.isFinite(templateId) || templateId <= 0) {
throw new Error('Template id is required.');
}
const playerInternalBaseUrl = await fetchPlayerInternalBaseUrl(pool);
if (!playerInternalBaseUrl) {
throw new Error('Player internal base URL is required.');
}
const [slides] = await pool.query(
'SELECT id, thumbnail_path FROM c_slides WHERE template_id = ? ORDER BY id ASC',
[templateId]
);
for (const slide of slides || []) {
const slideId = Number(slide && slide.id || 0);
if (!Number.isFinite(slideId) || slideId <= 0) {
continue;
}
await captureSlideThumbnail({
pool: pool,
common: common,
mediaDir: mediaDir,
baseUrl: playerInternalBaseUrl,
slideId: slideId,
previousThumbnailPath: slide && slide.thumbnail_path ? slide.thumbnail_path : null
});
}
return { templateId: templateId, slideCount: Array.isArray(slides) ? slides.length : 0 };
});
}
module.exports = { registerTemplateSlideThumbnailRefreshTask };