diff --git a/.eslintrc b/.eslintrc index 273362dc..dd96e40e 100644 --- a/.eslintrc +++ b/.eslintrc @@ -11,11 +11,7 @@ "no-console": 0, "indent": "off", "template-curly-spacing": "off", - "max-len": [2, { - "code": 300, - "tabWidth": 4, - "ignoreUrls": true - }], + "max-len": 0, "vue/no-v-html": 0, "vue/html-indent": ["error", 4], "vue/multiline-html-element-content-newline": 0, diff --git a/assets/components/actors/actor.vue b/assets/components/actors/actor.vue index dc550853..ac7e3b95 100644 --- a/assets/components/actors/actor.vue +++ b/assets/components/actors/actor.vue @@ -7,7 +7,9 @@

- {{ actor.name }} + {{ actor.name }} ({{ actor.network.name }}) + {{ actor.name }} + @import 'theme'; -.gender-link.selected .gender .icon { - fill: var(--text-light); +.gender-link { + &.selected .gender .icon { + fill: var(--text-light); + filter: none; + } + + &:hover:not(.selected) .gender .icon { + fill: var(--text-light); + } + + &:hover:not(.selected) .transsexual .icon { + fill: var(--female); + filter: drop-shadow(1px 0 0 var(--text-light)) drop-shadow(-1px 0 0 var(--text-light)) drop-shadow(0 1px 0 var(--text-light)) drop-shadow(0 -1px 0 var(--text-light)) drop-shadow(1px 0 0 var(--male)) drop-shadow(-1px 0 0 var(--male)) drop-shadow(0 1px 0 var(--male)) drop-shadow(0 -1px 0 var(--male)) drop-shadow(0 0 1px rgba(0, 0, 0, 0.5)); + } } @@ -159,7 +171,6 @@ export default { align-items: center; justify-content: center; box-sizing: border-box; - padding: .2rem 0 0 0; margin: .25rem .5rem .25rem 0; color: var(--shadow); background: var(--background); @@ -167,31 +178,29 @@ export default { text-decoration: none; box-shadow: 0 0 3px var(--darken-weak); + .male, + .female, + .transsexual { + padding: .2rem 0 0 0; + } + .icon { fill: var(--shadow); } &:hover { - color: var(--primary); + color: var(--text-light); cursor: pointer; + + .icon { + fill: var(--text-light); + } } &.selected { background: var(--primary); color: var(--text-light); - &.male { - background: var(--male); - } - - &.female { - background: var(--female); - } - - &.transsexual { - background: var(--text); - } - &.other .icon { fill: var(--text-light); } diff --git a/assets/components/container/container.vue b/assets/components/container/container.vue index b6e5386f..e7d3a4b3 100644 --- a/assets/components/container/container.vue +++ b/assets/components/container/container.vue @@ -72,6 +72,7 @@ export default { flex-direction: column; overflow: hidden; background: var(--background-dim); + color: var(--text); } .content { diff --git a/assets/components/releases/release.vue b/assets/components/releases/release.vue index a04dd9ed..e33ff5be 100644 --- a/assets/components/releases/release.vue +++ b/assets/components/releases/release.vue @@ -246,6 +246,7 @@ async function mounted() { this.release = await this.$store.dispatch('fetchReleaseById', this.$route.params.releaseId); this.filename = format(config.filename.pattern, { ...this.release, + shootId: this.release.shootId || '', date: this.formatDate(this.release.date, config.filename.date), }, { spreadSeparator: config.filename.separator, @@ -287,8 +288,8 @@ export default { .info { padding: 1rem; - border-left: solid 1px $shadow-hint; - border-right: solid 1px $shadow-hint; + border-left: solid 1px var(--shadow-hint); + border-right: solid 1px var(--shadow-hint); flex-grow: 1; } @@ -304,15 +305,15 @@ export default { .icon { display: inline-block; width: 1rem; - fill: $shadow-strong; + fill: var(--shadow-strong); margin: 0 1rem 0 0; } } .details { - background: $profile; - color: $text-contrast; - box-shadow: 0 0 3px $shadow-weak; + background: var(--profile); + color: var(--text-light); + box-shadow: 0 0 3px var(--shadow-weak); cursor: default; .column { @@ -322,7 +323,7 @@ export default { } .link { - color: $text-contrast; + color: var(--text-light); } } @@ -331,11 +332,11 @@ export default { height: 100%; &:not(:last-child) { - border-right: solid 1px $highlight-hint; + border-right: solid 1px var(--lighten-hint); } .icon { - fill: $highlight-weak; + fill: var(--lighten-weak); margin: 0 .25rem 0 0; } @@ -359,7 +360,6 @@ export default { .logo { display: inline-block; - filter: $logo-highlight; } .logo-site { @@ -377,7 +377,7 @@ export default { } .chain { - color: $highlight; + color: var(--lighten); padding: 0 .5rem; font-weight: bold; font-size: .8rem; @@ -388,7 +388,7 @@ export default { } .description { - line-height: 1.25; + line-height: 1.5; } .duration { @@ -416,34 +416,36 @@ export default { .filename { width: 100%; padding: .5rem; - border: solid 1px $shadow-weak; + color: var(--text); + border: solid 1px var(--shadow-weak); + background: var(--background); } .link { display: inline-block; - color: $link; + color: var(--link); text-decoration: none; &:hover { - color: $primary; + color: var(--primary); .icon { - fill: $primary; + fill: var(--primary); } } } .tag .link { - background: $background; + background: var(--background); display: inline-block; padding: .5rem; margin: 0 .25rem .25rem 0; - box-shadow: 0 0 2px $shadow-weak; + box-shadow: 0 0 2px var(--shadow-weak); text-decoration: none; text-transform: capitalize; &:hover { - color: $primary; + color: var(--primary); } } diff --git a/assets/components/tile/actor.vue b/assets/components/tile/actor.vue index 408b6f91..386a8f43 100644 --- a/assets/components/tile/actor.vue +++ b/assets/components/tile/actor.vue @@ -8,8 +8,21 @@ class="link" > + + {{ actor.name }} + + + {{ actor.name }}
@@ -89,28 +102,54 @@ export default { .actor { width: 100%; - background: $background; display: inline-block; margin: 0 .5rem .5rem 0; - box-shadow: 0 0 3px $shadow-weak; + box-shadow: 0 0 3px var(--darken-weak); + background: var(--profile); } .link { - color: $text; + color: var(--text-light); text-decoration: none; &:hover { - color: $primary; + color: var(--primary); } } +.handle { + display: flex; + align-items: center; + justify-content: center; + padding: .5rem; + font-weight: bold; +} + +.favicon { + width: 1rem; + height: 1rem; + margin: 0 .5rem 0 0; + + & + .name { + padding: 0 1rem 0 0; + } +} + +.name { + flex-grow: 1; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + text-align: center; +} + .avatar-container { position: relative; } .avatar { - color: $shadow-weak; - background: $shadow-hint; + color: var(--darken-weak); + background: var(--darken-hint); height: 13rem; width: 100%; display: flex; @@ -123,12 +162,12 @@ export default { .avatar-fallback { max-height: 75%; max-width: 80%; - opacity: .1; + opacity: .5; } .details { - background: $shadow; - color: $text-contrast; + background: var(--darken); + color: var(--text-light); width: 100%; height: 1.75rem; display: flex; @@ -156,16 +195,6 @@ export default { } .age-then { - color: $highlight; -} - -.name { - display: block; - padding: .5rem; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - text-align: center; - font-weight: bold; + color: var(--lighten); } diff --git a/assets/components/tile/release.vue b/assets/components/tile/release.vue index 94c0095f..bd7d11d0 100644 --- a/assets/components/tile/release.vue +++ b/assets/components/tile/release.vue @@ -216,9 +216,8 @@ export default { object-fit: cover; background-position: center; background-size: cover; - background-color: var(--shadow-hint); + background-color: var(--darken-hint); color: var(--shadow); - text-shadow: 1px 1px 0 var(--highlight); } .row { diff --git a/assets/components/tile/tag.vue b/assets/components/tile/tag.vue index 9d636973..2e094c7d 100644 --- a/assets/components/tile/tag.vue +++ b/assets/components/tile/tag.vue @@ -4,14 +4,14 @@ :title="tag.name" class="tile" > + {{ tag.name }} + - - {{ tag.name }} @@ -44,6 +44,7 @@ export default { align-items: left; justify-content: flex-end; box-sizing: border-box; + position: relative; text-align: center; text-decoration: none; box-shadow: inset 0 0 3px var(--darken); @@ -51,16 +52,23 @@ export default { .poster { width: 100%; - height: 14rem; + height: 16rem; object-fit: cover; - box-shadow: 0 0 3px var(--darken); + object-position: 50% 100%; + box-shadow: 0 0 1px var(--darken); } .title { + width: 100%; display: flex; - font-size: 1rem; + box-sizing: border-box; padding: .5rem 1rem; + position: absolute; + bottom: 0; + background: var(--darken); + font-size: 1rem; font-weight: bold; text-transform: capitalize; + text-shadow: 0 0 3px var(--darken-strong); } diff --git a/assets/css/style.scss b/assets/css/style.scss index f36c5732..6519319b 100644 --- a/assets/css/style.scss +++ b/assets/css/style.scss @@ -9,7 +9,6 @@ body { } body { - color: var(--text); margin: 0; font-family: Arial, Helvetica, sans-serif; } diff --git a/assets/js/actors/actions.js b/assets/js/actors/actions.js index 89b09e5b..25c06e13 100644 --- a/assets/js/actors/actions.js +++ b/assets/js/actors/actions.js @@ -77,6 +77,11 @@ function initActorActions(store, _router) { tattoos piercings description + network { + id + name + slug + } avatar: actorsAvatarByActorId { media { thumbnail @@ -222,6 +227,11 @@ function initActorActions(store, _router) { age birthdate gender + network { + id + name + slug + } avatar: actorsAvatarByActorId { media { thumbnail diff --git a/assets/js/fragments.js b/assets/js/fragments.js index 88b0a1a8..79351efc 100644 --- a/assets/js/fragments.js +++ b/assets/js/fragments.js @@ -37,6 +37,11 @@ const actorFields = ` birthdate age gender + network { + id + name + slug + } originCountry: countryByBirthCountryAlpha2 { alpha2 name diff --git a/migrations/20190325001339_releases.js b/migrations/20190325001339_releases.js index 321a332e..57a6e966 100644 --- a/migrations/20190325001339_releases.js +++ b/migrations/20190325001339_releases.js @@ -247,7 +247,6 @@ exports.up = knex => Promise.resolve() table.increments('id', 12); table.string('name') - .unique() .notNullable(); table.string('slug', 32) @@ -261,17 +260,6 @@ exports.up = knex => Promise.resolve() .references('id') .inTable('networks'); - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('actors_profiles', (table) => { - table.increments('id', 12); - - table.integer('actor_id') - .references('id') - .inTable('actors') - .notNullable(); - table.date('birthdate'); table.string('gender', 18); table.text('description'); @@ -307,6 +295,13 @@ exports.up = knex => Promise.resolve() table.datetime('created_at') .defaultTo(knex.fn.now()); + })) + .then(() => knex.raw('CREATE TABLE actors_profiles AS TABLE actors WITH NO DATA;')) + .then(() => knex.schema.alterTable('actors_profiles', (table) => { + table.integer('actor_id') + .references('id') + .inTable('actors') + .notNullable(); table.datetime('scraped_at'); table.boolean('scrape_success'); @@ -323,7 +318,9 @@ exports.up = knex => Promise.resolve() { slug: 'face', name: 'face' }, { slug: 'scalp', name: 'scalp' }, { slug: 'forehead', name: 'forehead' }, + { slug: 'temple', name: 'temple' }, { slug: 'cheek', name: 'cheek' }, + { slug: 'jaw', name: 'jaw' }, { slug: 'chin', name: 'chin' }, { slug: 'neck', name: 'neck' }, { slug: 'throat', name: 'throat' }, @@ -337,6 +334,9 @@ exports.up = knex => Promise.resolve() { slug: 'upper-lip', name: 'upper lip' }, { slug: 'lower-lip', name: 'lower lip' }, { slug: 'inner-lip', name: 'inner lip' }, + { slug: 'inner-lower-lip', name: 'inner lower lip' }, + { slug: 'inner-upper-lip', name: 'inner upper lip' }, + { slug: 'philtrum', name: 'philtrum' }, { slug: 'above-lip', name: 'above lip' }, { slug: 'below-lip', name: 'below lip' }, // nose @@ -364,7 +364,7 @@ exports.up = knex => Promise.resolve() // hands { slug: 'hand', name: 'hand' }, { slug: 'fingers', name: 'fingers' }, - { slug: 'knuckles', name: 'knucles' }, + { slug: 'knuckles', name: 'knuckles' }, { slug: 'thumb', name: 'thumb' }, { slug: 'index-finger', name: 'index finger' }, { slug: 'middle-finger', name: 'middle finger' }, @@ -378,11 +378,14 @@ exports.up = knex => Promise.resolve() { slug: 'collarbone', name: 'collarbone' }, { slug: 'chest', name: 'chest' }, { slug: 'rib-cage', name: 'rib cage' }, + { slug: 'breastbone', name: 'breastbone' }, { slug: 'underboob', name: 'underboob' }, + { slug: 'sideboob', name: 'sideboob' }, { slug: 'boob', name: 'boob' }, { slug: 'nipple', name: 'nipple' }, { slug: 'abdomen', name: 'abdomen' }, - { slug: 'lower-abdomen', name: 'lower abdomen' }, + { slug: 'navel', name: 'navel' }, + { slug: 'pelvis', name: 'pelvis' }, // back { slug: 'back', name: 'back' }, { slug: 'upper-back', name: 'upper back' }, @@ -392,9 +395,9 @@ exports.up = knex => Promise.resolve() // bottom { slug: 'butt', name: 'butt' }, { slug: 'hip', name: 'hip' }, + { slug: 'anus', name: 'anus' }, // genitals { slug: 'pubic-mound', name: 'pubic mound' }, - { slug: 'anus', name: 'anus' }, { slug: 'vagina', name: 'vagina' }, { slug: 'outer-labia', name: 'outer labia' }, { slug: 'inner-labia', name: 'inner labia' }, @@ -748,8 +751,8 @@ exports.up = knex => Promise.resolve() COMMENT ON VIEW movie_actors IS E'@foreignKey (movie_id) references releases (id)\n@foreignKey (actor_id) references actors (id)'; COMMENT ON VIEW movie_tags IS E'@foreignKey (movie_id) references releases (id)\n@foreignKey (tag_id) references tags (id)'; - COMMENT ON COLUMN actors_profiles.height IS E'@omit read,update,create,delete,all,many'; - COMMENT ON COLUMN actors_profiles.weight IS E'@omit read,update,create,delete,all,many'; + COMMENT ON COLUMN actors.height IS E'@omit read,update,create,delete,all,many'; + COMMENT ON COLUMN actors.weight IS E'@omit read,update,create,delete,all,many'; `)); exports.down = knex => knex.raw(` diff --git a/public/img/logos/digitalplayground/digitalplayground.png b/public/img/logos/digitalplayground/digitalplayground.png index 40641763..7751c88b 100644 Binary files a/public/img/logos/digitalplayground/digitalplayground.png and b/public/img/logos/digitalplayground/digitalplayground.png differ diff --git a/public/img/logos/digitalplayground/dpparodies.png b/public/img/logos/digitalplayground/dpparodies.png new file mode 100644 index 00000000..daedaa4b Binary files /dev/null and b/public/img/logos/digitalplayground/dpparodies.png differ diff --git a/public/img/logos/digitalplayground/dpstarepisodes.png b/public/img/logos/digitalplayground/dpstarepisodes.png new file mode 100644 index 00000000..f3e1416c Binary files /dev/null and b/public/img/logos/digitalplayground/dpstarepisodes.png differ diff --git a/public/img/logos/digitalplayground/favicon.png b/public/img/logos/digitalplayground/favicon.png index 9741de31..51afab7f 100644 Binary files a/public/img/logos/digitalplayground/favicon.png and b/public/img/logos/digitalplayground/favicon.png differ diff --git a/public/img/logos/digitalplayground/flixxx.png b/public/img/logos/digitalplayground/flixxx.png new file mode 100644 index 00000000..85cc4919 Binary files /dev/null and b/public/img/logos/digitalplayground/flixxx.png differ diff --git a/public/img/logos/digitalplayground/misc/dp-flixxx.png b/public/img/logos/digitalplayground/misc/dp-flixxx.png new file mode 100644 index 00000000..22a7e216 Binary files /dev/null and b/public/img/logos/digitalplayground/misc/dp-flixxx.png differ diff --git a/public/img/logos/digitalplayground/misc/dp-parodies.png b/public/img/logos/digitalplayground/misc/dp-parodies.png new file mode 100644 index 00000000..64cfa8b1 Binary files /dev/null and b/public/img/logos/digitalplayground/misc/dp-parodies.png differ diff --git a/public/img/logos/digitalplayground/misc/flixxx_square.png b/public/img/logos/digitalplayground/misc/flixxx_square.png new file mode 100644 index 00000000..3c990a91 Binary files /dev/null and b/public/img/logos/digitalplayground/misc/flixxx_square.png differ diff --git a/public/img/logos/digitalplayground/network.png b/public/img/logos/digitalplayground/network.png index 40641763..5d448324 100644 Binary files a/public/img/logos/digitalplayground/network.png and b/public/img/logos/digitalplayground/network.png differ diff --git a/public/img/logos/digitalplayground/rawcut.png b/public/img/logos/digitalplayground/rawcut.png new file mode 100644 index 00000000..e2f2ec62 Binary files /dev/null and b/public/img/logos/digitalplayground/rawcut.png differ diff --git a/src/actors.js b/src/actors.js index 373bb209..fbf6aaeb 100644 --- a/src/actors.js +++ b/src/actors.js @@ -1,57 +1,74 @@ 'use strict'; +const logger = require('./logger')(__filename); const knex = require('./knex'); const slugify = require('./utils/slugify'); const capitalize = require('./utils/capitalize'); function toBaseActors(actorsOrNames, release) { return actorsOrNames.map((actorOrName) => { + const name = capitalize(actorOrName.name || actorOrName); + const slug = slugify(name); + + const baseActor = { + name, + slug, + hasSingleName: name.split(/\s+/).length === 1, + network: release.site.network, + slugWithNetworkSlug: `${slug}-${release.site.network.slug}`, + }; + if (actorOrName.name) { return { ...actorOrName, - name: capitalize(actorOrName.name), - slug: slugify(actorOrName.name), - networkId: release.site.network.id, + ...baseActor, }; } - return { - name: capitalize(actorOrName), - slug: slugify(actorOrName), - networkId: release.site.network.id, - }; + return baseActor; }); } function curateActorEntry(baseActor) { - const actorEntry = { + if (baseActor.hasSingleName) { + logger.warn(`Assigning single name actor '${baseActor.name}' to network '${baseActor.network.name}'`); + + // attach network ID to allow separating actors with the same name + return { + name: baseActor.name, + slug: baseActor.slugWithNetworkSlug, + network_id: baseActor.network.id, + }; + } + + return { name: baseActor.name, slug: baseActor.slug, }; - - if (baseActor.name.split(/\s+/).length === 1) { - // attach network ID for single names, to reduce mismatches - actorEntry.network_id = baseActor.networkId; - } - - return actorEntry; } function curateActorEntries(baseActors) { return baseActors.map(baseActor => curateActorEntry(baseActor)); } -async function getActors(baseActors) { +async function getOrCreateActors(baseActors) { const existingActors = await knex('actors') + .select('id', 'name', 'slug', 'network_id') .whereIn('slug', baseActors.map(baseActor => baseActor.slug)) - .orWhereIn('name', baseActors.map(baseActor => baseActor.slug)); + .whereNull('network_id') + .orWhereIn(['slug', 'network_id'], baseActors.map(baseActor => [baseActor.slugWithNetworkSlug, baseActor.network.id])); - if (existingActors.length === 0) { - // TODO: TESTING ONLY - await knex('actors').insert(curateActorEntries(baseActors.slice(0, 3))); + const existingActorSlugs = new Set(existingActors.map(actor => actor.slug)); + const uniqueBaseActors = baseActors.filter(baseActor => !existingActorSlugs.has(baseActor.slug) && !existingActorSlugs.has(baseActor.slugWithNetworkSlug)); + + const curatedActorEntries = curateActorEntries(uniqueBaseActors); + const newActors = await knex('actors').insert(curatedActorEntries, ['id', 'name', 'slug', 'network_id']); + + if (Array.isArray(newActors)) { + return newActors.concat(existingActors); } - console.log(existingActors); + return existingActors; } async function associateActors(releases) { @@ -67,9 +84,18 @@ async function associateActors(releases) { const baseActorsBySlug = baseActors.reduce((acc, baseActor) => ({ ...acc, [baseActor.slug]: baseActor }), {}); const uniqueBaseActors = Object.values(baseActorsBySlug); - const actors = await getActors(uniqueBaseActors); + const actors = await getOrCreateActors(uniqueBaseActors); + const actorIdsBySlug = actors.reduce((acc, actor) => ({ ...acc, [actor.slug]: actor.id }), {}); - console.log(actors); + const releaseActorAssociations = Object.entries(baseActorsByReleaseId) + .map(([releaseId, releaseActors]) => releaseActors + .map(releaseActor => ({ + release_id: releaseId, + actor_id: actorIdsBySlug[releaseActor.slug] || actorIdsBySlug[releaseActor.slugWithNetworkSlug], + }))) + .flat(); + + await knex.raw(`${knex('releases_actors').insert(releaseActorAssociations).toString()} ON CONFLICT DO NOTHING;`); } module.exports = { diff --git a/src/web/plugins/actors.js b/src/web/plugins/actors.js index 9a851a3f..9899dce5 100644 --- a/src/web/plugins/actors.js +++ b/src/web/plugins/actors.js @@ -11,14 +11,14 @@ const schemaExtender = makeExtendSchemaPlugin(_build => ({ IMPERIAL } - extend type ActorProfile { + extend type Actor { age: Int @requires(columns: ["birthdate"]) height(units:Units): String @requires(columns: ["height"]) weight(units:Units): String @requires(columns: ["weight"]) } `, resolvers: { - ActorProfile: { + Actor: { age(parent, _args, _context, _info) { if (!parent.birthdate) return null;