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();
let mergedProfiles;
let mergedSceneActors;
let existingSceneActors;
let mergedProfiles = [];
let mergedSceneActors = [];
let existingSceneActors = [];
let duplicateSourceActors = [];
let mergedActorStashes = [];
try {
const [existingProfiles] = await Promise.all([
@@ -614,36 +616,83 @@ export async function mergeActors(targetActorId, sourceActorIds, reqUser) {
trx('actors_avatars')
.update('actor_id', targetActorId)
.whereIn('actor_id', sourceActorIds),
trx('stashes_actors')
.update('actor_id', targetActorId)
.whereIn('actor_id', sourceActorIds)
.returning('id'),
]);
existingSceneActors = await trx('releases_actors')
.where('actor_id', targetActorId);
// assign source actor profiles to target actor, unless a profile for that entity is already present
mergedProfiles = await trx('actors_profiles')
.update('actor_id', targetActorId)
.whereIn('actor_id', sourceActorIds)
.whereNotIn('entity_id', existingProfiles.map((profile) => profile.entity_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')
.update({
actor_id: targetActorId,
alias_id: knex.raw('actor_id'),
})
.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');
// delete aliased actors for scenes that already had both source/aliased and target actor assigned
const releases = await trx('releases_actors')
.whereIn('release_id', existingSceneActors.map((sceneActor) => sceneActor.release_id))
const [targetActorStashes, sourceActorStashes] = await Promise.all([
trx('stashes_actors')
.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)
.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();
} catch (error) {
await trx.rollback();
@@ -660,7 +709,11 @@ export async function mergeActors(targetActorId, sourceActorIds, reqUser) {
}, { refreshView: false });
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]),
syncStashes('actor', [targetActorId, ...sourceActorIds]),
]);
@@ -668,6 +721,7 @@ export async function mergeActors(targetActorId, sourceActorIds, reqUser) {
return {
scenes: mergedSceneActors.length,
profiles: mergedProfiles.length,
stashes: mergedActorStashes.length,
};
}