Added basic release and actor API.

This commit is contained in:
2020-05-20 01:11:32 +02:00
parent 6973d39cbd
commit 057362d011
18 changed files with 347 additions and 39 deletions

View File

@@ -130,6 +130,73 @@ function toBaseActors(actorsOrNames, release) {
});
}
function curateActor(actor) {
if (!actor) {
return null;
}
const curatedActor = {
id: actor.id,
name: actor.name,
slug: actor.slug,
gender: actor.gender,
alias: actor.alias && {
id: actor.alias.id,
name: actor.alias.name,
gender: actor.alias.gender,
},
network: actor.network && {
id: actor.network.id,
name: actor.network.name,
slug: actor.network.slug,
},
dateOfBirth: actor.date_of_birth,
dateOfDeath: actor.date_of_death,
cup: actor.cup,
bust: actor.bust,
waist: actor.waist,
hip: actor.hip,
naturalBoobs: actor.natural_boobs,
height: actor.height,
weight: actor.weight,
eyes: actor.eyes,
hair: actor.hair,
hasTattoos: actor.has_tattoos,
hasPiercings: actor.has_piercings,
tattoos: actor.tattoos,
piercings: actor.piercings,
description: actor.description,
origin: actor.birth_country && {
country: {
alpha2: actor.birth_country.alpha2,
name: actor.birth_country.name,
alias: actor.birth_country.alias,
},
state: actor.birth_state,
city: actor.birth_city,
},
residence: actor.residence_country && {
country: {
alpha2: actor.residence_country.alpha2,
name: actor.residence_country.name,
alias: actor.residence_country.alias,
},
state: actor.residence_state,
city: actor.residence_city,
},
avatar: actor.avatar && {
id: actor.avatar.id,
path: actor.avatar.path,
width: actor.avatar.width,
height: actor.avatar.height,
size: actor.avatar.size,
source: actor.avatar.source,
},
};
return curatedActor;
}
function curateActorEntry(baseActor, batchId) {
return {
name: baseActor.name,
@@ -196,7 +263,7 @@ async function curateProfile(profile) {
update: profile.update,
};
curatedProfile.description = domPurify.sanitize(profile.description.replace(/\s+/g, ' '), { ALLOWED_TAGS: [] }).trim() || null;
curatedProfile.description = domPurify.sanitize(profile.description?.replace(/\s+/g, ' '), { ALLOWED_TAGS: [] }).trim() || null;
const hasher = curatedProfile.description && blake2
.createHash('blake2b')
@@ -632,7 +699,36 @@ async function associateActors(releases, batchId) {
return actors;
}
async function fetchActor(actorId) {
const actor = await knex('actors')
.select(knex.raw(`
actors.*,
row_to_json(networks) as network,
row_to_json(actor_alias) as alias,
row_to_json(birth_country) as birth_country,
row_to_json(residence_country) as residence_country,
row_to_json(media) as avatar
`))
.modify((queryBuilder) => {
if (Number.isNaN(Number(actorId))) {
queryBuilder.where('actors.slug', actorId);
return;
}
queryBuilder.where('actors.id', actorId);
})
.leftJoin('actors as actor_alias', 'actor_alias.id', 'actors.alias_for')
.leftJoin('networks', 'networks.id', 'actors.network_id')
.leftJoin('countries as birth_country', 'birth_country.alpha2', 'actors.birth_country_alpha2')
.leftJoin('countries as residence_country', 'residence_country.alpha2', 'actors.residence_country_alpha2')
.leftJoin('media', 'media.id', 'actors.avatar_media_id')
.first();
return curateActor(actor);
}
module.exports = {
associateActors,
fetchActor,
scrapeActors,
};

View File

@@ -24,6 +24,7 @@ async function init() {
if (argv.actorScenes) {
const actorReleases = actors.map(actor => actor.releases).flat().filter(Boolean);
console.log(actors, actorReleases);
await storeReleases(actorReleases);
}

View File

@@ -2,19 +2,121 @@
const knex = require('./knex');
async function fetchReleases(limit = 100) {
const releases = await knex('releases').limit(limit);
function curateRelease(release, withMedia = false) {
if (!release) {
return null;
}
return releases;
const network = release.site_network || release.network;
return {
id: release.id,
entryId: release.entry_id,
shootId: release.shoot_id,
title: release.title,
url: release.url,
date: release.date,
description: release.description,
duration: release.duration,
site: release.site && {
id: release.site.id,
name: release.site.name,
slug: release.site.slug,
},
network: network && {
id: network.id,
name: network.name,
slug: network.slug,
},
actors: (release.actors || []).map(actor => ({
id: actor.id,
name: actor.name,
gender: actor.gender,
})),
...(withMedia && {
poster: release.poster ? {
id: release.poster.id,
path: release.poster.path,
width: release.poster.width,
height: release.poster.height,
size: release.poster.size,
source: release.poster.source,
} : null,
photos: (release.photos || []).map(photo => ({
id: photo.id,
path: photo.path,
width: photo.width,
height: photo.height,
size: photo.size,
source: photo.source,
})),
}),
createdAt: release.created_at,
};
}
function withRelations(queryBuilder, withMedia = false) {
queryBuilder
.select(knex.raw(`
releases.id, releases.entry_id, releases.shoot_id, releases.title, releases.url, releases.date, releases.description, releases.duration, releases.created_at,
row_to_json(sites) as site,
row_to_json(networks) as network,
row_to_json(site_networks) as site_network,
json_agg(DISTINCT actors) as actors
`))
.leftJoin('sites', 'sites.id', 'releases.site_id')
.leftJoin('networks', 'networks.id', 'releases.network_id')
.leftJoin('networks as site_networks', 'site_networks.id', 'sites.network_id')
.leftJoin('releases_actors', 'releases_actors.release_id', 'releases.id')
.leftJoin('actors', 'actors.id', 'releases_actors.actor_id')
.groupBy(knex.raw(`
releases.id, releases.entry_id, releases.shoot_id, releases.title, releases.url, releases.date, releases.description, releases.duration, releases.created_at,
sites.id, networks.id, site_networks.id
`));
if (withMedia) {
queryBuilder
.select(knex.raw(`
row_to_json(posters) as poster,
json_agg(DISTINCT photos) as photos
`))
.leftJoin('releases_posters', 'releases_posters.release_id', 'releases.id')
.leftJoin('media as posters', 'posters.id', 'releases_posters.media_id')
.leftJoin('releases_photos', 'releases_photos.release_id', 'releases.id')
.leftJoin('media as photos', 'photos.id', 'releases_photos.media_id')
.groupBy('posters.id');
}
}
async function fetchRelease(releaseId) {
const release = await knex('releases')
.where('releases.id', releaseId)
.modify(withRelations, true)
.first();
return curateRelease(release, true);
}
async function fetchReleases(limit = 100) {
const releases = await knex('releases')
.modify(withRelations)
.limit(Math.min(limit, 1000));
return releases.map(release => curateRelease(release));
}
async function searchReleases(query, limit = 100) {
const releases = await knex.raw('SELECT * FROM search_releases(?) LIMIT ?;', [query, limit]);
// const releases = await knex.raw('SELECT * FROM search_releases(?) LIMIT ?;', [query, limit]);
const releases = await knex
.from(knex.raw('search_releases(?) as releases', [query]))
.modify(withRelations)
.limit(Math.min(limit, 1000));
return releases.rows;
return releases.map(release => curateRelease(release));
}
module.exports = {
fetchRelease,
fetchReleases,
searchReleases,
};

View File

@@ -7,8 +7,8 @@ function include(argv) {
photos: argv.media && argv.photos,
poster: argv.media && argv.posters,
posters: argv.media && argv.posters,
releases: argv.withReleases,
scenes: argv.withReleases,
releases: argv.withScenes,
scenes: argv.withScenes,
teaser: argv.media && argv.videos && argv.teasers,
teasers: argv.media && argv.videos && argv.teasers,
trailer: argv.media && argv.videos && argv.trailers,

View File

@@ -1,31 +1,18 @@
'use strict';
const { fetchActors } = require('../actors');
const { fetchActor } = require('../actors');
async function fetchActorsApi(req, res) {
const actorId = typeof req.params.actorId === 'number' ? req.params.actorId : null;
const actorSlug = typeof req.params.actorId === 'string' ? req.params.actorId : null;
async function fetchActorApi(req, res) {
const actor = await fetchActor(req.params.actorId);
if (actorId || actorSlug) {
const actors = await fetchActors({
id: actorId,
slug: actorSlug,
});
if (actors.length > 0) {
res.send(actors[0]);
return;
}
res.status(404).send();
if (actor) {
res.send({ actor });
return;
}
const actors = await fetchActors(null, req.query.limit);
res.send(actors);
res.status(404).send({ actor: null });
}
module.exports = {
fetchActors: fetchActorsApi,
fetchActor: fetchActorApi,
};

View File

@@ -1,6 +1,17 @@
'use strict';
const { fetchReleases, searchReleases } = require('../releases');
const { fetchRelease, fetchReleases, searchReleases } = require('../releases');
async function fetchReleaseApi(req, res) {
const release = await fetchRelease(req.params.releaseId);
if (release) {
res.send({ release });
return;
}
res.status(404).send({ release: null });
}
async function fetchReleasesApi(req, res) {
const query = req.query.query || req.query.q;
@@ -9,9 +20,10 @@ async function fetchReleasesApi(req, res) {
? await searchReleases(query, req.query.limit)
: await fetchReleases(req.query.limit);
res.send(releases);
res.send({ releases });
}
module.exports = {
fetchRelease: fetchReleaseApi,
fetchReleases: fetchReleasesApi,
};

View File

@@ -15,9 +15,12 @@ const logger = require('../logger')(__filename);
const { ActorPlugins, SitePlugins, ReleasePlugins } = require('./plugins/plugins');
const {
fetchRelease,
fetchReleases,
} = require('./releases');
const { fetchActor } = require('./actors');
function initServer() {
const app = express();
const router = Router();
@@ -61,6 +64,9 @@ function initServer() {
router.use(bodyParser.json({ strict: false }));
router.get('/api/releases', fetchReleases);
router.get('/api/releases/:releaseId', fetchRelease);
router.get('/api/actors/:actorId', fetchActor);
router.get('*', (req, res) => {
res.render(path.join(__dirname, '../../assets/index.ejs'), {