Added experimental edit page and revision history.

This commit is contained in:
2024-09-10 02:47:03 +02:00
parent 4b8dfba289
commit 8bf9e22b39
20 changed files with 1177 additions and 14 deletions

View File

@@ -11,6 +11,9 @@ import { curateStash } from './stashes.js';
import { curateMedia } from './media.js';
import escape from '../utils/escape-manticore.js';
import promiseProps from '../utils/promise-props.js';
import initLogger from './logger.js';
const logger = initLogger();
function getWatchUrl(scene) {
if (scene.url) {
@@ -64,6 +67,7 @@ function curateScene(rawScene, assets) {
description: rawScene.description,
duration: rawScene.duration,
shootId: rawScene.shoot_id,
productionDate: rawScene.production_date,
channel: {
id: assets.channel.id,
slug: assets.channel.slug,
@@ -595,3 +599,180 @@ export async function fetchScenes(filters, rawOptions, reqUser) {
limit: options.limit,
};
}
async function applySceneValueDelta(sceneId, delta, trx) {
console.log('value delta', delta);
return knexOwner('releases')
.where('id', sceneId)
.update(delta.key, delta.value)
.transacting(trx);
}
async function applySceneActorsDelta(sceneId, delta, trx) {
console.log('actors delta', delta);
await knexOwner('releases_actors')
.where('release_id', sceneId)
.delete()
.transacting(trx);
await knexOwner('releases_actors')
.insert(delta.value.map((actorId) => ({
release_id: sceneId,
actor_id: actorId,
})))
.transacting(trx);
}
async function applySceneTagsDelta(sceneId, delta, trx) {
console.log('tags delta', delta);
await knexOwner('releases_tags')
.where('release_id', sceneId)
.whereNotNull('tag_id')
.delete()
.transacting(trx);
await knexOwner('releases_tags')
.insert(delta.value.map((tagId) => ({
release_id: sceneId,
tag_id: tagId,
source: 'editor',
})))
.transacting(trx);
}
async function applySceneRevision(sceneIds) {
const revisions = await knexOwner('scenes_revisions')
.whereIn('scene_id', sceneIds)
.whereNull('applied_at');
await revisions.reduce(async (chain, revision) => {
await chain;
console.log('revision', revision);
await knexOwner.transaction(async (trx) => {
await revision.deltas.map(async (delta) => {
if ([
'title',
'description',
'date',
'duration',
'production_date',
'production_location',
'production_city',
'production_state',
].includes(delta.key)) {
return applySceneValueDelta(revision.scene_id, delta, trx);
}
if (delta.key === 'actors') {
return applySceneActorsDelta(revision.scene_id, delta, trx);
}
if (delta.key === 'tags') {
return applySceneTagsDelta(revision.scene_id, delta, trx);
}
return null;
});
await knexOwner('scenes_revisions')
.where('id', revision.id)
.update('applied_at', knex.fn.now());
// await trx.commit();
}).catch(async (error) => {
logger.error(`Failed to apply revision ${revision.id} on scene ${revision.scene_id}: ${error.message}`);
});
}, Promise.resolve());
}
const keyMap = {
productionDate: 'production_date',
};
export async function createSceneRevision(sceneId, { edits, comment }, reqUser) {
const [
[scene],
openRevisions,
] = await Promise.all([
fetchScenesById([sceneId], { reqUser, includeAssets: true }),
knexOwner('scenes_revisions')
.where('user_id', reqUser.id)
.whereNull('approved_by')
.whereNot('failed', true),
]);
if (!scene) {
throw new HttpError(`No scene with ID ${sceneId} found to update`, 404);
}
if (openRevisions.length >= config.revisions.unapprovedLimit) {
throw new HttpError(`You have ${config.revisions.unapprovedLimit} unapproved revisions, please wait for approval before submitting another revision.`, 429);
}
const baseScene = Object.fromEntries(Object.entries(scene).map(([key, values]) => {
if ([
'effectiveDate',
'isNew',
'network',
'stashes',
'watchUrl',
].includes(key)) {
return null;
}
if (values?.hash) {
return [key, values.hash];
}
if (values?.id) {
return [key, values.id];
}
if (Array.isArray(values)) {
return [key, values.map((value) => value?.hash || value?.id || value)];
}
return [key, values];
}).filter(Boolean));
const deltas = Object.entries(edits).map(([key, value]) => {
if (baseScene[key] === value) {
return null;
}
if (Array.isArray(value)) {
const valueSet = new Set(value);
const baseSet = new Set(baseScene[key]);
if (valueSet.size === baseSet.size && baseScene[key].every((id) => valueSet.has(id))) {
return null;
}
}
return {
key: keyMap[key] || key,
value,
};
}).filter(Boolean);
if (deltas.length === 0) {
throw new HttpError('No effective changes provided', 400);
}
await knexOwner('scenes_revisions').insert({
user_id: reqUser.id,
scene_id: scene.id,
base: JSON.stringify(baseScene),
deltas: JSON.stringify(deltas),
comment,
});
if (['admin', 'editor'].includes(reqUser.role)) {
await applySceneRevision([scene.id]);
}
}