Fixed actor merge failing if scene has multiple source actors assigned, merging stashed actors.

This commit is contained in:
2026-06-28 05:35:51 +02:00
parent 4125811017
commit adf9e2334c

View File

@@ -598,9 +598,11 @@ export async function mergeActors(targetActorId, sourceActorIds, reqUser) {
const trx = await knex.transaction(); const trx = await knex.transaction();
let mergedProfiles; let mergedProfiles = [];
let mergedSceneActors; let mergedSceneActors = [];
let existingSceneActors; let existingSceneActors = [];
let duplicateSourceActors = [];
let mergedActorStashes = [];
try { try {
const [existingProfiles] = await Promise.all([ const [existingProfiles] = await Promise.all([
@@ -614,36 +616,83 @@ export async function mergeActors(targetActorId, sourceActorIds, reqUser) {
trx('actors_avatars') trx('actors_avatars')
.update('actor_id', targetActorId) .update('actor_id', targetActorId)
.whereIn('actor_id', sourceActorIds), .whereIn('actor_id', sourceActorIds),
trx('stashes_actors')
.update('actor_id', targetActorId)
.whereIn('actor_id', sourceActorIds)
.returning('id'),
]); ]);
existingSceneActors = await trx('releases_actors') // assign source actor profiles to target actor, unless a profile for that entity is already present
.where('actor_id', targetActorId);
mergedProfiles = await trx('actors_profiles') mergedProfiles = await trx('actors_profiles')
.update('actor_id', targetActorId) .update('actor_id', targetActorId)
.whereIn('actor_id', sourceActorIds) .whereIn('actor_id', sourceActorIds)
.whereNotIn('entity_id', existingProfiles.map((profile) => profile.entity_id)) .whereNotIn('entity_id', existingProfiles.map((profile) => profile.entity_id))
.returning('id'); .returning('id');
// find releases that have more than one source actor assigned
duplicateSourceActors = await trx('releases_actors')
.select('release_id', knex.raw('array_agg(actor_id) as actor_ids'))
.whereIn('actor_id', sourceActorIds)
.groupBy('release_id')
.having(trx.raw('COUNT(DISTINCT actor_id) > 1'));
if (duplicateSourceActors.length > 0) {
// some scenes have multiple source actors assigned, which will cause a conflict after merging; we will need to remove all but one
await trx('releases_actors')
.whereIn('release_id', duplicateSourceActors.map((sceneActor) => sceneActor.release_id))
.whereIn('actor_id', duplicateSourceActors.flatMap((sceneActor) => sceneActor.actor_ids.slice(1)))
.delete();
}
// find scenes that already have target actor assigned
existingSceneActors = await trx('releases_actors')
.where('actor_id', targetActorId);
// delete release source actors for scenes that already have the target actor assigned
await trx('releases_actors')
.whereIn('release_id', existingSceneActors.map((sceneActor) => sceneActor.release_id))
.whereIn('actor_id', sourceActorIds)
.delete();
// alias release source actors to target actors
mergedSceneActors = await trx('releases_actors') mergedSceneActors = await trx('releases_actors')
.update({ .update({
actor_id: targetActorId, actor_id: targetActorId,
alias_id: knex.raw('actor_id'), alias_id: knex.raw('actor_id'),
}) })
.whereIn('actor_id', sourceActorIds) .whereIn('actor_id', sourceActorIds)
.whereNotIn('release_id', existingSceneActors.map((sceneActor) => sceneActor.release_id)) // can't update entry if target actor already exists alongside source actor
.returning('release_id'); .returning('release_id');
// delete aliased actors for scenes that already had both source/aliased and target actor assigned const [targetActorStashes, sourceActorStashes] = await Promise.all([
const releases = await trx('releases_actors') trx('stashes_actors')
.whereIn('release_id', existingSceneActors.map((sceneActor) => sceneActor.release_id)) .where('actor_id', targetActorId),
trx('stashes_actors')
.whereIn('actor_id', sourceActorIds),
]);
// remove source actors from stashes that already contain target actor
await trx('stashes_actors')
.whereIn('stash_id', targetActorStashes.map((stash) => stash.stash_id))
.whereIn('actor_id', sourceActorIds) .whereIn('actor_id', sourceActorIds)
.delete(); .delete();
// find stashes that have more than one source actor assigned
const duplicateStashActors = await trx('stashes_actors')
.select('stash_id', knex.raw('array_agg(actor_id order by created_at) as actor_ids'))
.whereIn('actor_id', sourceActorStashes.map((actorStash) => actorStash.actor_id))
.groupBy('stash_id')
.having(trx.raw('COUNT(DISTINCT actor_id) > 1'));
if (duplicateStashActors.length > 0) {
// some stashes have multiple source actors assigned, which will cause a conflict after merging; we will need to remove all but one
await trx('stashes_actors')
.whereIn('stash_id', duplicateStashActors.map((actorStash) => actorStash.stash_id))
.whereIn('actor_id', duplicateStashActors.flatMap((actorStash) => actorStash.actor_ids.slice(1)))
.delete();
}
// we update an existing entry instead of creating a new one, so the original stash date is preserved
mergedActorStashes = await trx('stashes_actors')
.update('actor_id', targetActorId)
.whereIn('actor_id', sourceActorIds)
.returning('stash_id');
await trx.commit(); await trx.commit();
} catch (error) { } catch (error) {
await trx.rollback(); await trx.rollback();
@@ -660,7 +709,11 @@ export async function mergeActors(targetActorId, sourceActorIds, reqUser) {
}, { refreshView: false }); }, { refreshView: false });
await Promise.all([ await Promise.all([
syncScenes([...mergedSceneActors.map((sceneActor) => sceneActor.release_id), ...existingSceneActors.map((sceneActor) => sceneActor.release_id)]), syncScenes([
...mergedSceneActors.map((sceneActor) => sceneActor.release_id),
...existingSceneActors.map((sceneActor) => sceneActor.release_id),
...duplicateSourceActors.map((sceneActor) => sceneActor.release_id),
]),
syncActors([targetActorId, ...sourceActorIds]), syncActors([targetActorId, ...sourceActorIds]),
syncStashes('actor', [targetActorId, ...sourceActorIds]), syncStashes('actor', [targetActorId, ...sourceActorIds]),
]); ]);
@@ -668,6 +721,7 @@ export async function mergeActors(targetActorId, sourceActorIds, reqUser) {
return { return {
scenes: mergedSceneActors.length, scenes: mergedSceneActors.length,
profiles: mergedProfiles.length, profiles: mergedProfiles.length,
stashes: mergedActorStashes.length,
}; };
} }