Compare commits

..

No commits in common. "cdea8770249520b7d3ec0d2a90aeb909a5fbe513" and "162e5c218188fa401e0f0407b00f5b33f909fd3f" have entirely different histories.

6 changed files with 111 additions and 223 deletions

5
package-lock.json generated
View File

@ -1,11 +1,11 @@
{ {
"name": "traxxx", "name": "traxxx",
"version": "1.177.0", "version": "1.176.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"version": "1.177.0", "version": "1.176.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@graphile-contrib/pg-order-by-related": "^1.0.0-beta.6", "@graphile-contrib/pg-order-by-related": "^1.0.0-beta.6",
@ -42,7 +42,6 @@
"fluent-ffmpeg": "^2.1.2", "fluent-ffmpeg": "^2.1.2",
"fs-extra": "^7.0.1", "fs-extra": "^7.0.1",
"graphile-utils": "^4.5.6", "graphile-utils": "^4.5.6",
"graphql": "^14.6.0",
"iconv-lite": "^0.5.1", "iconv-lite": "^0.5.1",
"inquirer": "^7.3.3", "inquirer": "^7.3.3",
"jsdom": "^16.3.0", "jsdom": "^16.3.0",

View File

@ -1,6 +1,6 @@
{ {
"name": "traxxx", "name": "traxxx",
"version": "1.177.0", "version": "1.176.0",
"description": "All the latest porn releases in one place", "description": "All the latest porn releases in one place",
"main": "src/app.js", "main": "src/app.js",
"scripts": { "scripts": {
@ -103,7 +103,6 @@
"fluent-ffmpeg": "^2.1.2", "fluent-ffmpeg": "^2.1.2",
"fs-extra": "^7.0.1", "fs-extra": "^7.0.1",
"graphile-utils": "^4.5.6", "graphile-utils": "^4.5.6",
"graphql": "^14.6.0",
"iconv-lite": "^0.5.1", "iconv-lite": "^0.5.1",
"inquirer": "^7.3.3", "inquirer": "^7.3.3",
"jsdom": "^16.3.0", "jsdom": "^16.3.0",

View File

@ -5,78 +5,7 @@ const inquirer = require('inquirer');
const logger = require('./logger')(__filename); const logger = require('./logger')(__filename);
const knex = require('./knex'); const knex = require('./knex');
const { flushOrphanedMedia } = require('./media'); const { flushOrphanedMedia } = require('./media');
const { HttpError } = require('./errors');
const { graphql } = require('./web/graphql');
const releaseFields = `
id
entryId
shootId
title
url
date
description
duration
entity {
id
name
slug
parent {
id
name
slug
}
}
actors: releasesActors {
actor {
id
name
slug
gender
aliasFor
entityId
entryId
}
}
tags: releasesTags {
tag {
id
name
slug
}
}
chapters @include (if: $full) {
id
index
time
duration
title
description
}
poster: releasesPosterByReleaseId {
media {
id
path
thumbnail
s3: isS3
width
height
size
}
}
photos: releasesPhotos @include (if: $full) {
media {
id
path
thumbnail
s3: isS3
width
height
size
}
}
createdAt
`;
function curateRelease(release, withMedia = false, withPoster = true) { function curateRelease(release, withMedia = false, withPoster = true) {
if (!release) { if (!release) {
@ -152,100 +81,93 @@ function curateRelease(release, withMedia = false, withPoster = true) {
}; };
} }
function curateGraphqlRelease(release) { function withRelations(queryBuilder, withMedia = false, withPoster = true) {
if (!release) { queryBuilder
return null; .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(entities) as entity,
row_to_json(parents) as parent,
COALESCE(json_agg(DISTINCT actors) FILTER (WHERE actors.id IS NOT NULL), '[]') as actors,
COALESCE(json_agg(DISTINCT tags) FILTER (WHERE tags.id IS NOT NULL), '[]') as tags,
COALESCE(json_agg(DISTINCT chapters) FILTER (WHERE chapters.id IS NOT NULL), '[]') as chapters
`))
.leftJoin('entities', 'entities.id', 'releases.entity_id')
.leftJoin('entities as parents', 'parents.id', 'entities.parent_id')
.leftJoin('releases_actors', 'releases_actors.release_id', 'releases.id')
.leftJoin('actors', 'actors.id', 'releases_actors.actor_id')
.leftJoin('releases_tags', 'releases_tags.release_id', 'releases.id')
.leftJoin('tags', 'tags.id', 'releases_tags.tag_id')
.leftJoin('chapters', 'chapters.release_id', 'releases.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,
entities.id, parents.id
`));
if (withMedia || withPoster) {
queryBuilder
.select(knex.raw(`
row_to_json(posters) as poster
`))
.leftJoin('releases_posters', 'releases_posters.release_id', 'releases.id')
.leftJoin('media as posters', 'posters.id', 'releases_posters.media_id')
.groupBy('posters.id');
} }
return { if (withMedia) {
id: release.id, queryBuilder
...(release.relevance && { relevance: release.relevance }), .select(knex.raw(`
entryId: release.entryId, row_to_json(trailers) as trailer,
shootId: release.shootId, COALESCE(json_agg(DISTINCT photos) FILTER (WHERE photos.id IS NOT NULL), '[]') as photos
title: release.title || null, `))
url: release.url || null, .leftJoin('releases_photos', 'releases_photos.release_id', 'releases.id')
date: release.date, .leftJoin('media as photos', 'photos.id', 'releases_photos.media_id')
description: release.description || null, .leftJoin('releases_trailers', 'releases_trailers.release_id', 'releases.id')
duration: release.duration, .leftJoin('media as trailers', 'trailers.id', 'releases_trailers.media_id')
entity: release.entity, .groupBy('posters.id', 'trailers.id');
actors: release.actors.map(actor => actor.actor), }
tags: release.tags.map(tag => tag.tag),
...(release.chapters && { chapters: release.chapters }),
poster: release.poster?.media || null,
...(release.photos && { photos: release.photos.map(photo => photo.media) }),
trailer: release.trailer?.media || null,
createdAt: release.createdAt,
};
} }
async function fetchScene(releaseId) { async function fetchScene(releaseId) {
const { release } = await graphql(` const release = await knex('releases')
query Release( .where('releases.id', releaseId)
$releaseId: Int! .modify(withRelations, true, true)
$full: Boolean = true .first();
) {
release(id: $releaseId) {
${releaseFields}
}
}
`, {
releaseId: Number(releaseId),
});
return curateGraphqlRelease(release); return curateRelease(release, true);
} }
async function fetchScenes(limit = 100) { async function fetchScenes(limit = 100) {
const { releases } = await graphql(` if (typeof limit !== 'number') {
query SearchReleases( throw new HttpError('Limit parameter needs to be a number', 400);
$limit: Int = 20 }
$full: Boolean = false
) {
releases(
first: $limit
orderBy: DATE_DESC
) {
${releaseFields}
}
}
`, {
limit: Math.min(limit, 10000),
});
return releases.map(release => curateGraphqlRelease(release)); const releases = await knex('releases')
.modify(withRelations, false, true)
.limit(Math.min(limit, 1000000));
return releases.map(release => curateRelease(release));
} }
async function searchScenes(query, limit = 100, relevance = 0) { async function searchScenes(query, limit = 100, relevance = 0) {
const { releases } = await graphql(` if (typeof limit !== 'number') {
query SearchReleases( throw new HttpError('Limit parameter needs to be a number', 400);
$query: String! }
$limit: Int = 20
$relevance: Float = 0.025
$full: Boolean = false
) {
releases: searchReleases(
query: $query
first: $limit
orderBy: RANK_DESC
filter: {
rank: {
greaterThan: $relevance
}
}
) {
rank
release {
${releaseFields}
}
}
}
`, {
query,
limit,
relevance,
});
return releases.map(release => curateGraphqlRelease({ ...release.release, relevance: release.rank })); if (typeof relevance !== 'number') {
throw new HttpError('Relevance parameter needs to be a number', 400);
}
const releases = await knex
.select(knex.raw('search_results.rank as relevance'))
.from(knex.raw('search_releases(:query) as search_results', { query }))
.leftJoin('releases', 'releases.id', 'search_results.release_id')
.where('search_results.rank', '>=', relevance)
.modify(withRelations, false, true)
.limit(Math.min(limit, 1000000))
.groupBy('search_results.rank')
.orderBy('search_results.rank', 'desc');
return releases.map(release => curateRelease(release));
} }
async function deleteScenes(sceneIds) { async function deleteScenes(sceneIds) {

View File

@ -1,24 +0,0 @@
'use strict';
const { withPostGraphileContext } = require('postgraphile');
const { graphql } = require('graphql');
const pg = require('./postgraphile');
const logger = require('../logger')(__filename);
async function query(graphqlQuery, params) {
return withPostGraphileContext(pg, async (context) => {
const schema = await pg.getGraphQLSchema();
const result = await graphql(schema, graphqlQuery, null, context, params);
if (result.errors?.length > 0) {
logger.error(result.errors);
throw result.errors[0];
}
return result.data;
});
}
module.exports = { graphql: query };

View File

@ -1,39 +0,0 @@
'use strict';
const config = require('config');
const { postgraphile } = require('postgraphile');
const PgConnectionFilterPlugin = require('postgraphile-plugin-connection-filter');
const PgSimplifyInflectorPlugin = require('@graphile-contrib/pg-simplify-inflector');
const PgOrderByRelatedPlugin = require('@graphile-contrib/pg-order-by-related');
const { ActorPlugins, SitePlugins, ReleasePlugins } = require('./plugins/plugins');
const connectionString = `postgres://${config.database.user}:${config.database.password}@${config.database.host}:5432/${config.database.database}`;
module.exports = postgraphile(
connectionString,
'public',
{
// watchPg: true,
dynamicJson: true,
graphiql: true,
enhanceGraphiql: true,
allowExplain: () => true,
// simpleCollections: 'only',
simpleCollections: 'both',
graphileBuildOptions: {
pgOmitListSuffix: true,
connectionFilterRelations: true,
},
appendPlugins: [
PgSimplifyInflectorPlugin,
PgConnectionFilterPlugin,
PgOrderByRelatedPlugin,
...ActorPlugins,
...SitePlugins,
...ReleasePlugins,
],
},
);

View File

@ -3,18 +3,22 @@
const path = require('path'); const path = require('path');
const config = require('config'); const config = require('config');
const express = require('express'); const express = require('express');
const { postgraphile } = require('postgraphile');
const Router = require('express-promise-router'); const Router = require('express-promise-router');
const bodyParser = require('body-parser'); const bodyParser = require('body-parser');
const session = require('express-session'); const session = require('express-session');
const KnexSessionStore = require('connect-session-knex')(session); const KnexSessionStore = require('connect-session-knex')(session);
const nanoid = require('nanoid'); const nanoid = require('nanoid');
const PgConnectionFilterPlugin = require('postgraphile-plugin-connection-filter');
const PgSimplifyInflectorPlugin = require('@graphile-contrib/pg-simplify-inflector');
const PgOrderByRelatedPlugin = require('@graphile-contrib/pg-order-by-related');
const logger = require('../logger')(__filename); const logger = require('../logger')(__filename);
const knex = require('../knex'); const knex = require('../knex');
const { ActorPlugins, SitePlugins, ReleasePlugins } = require('./plugins/plugins');
const errorHandler = require('./error'); const errorHandler = require('./error');
const pg = require('./postgraphile');
const { const {
fetchScene, fetchScene,
fetchScenes, fetchScenes,
@ -41,7 +45,34 @@ async function initServer() {
const router = Router(); const router = Router();
const store = new KnexSessionStore({ knex }); const store = new KnexSessionStore({ knex });
app.use(pg); const connectionString = `postgres://${config.database.user}:${config.database.password}@${config.database.host}:5432/${config.database.database}`;
app.use(postgraphile(
connectionString,
'public',
{
// watchPg: true,
dynamicJson: true,
graphiql: true,
enhanceGraphiql: true,
allowExplain: () => true,
// simpleCollections: 'only',
simpleCollections: 'both',
graphileBuildOptions: {
pgOmitListSuffix: true,
connectionFilterRelations: true,
},
appendPlugins: [
PgSimplifyInflectorPlugin,
PgConnectionFilterPlugin,
PgOrderByRelatedPlugin,
...ActorPlugins,
...SitePlugins,
...ReleasePlugins,
],
},
));
app.set('view engine', 'ejs'); app.set('view engine', 'ejs');
router.use('/media', express.static(config.media.path)); router.use('/media', express.static(config.media.path));