From af67d733ade7ff39a2567156b213b9434603e1c7 Mon Sep 17 00:00:00 2001 From: DebaucheryLibrarian Date: Wed, 30 Dec 2020 02:23:43 +0100 Subject: [PATCH] Added profile flush. --- assets/components/entities/children.vue | 25 +++++---- assets/components/entities/entity.vue | 6 +++ assets/components/entities/tile.vue | 7 +++ assets/js/curate.js | 2 +- migrations/20190325001339_releases.js | 4 +- package-lock.json | 18 +++++++ package.json | 1 + src/actors.js | 69 ++++++++++++++++++++++--- src/app.js | 8 ++- src/argv.js | 5 ++ src/media.js | 1 + 11 files changed, 123 insertions(+), 23 deletions(-) diff --git a/assets/components/entities/children.vue b/assets/components/entities/children.vue index 2206e8b61..9cf18d7a7 100644 --- a/assets/components/entities/children.vue +++ b/assets/components/entities/children.vue @@ -1,10 +1,15 @@ @@ -15,6 +20,7 @@ export default { components: { EntityTile, }, + emits: ['load'], props: { entity: { type: Object, @@ -33,13 +39,14 @@ export default { padding: 1rem; margin: 0 1rem 0 0; border-bottom: solid 1px var(--darken-hint); - overflow-x: auto; - scroll-behavior: smooth; - scrollbar-width: none; + + .tile-container { + display: inline-block; + padding: 0 1rem 0 0; + } .tile { width: 15rem; - margin: 0 1rem 0 0; } &.expanded { @@ -52,10 +59,6 @@ export default { width: 100%; } } - - &::-webkit-scrollbar { - display: none; - } } @media(max-width: $breakpoint0) { diff --git a/assets/components/entities/entity.vue b/assets/components/entities/entity.vue index c1668b9dd..799957bb2 100644 --- a/assets/components/entities/entity.vue +++ b/assets/components/entities/entity.vue @@ -81,6 +81,7 @@ > @@ -234,6 +236,10 @@ export default { flex-direction: column; } +.scroll { + background: var(--profile); +} + .releases { flex-grow: 1; } diff --git a/assets/components/entities/tile.vue b/assets/components/entities/tile.vue index db9e4ebd0..e6185b29a 100644 --- a/assets/components/entities/tile.vue +++ b/assets/components/entities/tile.vue @@ -9,21 +9,27 @@ v-if="entity.type === 'network' || entity.independent" :src="`/img/logos/${entity.slug}/thumbs/network.png`" :alt="entity.name" + loading="lazy" class="logo" + @load="$emit('load', $event)" > @@ -42,6 +48,7 @@ export default { default: null, }, }, + emits: ['load'], }; diff --git a/assets/js/curate.js b/assets/js/curate.js index 8dc25d77c..137c0f853 100644 --- a/assets/js/curate.js +++ b/assets/js/curate.js @@ -29,7 +29,7 @@ function curateActor(actor, release) { updatedAt: new Date(actor.updatedAt), }; - if (actor.profiles && actor.profiles.length > 0) { + if (actor.profiles) { const photos = actor.profiles .map(profile => ({ entity: profile.entity, ...profile.avatar })) .filter(avatar => avatar.id && (!curatedActor.avatar || avatar.hash !== curatedActor.avatar.hash)); diff --git a/migrations/20190325001339_releases.js b/migrations/20190325001339_releases.js index 71b667cb7..c366210bb 100644 --- a/migrations/20190325001339_releases.js +++ b/migrations/20190325001339_releases.js @@ -240,8 +240,6 @@ exports.up = knex => Promise.resolve() table.text('slug', 32) .notNullable(); - table.text('real_name'); - table.integer('entity_id', 12) .references('id') .inTable('entities'); @@ -252,6 +250,8 @@ exports.up = knex => Promise.resolve() .references('id') .inTable('actors'); + table.text('real_name'); + table.date('date_of_birth'); table.date('date_of_death'); table.integer('age', 3); diff --git a/package-lock.json b/package-lock.json index 78b3c0a16..b12601edf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8156,6 +8156,24 @@ "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": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", diff --git a/package.json b/package.json index a7ceeb994..8df4e5c69 100644 --- a/package.json +++ b/package.json @@ -111,6 +111,7 @@ "moment": "^2.24.0", "nanoid": "^2.1.11", "object-merge-advanced": "^10.12.1", + "object.omit": "^3.0.0", "opn": "^5.5.0", "pg": "^8.5.1", "postgraphile": "^4.10.0", diff --git a/src/actors.js b/src/actors.js index a7fdb2172..129c25afe 100644 --- a/src/actors.js +++ b/src/actors.js @@ -7,6 +7,7 @@ const moment = require('moment'); const blake2 = require('blake2'); const DOMPurify = require('dompurify'); const { JSDOM } = require('jsdom'); +const omit = require('object.omit'); const { window } = new JSDOM(''); const domPurify = DOMPurify(window); @@ -21,7 +22,7 @@ const bulkInsert = require('./utils/bulk-insert'); const logger = require('./logger')(__filename); const { toBaseReleases } = require('./deep'); -const { associateAvatars } = require('./media'); +const { associateAvatars, flushOrphanedMedia } = require('./media'); const slugify = require('./utils/slugify'); const capitalize = require('./utils/capitalize'); @@ -436,18 +437,26 @@ async function curateProfile(profile, actor) { } } -async function interpolateProfiles(actorIdsOrNames) { - const profiles = await knex('actors_profiles') - .select('actors_profiles.*', knex.raw('row_to_json(media) as avatar')) +async function fetchProfiles(actorIdsOrNames) { + return knex('actors_profiles') + .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') .modify((query) => { - if (actorIdsOrNames?.length > 0) { + if (actorIdsOrNames) { query .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'); +} + +async function interpolateProfiles(actorIdsOrNames) { + const profiles = await fetchProfiles(actorIdsOrNames); const profilesByActorId = profiles.reduce((acc, profile) => ({ ...acc, @@ -545,6 +554,27 @@ async function interpolateProfiles(actorIdsOrNames) { 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') .where('id', profile.id) .update(profile) @@ -689,6 +719,30 @@ async function storeProfiles(profiles) { 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) { const actorNames = await getActorNames(argNames); const baseActors = toBaseActors(actorNames); @@ -905,8 +959,9 @@ async function searchActors(query) { module.exports = { associateActors, fetchActor, + flushProfiles, + interpolateProfiles, scrapeActors, searchActors, toBaseActors, - interpolateProfiles, }; diff --git a/src/app.js b/src/app.js index 37f1c6755..83590ec34 100644 --- a/src/app.js +++ b/src/app.js @@ -9,7 +9,7 @@ const knex = require('./knex'); const fetchUpdates = require('./updates'); const { fetchScenes, fetchMovies } = require('./deep'); const { storeScenes, storeMovies, updateReleasesSearch } = require('./store-releases'); -const { scrapeActors, interpolateProfiles } = require('./actors'); +const { scrapeActors, flushProfiles, interpolateProfiles } = require('./actors'); const { flushEntities } = require('./entities'); const { deleteScenes, deleteMovies, flushBatches } = require('./releases'); const { flushOrphanedMedia } = require('./media'); @@ -26,7 +26,11 @@ async function init() { } 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) { diff --git a/src/argv.js b/src/argv.js index 214a4254a..d7c99920e 100644 --- a/src/argv.js +++ b/src/argv.js @@ -268,6 +268,11 @@ const { argv } = yargs type: 'array', 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', { describe: 'Delete all scenes and movies from batch by ID.', type: 'array', diff --git a/src/media.js b/src/media.js index a4d8287d3..36d250f88 100644 --- a/src/media.js +++ b/src/media.js @@ -779,6 +779,7 @@ async function flushOrphanedMedia() { knex('releases_teasers').select('media_id'), knex('movies_covers').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_photos').select('media_id'), knex('clips_photos').select('media_id'),