Added profile flush.

This commit is contained in:
DebaucheryLibrarian 2020-12-30 02:23:43 +01:00
parent cdc963c42c
commit af67d733ad
11 changed files with 123 additions and 23 deletions

View File

@ -1,10 +1,15 @@
<template> <template>
<div class="children"> <div class="children">
<EntityTile <div
v-for="child in entity.children" v-for="child in entity.children"
:key="`child-${child.id}`" :key="`child-${child.id}`"
:entity="child" class="tile-container"
/> >
<EntityTile
:entity="child"
@load="(event) => $emit('load', event)"
/>
</div>
</div> </div>
</template> </template>
@ -15,6 +20,7 @@ export default {
components: { components: {
EntityTile, EntityTile,
}, },
emits: ['load'],
props: { props: {
entity: { entity: {
type: Object, type: Object,
@ -33,13 +39,14 @@ export default {
padding: 1rem; padding: 1rem;
margin: 0 1rem 0 0; margin: 0 1rem 0 0;
border-bottom: solid 1px var(--darken-hint); border-bottom: solid 1px var(--darken-hint);
overflow-x: auto;
scroll-behavior: smooth; .tile-container {
scrollbar-width: none; display: inline-block;
padding: 0 1rem 0 0;
}
.tile { .tile {
width: 15rem; width: 15rem;
margin: 0 1rem 0 0;
} }
&.expanded { &.expanded {
@ -52,10 +59,6 @@ export default {
width: 100%; width: 100%;
} }
} }
&::-webkit-scrollbar {
display: none;
}
} }
@media(max-width: $breakpoint0) { @media(max-width: $breakpoint0) {

View File

@ -81,6 +81,7 @@
> >
<Scroll <Scroll
v-if="entity.children.length > 0" v-if="entity.children.length > 0"
v-slot="scroll"
:expanded="expanded" :expanded="expanded"
class="scroll-dark" class="scroll-dark"
@expand="(state) => expanded = state" @expand="(state) => expanded = state"
@ -88,6 +89,7 @@
<Children <Children
:entity="entity" :entity="entity"
:class="{ expanded }" :class="{ expanded }"
@load="scroll.loaded"
/> />
</Scroll> </Scroll>
@ -234,6 +236,10 @@ export default {
flex-direction: column; flex-direction: column;
} }
.scroll {
background: var(--profile);
}
.releases { .releases {
flex-grow: 1; flex-grow: 1;
} }

View File

@ -9,21 +9,27 @@
v-if="entity.type === 'network' || entity.independent" v-if="entity.type === 'network' || entity.independent"
:src="`/img/logos/${entity.slug}/thumbs/network.png`" :src="`/img/logos/${entity.slug}/thumbs/network.png`"
:alt="entity.name" :alt="entity.name"
loading="lazy"
class="logo" class="logo"
@load="$emit('load', $event)"
> >
<img <img
v-else-if="entity.parent" v-else-if="entity.parent"
:src="`/img/logos/${entity.parent.slug}/thumbs/${entity.slug}.png`" :src="`/img/logos/${entity.parent.slug}/thumbs/${entity.slug}.png`"
:alt="entity.name" :alt="entity.name"
loading="lazy"
class="logo" class="logo"
@load="$emit('load', $event)"
> >
<img <img
v-else v-else
:src="`/img/logos/${entity.slug}/thumbs/${entity.slug}.png`" :src="`/img/logos/${entity.slug}/thumbs/${entity.slug}.png`"
:alt="entity.name" :alt="entity.name"
loading="lazy"
class="logo" class="logo"
@load="$emit('load', $event)"
> >
</template> </template>
@ -42,6 +48,7 @@ export default {
default: null, default: null,
}, },
}, },
emits: ['load'],
}; };
</script> </script>

View File

