diff --git a/assets/components/actors/actor.vue b/assets/components/actors/actor.vue
index 22bdcdb0..08938563 100644
--- a/assets/components/actors/actor.vue
+++ b/assets/components/actors/actor.vue
@@ -210,7 +210,7 @@
v-if="actor.hasTattoos"
class="bio-item tattoos hideable"
>
- Tattoos
+ Tattoos
-
-
@@ -595,15 +595,16 @@ export default {
.description {
margin: 0;
- padding: 0 2rem 0 0;
+ padding: 0 1rem;
+ border-left: solid 3px var(--lighten-hint);
line-height: 1.5;
font-size: .9rem;
}
.description-logo {
display: block;
- width: 15rem;
- max-height: 2rem;
+ width: 12rem;
+ max-height: 1.5rem;
margin: .5rem 0 1.5rem 0;
object-fit: contain;
object-position: 0 50%;
@@ -623,9 +624,9 @@ export default {
.photos-container {
min-width: 15rem;
box-sizing: border-box;
- border-right: solid 1px $shadow-hint;
- padding: 1rem 1.5rem 1rem 1rem;
- margin: 0 .5rem 0 0;
+ border-left: solid 1px $shadow-hint;
+ padding: 1rem 1rem 1rem 1.5rem;
+ margin: 0 0 0 .5rem;
}
.photos.compact {
diff --git a/assets/img/icons/anchor.svg b/assets/img/icons/anchor.svg
new file mode 100644
index 00000000..4e4542b9
--- /dev/null
+++ b/assets/img/icons/anchor.svg
@@ -0,0 +1,5 @@
+
+
diff --git a/assets/img/icons/lotus.svg b/assets/img/icons/lotus.svg
new file mode 100755
index 00000000..0dabe206
--- /dev/null
+++ b/assets/img/icons/lotus.svg
@@ -0,0 +1,77 @@
+
+
diff --git a/assets/img/icons/tattoo.svg b/assets/img/icons/tattoo.svg
new file mode 100644
index 00000000..dcd7f7df
--- /dev/null
+++ b/assets/img/icons/tattoo.svg
@@ -0,0 +1,21 @@
+
diff --git a/public/img/logos/freeones/freeoneslegacy.png b/public/img/logos/freeones/freeoneslegacy.png
index 6900e730..7c623fd8 100644
Binary files a/public/img/logos/freeones/freeoneslegacy.png and b/public/img/logos/freeones/freeoneslegacy.png differ
diff --git a/public/img/logos/freeones/lazy/freeones.png b/public/img/logos/freeones/lazy/freeones.png
index f0264c08..d1ddb077 100644
Binary files a/public/img/logos/freeones/lazy/freeones.png and b/public/img/logos/freeones/lazy/freeones.png differ
diff --git a/public/img/logos/freeones/lazy/freeoneslegacy.png b/public/img/logos/freeones/lazy/freeoneslegacy.png
index 73510f48..2d7cf5bc 100644
Binary files a/public/img/logos/freeones/lazy/freeoneslegacy.png and b/public/img/logos/freeones/lazy/freeoneslegacy.png differ
diff --git a/public/img/logos/freeones/lazy/network.png b/public/img/logos/freeones/lazy/network.png
index 9dc83830..d9148c62 100644
Binary files a/public/img/logos/freeones/lazy/network.png and b/public/img/logos/freeones/lazy/network.png differ
diff --git a/public/img/logos/freeones/thumbs/freeones.png b/public/img/logos/freeones/thumbs/freeones.png
index 143a88c1..58a14674 100644
Binary files a/public/img/logos/freeones/thumbs/freeones.png and b/public/img/logos/freeones/thumbs/freeones.png differ
diff --git a/public/img/logos/freeones/thumbs/freeoneslegacy.png b/public/img/logos/freeones/thumbs/freeoneslegacy.png
index a1aa3cbd..bfd1e530 100644
Binary files a/public/img/logos/freeones/thumbs/freeoneslegacy.png and b/public/img/logos/freeones/thumbs/freeoneslegacy.png differ
diff --git a/public/img/logos/freeones/thumbs/network.png b/public/img/logos/freeones/thumbs/network.png
index 0e99048e..13cb6436 100644
Binary files a/public/img/logos/freeones/thumbs/network.png and b/public/img/logos/freeones/thumbs/network.png differ
diff --git a/src/actors.js b/src/actors.js
index 1d9ac36c..8faa6cf7 100644
--- a/src/actors.js
+++ b/src/actors.js
@@ -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,
};
diff --git a/src/app.js b/src/app.js
index adeaf70c..741e5344 100644
--- a/src/app.js
+++ b/src/app.js
@@ -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);
}
diff --git a/src/releases.js b/src/releases.js
index d5fe5089..b8e7df8a 100644
--- a/src/releases.js
+++ b/src/releases.js
@@ -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,
};
diff --git a/src/utils/argv-include.js b/src/utils/argv-include.js
index a955bec6..72fd0d3e 100644
--- a/src/utils/argv-include.js
+++ b/src/utils/argv-include.js
@@ -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,
diff --git a/src/web/actors.js b/src/web/actors.js
index b6cace5c..2dd61957 100644
--- a/src/web/actors.js
+++ b/src/web/actors.js
@@ -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,
};
diff --git a/src/web/releases.js b/src/web/releases.js
index 68e1f70f..b6094cea 100644
--- a/src/web/releases.js
+++ b/src/web/releases.js
@@ -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,
};
diff --git a/src/web/server.js b/src/web/server.js
index 48024c62..a53b2440 100644
--- a/src/web/server.js
+++ b/src/web/server.js
@@ -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'), {