@ -29,7 +29,7 @@ function curateActor(actor, release) {
updatedAt: new Date(actor.updatedAt), updatedAt: new Date(actor.updatedAt),
}; };
if (actor.profiles && actor.profiles.length > 0) { if (actor.profiles) {
const photos = actor.profiles const photos = actor.profiles
.map(profile => ({ entity: profile.entity, ...profile.avatar })) .map(profile => ({ entity: profile.entity, ...profile.avatar }))
.filter(avatar => avatar.id && (!curatedActor.avatar || avatar.hash !== curatedActor.avatar.hash)); .filter(avatar => avatar.id && (!curatedActor.avatar || avatar.hash !== curatedActor.avatar.hash));

View File

@ -240,8 +240,6 @@ exports.up = knex => Promise.resolve()
table.text('slug', 32) table.text('slug', 32)
.notNullable(); .notNullable();
table.text('real_name');
table.integer('entity_id', 12) table.integer('entity_id', 12)
.references('id') .references('id')
.inTable('entities'); .inTable('entities');
@ -252,6 +250,8 @@ exports.up = knex => Promise.resolve()
.references('id') .references('id')
.inTable('actors'); .inTable('actors');
table.text('real_name');
table.date('date_of_birth'); table.date('date_of_birth');
table.date('date_of_death'); table.date('date_of_death');
table.integer('age', 3); table.integer('age', 3);

18
package-lock.json generated
View File

@ -8156,6 +8156,24 @@
"make-iterator": "^1.0.0" "make-iterator": "^1.0.0"
} }
}, },
"object.omit": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/object.omit/-/object.omit-3.0.0.tgz",
"integrity": "sha512-EO+BCv6LJfu+gBIF3ggLicFebFLN5zqzz/WWJlMFfkMyGth+oBkhxzDl0wx2W4GkLzuQs/FsSkXZb2IMWQqmBQ==",
"requires": {
"is-extendable": "^1.0.0"
},
"dependencies": {
"is-extendable": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
"integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
"requires": {
"is-plain-object": "^2.0.4"
}
}
}
},
"object.pick": { "object.pick": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",

View File

@ -111,6 +111,7 @@
"moment": "^2.24.0", "moment": "^2.24.0",
"nanoid": "^2.1.11", "nanoid": "^2.1.11",
"object-merge-advanced": "^10.12.1", "object-merge-advanced": "^10.12.1",
"object.omit": "^3.0.0",
"opn": "^5.5.0", "opn": "^5.5.0",
"pg": "^8.5.1", "pg": "^8.5.1",
"postgraphile": "^4.10.0", "postgraphile": "^4.10.0",

View File

@ -7,6 +7,7 @@ const moment = require('moment');
const blake2 = require('blake2'); const blake2 = require('blake2');
const DOMPurify = require('dompurify'); const DOMPurify = require('dompurify');
const { JSDOM } = require('jsdom'); const { JSDOM } = require('jsdom');
const omit = require('object.omit');
const { window } = new JSDOM(''); const { window } = new JSDOM('');
const domPurify = DOMPurify(window); const domPurify = DOMPurify(window);
@ -21,7 +22,7 @@ const bulkInsert = require('./utils/bulk-insert');
const logger = require('./logger')(__filename); const logger = require('./logger')(__filename);
const { toBaseReleases } = require('./deep'); const { toBaseReleases } = require('./deep');
const { associateAvatars } = require('./media'); const { associateAvatars, flushOrphanedMedia } = require('./media');
const slugify = require('./utils/slugify'); const slugify = require('./utils/slugify');
const capitalize = require('./utils/capitalize'); const capitalize = require('./utils/capitalize');
@ -436,18 +437,26 @@ async function curateProfile(profile, actor) {
} }
} }
async function interpolateProfiles(actorIdsOrNames) { async function fetchProfiles(actorIdsOrNames) {
const profiles = await knex('actors_profiles') return knex('actors_profiles')
.select('actors_profiles.*', knex.raw('row_to_json(media) as avatar')) .select(knex.raw('actors_profiles.*, row_to_json(actors) as actor, row_to_json(media) as avatar'))
.leftJoin('actors', 'actors.id', 'actors_profiles.actor_id') .leftJoin('actors', 'actors.id', 'actors_profiles.actor_id')
.modify((query) => { .modify((query) => {
if (actorIdsOrNames?.length > 0) { if (actorIdsOrNames) {
query query
.whereIn('actor_id', actorIdsOrNames.filter(idOrName => typeof idOrName === 'number')) .whereIn('actor_id', actorIdsOrNames.filter(idOrName => typeof idOrName === 'number'))
.orWhereIn('actors.name', actorIdsOrNames.filter(idOrName => typeof idOrName === 'string')); .orWhere((builder) => {
builder
.whereIn('actors.name', actorIdsOrNames.filter(idOrName => typeof idOrName === 'string'))
.whereNull('actors.entity_id');
});
} }
}) })
.leftJoin('media', 'actors_profiles.avatar_media_id', 'media.id'); .leftJoin('media', 'actors_profiles.avatar_media_id', 'media.id');
}
async function interpolateProfiles(actorIdsOrNames) {
const profiles = await fetchProfiles(actorIdsOrNames);
const profilesByActorId = profiles.reduce((acc, profile) => ({ const profilesByActorId = profiles.reduce((acc, profile) => ({
...acc, ...acc,
@ -545,6 +554,27 @@ async function interpolateProfiles(actorIdsOrNames) {
const transaction = await knex.transaction(); const transaction = await knex.transaction();
// clear existing interpolated data
const emptyProfile = Object
.keys(omit(curateProfileEntry({ id: 1 }), ['id', 'actor_id', 'entity_id', 'url', 'description_hash']))
.reduce((acc, key) => ({ ...acc, [key]: null }), {});
await knex('actors')
.modify((modifyBuilder) => {
if (actorIdsOrNames) {
modifyBuilder
.whereIn('id', actorIdsOrNames.filter(idOrName => typeof idOrName === 'number'))
.orWhere((whereBuilder) => {
whereBuilder
.whereIn('name', actorIdsOrNames.filter(idOrName => typeof idOrName === 'string'))
.whereNull('entity_id');
});
}
})
.update(emptyProfile)
.transacting(transaction);
// insert new interpolated data
const queries = interpolatedProfiles.map(profile => knex('actors') const queries = interpolatedProfiles.map(profile => knex('actors')
.where('id', profile.id) .where('id', profile.id)
.update(profile) .update(profile)
@ -689,6 +719,30 @@ async function storeProfiles(profiles) {
await interpolateProfiles(actorIds); await interpolateProfiles(actorIds);
} }
async function flushProfiles(actorIdsOrNames) {
const profiles = await fetchProfiles(actorIdsOrNames);
const actorNames = Array.from(new Set(profiles.map(profile => profile.actor.name)));
const deleteCount = await knex('actors_profiles')
.whereIn('id', profiles.map(profile => profile.id))
.delete();
await interpolateProfiles(actorIdsOrNames);
await flushOrphanedMedia(); // don't flush until main avatar is detached by re-interpolating
if (actorNames.length > 20) {
logger.info(`Removed ${deleteCount} profiles for ${actorNames.length} actors`);
return;
}
if (deleteCount > 0) {
logger.info(`Removed ${deleteCount} profiles for ${actorNames.join(', ')}`);
return;
}
logger.info(`Removed ${deleteCount} profiles`);
}
async function scrapeActors(argNames) { async function scrapeActors(argNames) {
const actorNames = await getActorNames(argNames); const actorNames = await getActorNames(argNames);
const baseActors = toBaseActors(actorNames); const baseActors = toBaseActors(actorNames);
@ -905,8 +959,9 @@ async function searchActors(query) {
module.exports = { module.exports = {
associateActors, associateActors,
fetchActor, fetchActor,
flushProfiles,
interpolateProfiles,
scrapeActors, scrapeActors,
searchActors, searchActors,
toBaseActors, toBaseActors,
interpolateProfiles,
}; };

View File

@ -9,7 +9,7 @@ const knex = require('./knex');
const fetchUpdates = require('./updates'); const fetchUpdates = require('./updates');
const { fetchScenes, fetchMovies } = require('./deep'); const { fetchScenes, fetchMovies } = require('./deep');
const { storeScenes, storeMovies, updateReleasesSearch } = require('./store-releases'); const { storeScenes, storeMovies, updateReleasesSearch } = require('./store-releases');
const { scrapeActors, interpolateProfiles } = require('./actors'); const { scrapeActors, flushProfiles, interpolateProfiles } = require('./actors');
const { flushEntities } = require('./entities'); const { flushEntities } = require('./entities');
const { deleteScenes, deleteMovies, flushBatches } = require('./releases'); const { deleteScenes, deleteMovies, flushBatches } = require('./releases');
const { flushOrphanedMedia } = require('./media'); const { flushOrphanedMedia } = require('./media');
@ -26,7 +26,11 @@ async function init() {
} }
if (argv.interpolateProfiles) { if (argv.interpolateProfiles) {
await interpolateProfiles(argv.interpolateProfiles); await interpolateProfiles(argv.flushProfiles.length > 0 ? argv.flushProfiles : null);
}
if (argv.flushProfiles) {
await flushProfiles(argv.flushProfiles.length > 0 ? argv.flushProfiles : null);
} }
if (argv.flushNetworks || argv.flushChannels) { if (argv.flushNetworks || argv.flushChannels) {

View File

@ -268,6 +268,11 @@ const { argv } = yargs
type: 'array', type: 'array',
alias: 'flush-network', alias: 'flush-network',
}) })
.option('flush-profiles', {
describe: 'Delete all profiles for an actor.',
type: 'array',
alias: ['flush-profile', 'flush-actors', 'flush-actor'],
})
.option('flush-batches', { .option('flush-batches', {
describe: 'Delete all scenes and movies from batch by ID.', describe: 'Delete all scenes and movies from batch by ID.',
type: 'array', type: 'array',

View File

@ -779,6 +779,7 @@ async function flushOrphanedMedia() {
knex('releases_teasers').select('media_id'), knex('releases_teasers').select('media_id'),
knex('movies_covers').select('media_id'), knex('movies_covers').select('media_id'),
knex('movies_trailers').select('media_id'), knex('movies_trailers').select('media_id'),
knex('actors').select(knex.raw('avatar_media_id as media_id')),
knex('actors_profiles').select(knex.raw('avatar_media_id as media_id')), knex('actors_profiles').select(knex.raw('avatar_media_id as media_id')),
knex('actors_photos').select('media_id'), knex('actors_photos').select('media_id'),
knex('clips_photos').select('media_id'), knex('clips_photos').select('media_id'),