diff --git a/assets/components/actors/actor.vue b/assets/components/actors/actor.vue
index 231de1c0..3f983288 100644
--- a/assets/components/actors/actor.vue
+++ b/assets/components/actors/actor.vue
@@ -44,7 +44,7 @@
class="avatar-link"
>
@@ -153,7 +153,7 @@
{{ actor.bust || '??' }}{{ actor.cup || '?' }}-{{ actor.waist || '??' }}-{{ actor.hip || '??' }}
@@ -271,6 +271,10 @@ async function fetchActor() {
});
}
+function sfw() {
+ return this.$store.state.ui.sfw;
+}
+
async function route() {
await this.fetchActor();
}
@@ -303,6 +307,9 @@ export default {
expanded: false,
};
},
+ computed: {
+ sfw,
+ },
watch: {
$route: route,
},
@@ -495,6 +502,7 @@ export default {
.enhanced.icon {
fill: $primary;
padding: 0 .5rem;
+ transform: scaleX(-1);
}
.ethnicity {
diff --git a/assets/components/actors/actors.vue b/assets/components/actors/actors.vue
index 260eedb3..6e58a500 100644
--- a/assets/components/actors/actors.vue
+++ b/assets/components/actors/actors.vue
@@ -1,60 +1,60 @@
-
-
@@ -226,7 +226,7 @@ export default {
}
}
-@media(max-width: $breakpoint) {
+@media(max-width: $breakpoint0) {
.tiles {
grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
}
diff --git a/assets/components/actors/photos.vue b/assets/components/actors/photos.vue
index af54d184..c10226c0 100644
--- a/assets/components/actors/photos.vue
+++ b/assets/components/actors/photos.vue
@@ -11,7 +11,7 @@
class="avatar-link photo-link"
>
@@ -26,7 +26,7 @@
class="photo-link"
>
@@ -35,6 +35,10 @@
diff --git a/assets/components/header/header.vue b/assets/components/header/header.vue
index 95d4e867..931767a0 100644
--- a/assets/components/header/header.vue
+++ b/assets/components/header/header.vue
@@ -1,128 +1,128 @@
-
diff --git a/assets/components/header/search.vue b/assets/components/header/search.vue
index 8d14a1bd..cc01941c 100644
--- a/assets/components/header/search.vue
+++ b/assets/components/header/search.vue
@@ -1,57 +1,64 @@
-
+
diff --git a/assets/components/releases/releases.vue b/assets/components/releases/releases.vue
index ba33be91..979dffb3 100644
--- a/assets/components/releases/releases.vue
+++ b/assets/components/releases/releases.vue
@@ -1,72 +1,72 @@
-
-
{{ range }} releases for '{{ context }}'
+
+
{{ range }} releases for '{{ context }}'
-
+
-
No {{ range }} releases
+
No {{ range }} releases
-
No recent or upcoming releases
-
+
No recent or upcoming releases
+
diff --git a/assets/components/search/search.vue b/assets/components/search/search.vue
index 10f70ce9..4aae2bbb 100644
--- a/assets/components/search/search.vue
+++ b/assets/components/search/search.vue
@@ -1,47 +1,89 @@
-
-
Found {{ releases.length }} results for '{{ query }}'
+
+
Searching...
-
Searching...
+
+ Found {{ actors.length }} actors for '{{ query }}'
-
-
+
+
+
+
+ Found {{ releases.length }} releases for '{{ query }}'
+
+
+
+
@@ -54,4 +96,12 @@ export default {
color: $shadow;
font-weight: bold;
}
+
+.tiles {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
+ grid-gap: 0 .5rem;
+ flex-grow: 1;
+ margin: 0 0 1rem 0;
+}
diff --git a/assets/components/tile/actor.vue b/assets/components/tile/actor.vue
index 4f568900..714da5df 100644
--- a/assets/components/tile/actor.vue
+++ b/assets/components/tile/actor.vue
@@ -8,29 +8,37 @@
class="link"
>
- {{ actor.name }}
+
+
- {{ actor.name }}
-
+
+
-
- {{ actor.name }}
+
@@ -45,7 +53,9 @@
-
+
+
+
{{ actor.ageThen }}
-
-
import Gender from '../actors/gender.vue';
+function sfw() {
+ return this.$store.state.ui.sfw;
+}
+
export default {
components: {
Gender,
@@ -95,6 +107,13 @@ export default {
type: Object,
default: null,
},
+ alias: {
+ type: Object,
+ default: null,
+ },
+ },
+ computed: {
+ sfw,
},
};
@@ -137,18 +156,29 @@ export default {
display: flex;
align-items: center;
justify-content: center;
- padding: .5rem;
font-weight: bold;
+
+ .name {
+ padding: .5rem;
+ }
+
+ .alias {
+ fill: var(--highlight);
+ }
}
.favicon {
+ font-size: 0;
+ padding: .5rem .25rem;
+
+ &:last-child {
+ padding: .5rem;
+ }
+}
+
+.favicon-icon {
width: 1rem;
height: 1rem;
- margin: 0 .5rem 0 0;
-
- & + .name {
- padding: 0 1rem 0 0;
- }
}
.name {
@@ -156,13 +186,13 @@ export default {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
- text-align: center;
}
.avatar-container {
display: flex;
flex-grow: 1;
position: relative;
+ overflow: hidden;
}
.avatar {
@@ -191,6 +221,7 @@ export default {
height: 1.75rem;
display: flex;
align-items: center;
+ justify-content: space-between;
box-sizing: border-box;
padding: .5rem;
position: absolute;
@@ -199,14 +230,13 @@ export default {
font-weight: bold;
}
-.age,
-.country,
-.gender {
- flex: 1;
+.gender-age {
+ display: flex;
+ align-items: center;
}
.gender {
- text-align: center;
+ margin: .25rem .25rem 0 0;
}
.country {
@@ -216,7 +246,7 @@ export default {
}
.flag {
- height: 1rem;
+ height: .75rem;
margin: 0 0 0 .5rem;
}
diff --git a/assets/components/tile/release.vue b/assets/components/tile/release.vue
index b61f8824..281ff4a3 100644
--- a/assets/components/tile/release.vue
+++ b/assets/components/tile/release.vue
@@ -1,175 +1,175 @@
-
-
-
- {{ release.network.name }}
+
+
diff --git a/assets/components/tile/tag.vue b/assets/components/tile/tag.vue
index 885d4daf..82ce165b 100644
--- a/assets/components/tile/tag.vue
+++ b/assets/components/tile/tag.vue
@@ -1,68 +1,68 @@
-
- {{ tag.name }}
+
+ {{ tag.name }}
-
-
+
+
-
+
-
+
-
-
-
+
+
+
diff --git a/assets/img/icons/boobjob.svg b/assets/img/icons/boobjob.svg
new file mode 100644
index 00000000..e4db8562
--- /dev/null
+++ b/assets/img/icons/boobjob.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/img/icons/forward.svg b/assets/img/icons/forward.svg
new file mode 100644
index 00000000..69d294d5
--- /dev/null
+++ b/assets/img/icons/forward.svg
@@ -0,0 +1,5 @@
+
+
diff --git a/assets/img/icons/redo2.svg b/assets/img/icons/redo2.svg
new file mode 100644
index 00000000..becaa9b8
--- /dev/null
+++ b/assets/img/icons/redo2.svg
@@ -0,0 +1,5 @@
+
+
diff --git a/assets/img/icons/reply-all.svg b/assets/img/icons/reply-all.svg
new file mode 100644
index 00000000..f5e3581e
--- /dev/null
+++ b/assets/img/icons/reply-all.svg
@@ -0,0 +1,6 @@
+
+
diff --git a/assets/img/icons/reply.svg b/assets/img/icons/reply.svg
new file mode 100644
index 00000000..94db7312
--- /dev/null
+++ b/assets/img/icons/reply.svg
@@ -0,0 +1,5 @@
+
+
diff --git a/assets/img/icons/undo2.svg b/assets/img/icons/undo2.svg
new file mode 100644
index 00000000..a1135fe8
--- /dev/null
+++ b/assets/img/icons/undo2.svg
@@ -0,0 +1,5 @@
+
+
diff --git a/assets/img/icons/users2.svg b/assets/img/icons/users2.svg
new file mode 100644
index 00000000..80900d73
--- /dev/null
+++ b/assets/img/icons/users2.svg
@@ -0,0 +1,31 @@
+
+
diff --git a/assets/img/icons/users3.svg b/assets/img/icons/users3.svg
new file mode 100644
index 00000000..ec2d3cec
--- /dev/null
+++ b/assets/img/icons/users3.svg
@@ -0,0 +1,6 @@
+
+
diff --git a/assets/js/actors/actions.js b/assets/js/actors/actions.js
index 47a61e6e..2350052c 100644
--- a/assets/js/actors/actions.js
+++ b/assets/js/actors/actions.js
@@ -4,53 +4,9 @@ import {
releaseActorsFragment,
releaseTagsFragment,
} from '../fragments';
-import { curateRelease } from '../curate';
+import { curateActor, curateRelease } from '../curate';
import getDateRange from '../get-date-range';
-function curateActor(actor) {
- if (!actor) {
- return null;
- }
-
- const curatedActor = {
- ...actor,
- height: actor.heightMetric && {
- metric: actor.heightMetric,
- imperial: actor.heightImperial,
- },
- weight: actor.weightMetric && {
- metric: actor.weightMetric,
- imperial: actor.weightImperial,
- },
- origin: actor.birthCountry && {
- city: actor.birthCity,
- state: actor.birthState,
- country: actor.birthCountry,
- },
- residence: actor.residenceCountry && {
- city: actor.residenceCity,
- state: actor.residenceState,
- country: actor.residenceCountry,
- },
- scrapedAt: new Date(actor.createdAt),
- updatedAt: new Date(actor.updatedAt),
- };
-
- if (actor.profiles && actor.profiles.length > 0) {
- const photos = actor.profiles
- .map(profile => profile.avatar)
- .filter(avatar => avatar && (!curatedActor.avatar || avatar.hash !== curatedActor.avatar.hash));
-
- curatedActor.photos = Object.values(photos.reduce((acc, photo) => ({ ...acc, [photo.hash]: photo }), {}));
- }
-
- if (actor.releases) {
- curatedActor.releases = actor.releases.map(release => curateRelease(release.release));
- }
-
- return curatedActor;
-}
-
function initActorActions(store, _router) {
async function fetchActorBySlug({ _commit }, { actorSlug, limit = 100, range = 'latest' }) {
const { before, after, orderBy } = getDateRange(range);
@@ -110,6 +66,12 @@ function initActorActions(store, _router) {
hash
comment
copyright
+ sfw: sfwMedia {
+ id
+ thumbnail
+ path
+ comment
+ }
}
profiles: actorsProfiles {
description
@@ -121,6 +83,12 @@ function initActorActions(store, _router) {
hash
comment
copyright
+ sfw: sfwMedia {
+ id
+ thumbnail
+ path
+ comment
+ }
}
}
birthCity
@@ -202,7 +170,7 @@ function initActorActions(store, _router) {
exclude: store.state.ui.filter,
});
- return curateActor(actor);
+ return curateActor(actor, null, curateRelease);
}
async function fetchActors({ _commit }, {
@@ -223,13 +191,16 @@ function initActorActions(store, _router) {
first:$limit,
orderBy: NAME_ASC,
filter: {
+ aliasFor: {
+ isNull: true
+ }
name: {
startsWith: $letter
- },
+ }
gender: {
${genderFilter}
- },
- },
+ }
+ }
) {
id
name
@@ -249,17 +220,13 @@ function initActorActions(store, _router) {
lazy
comment
copyright
+ sfw: sfwMedia {
+ id
+ thumbnail
+ path
+ comment
+ }
}
- actorsProfiles {
- actorsAvatarByProfileId {
- media {
- id
- path
- thumbnail
- copyright
- }
- }
- }
birthCountry: countryByBirthCountryAlpha2 {
alpha2
name
diff --git a/assets/js/curate.js b/assets/js/curate.js
index 22c2dc48..ebb7666b 100644
--- a/assets/js/curate.js
+++ b/assets/js/curate.js
@@ -1,17 +1,50 @@
import dayjs from 'dayjs';
-function curateActor(actor, release) {
+function curateActor(actor, release, curateActorRelease) {
+ if (!actor) {
+ return null;
+ }
+
const curatedActor = {
...actor,
- origin: actor.originCountry && {
- country: actor.originCountry,
+ height: actor.heightMetric && {
+ metric: actor.heightMetric,
+ imperial: actor.heightImperial,
},
+ weight: actor.weightMetric && {
+ metric: actor.weightMetric,
+ imperial: actor.weightImperial,
+ },
+ origin: actor.birthCountry && {
+ city: actor.birthCity,
+ state: actor.birthState,
+ country: actor.birthCountry,
+ },
+ residence: actor.residenceCountry && {
+ city: actor.residenceCity,
+ state: actor.residenceState,
+ country: actor.residenceCountry,
+ },
+ scrapedAt: new Date(actor.createdAt),
+ updatedAt: new Date(actor.updatedAt),
};
+ if (actor.profiles && actor.profiles.length > 0) {
+ const photos = actor.profiles
+ .map(profile => profile.avatar)
+ .filter(avatar => avatar && (!curatedActor.avatar || avatar.hash !== curatedActor.avatar.hash));
+
+ curatedActor.photos = Object.values(photos.reduce((acc, photo) => ({ ...acc, [photo.hash]: photo }), {}));
+ }
+
if (release && release.date && curatedActor.birthdate) {
curatedActor.ageThen = dayjs(release.date).diff(actor.birthdate, 'year');
}
+ if (actor.releases) {
+ curatedActor.releases = actor.releases.map(actorRelease => curateActorRelease(actorRelease.release));
+ }
+
return curatedActor;
}
diff --git a/assets/js/releases/actions.js b/assets/js/releases/actions.js
index 11054a96..9ea26acd 100644
--- a/assets/js/releases/actions.js
+++ b/assets/js/releases/actions.js
@@ -28,75 +28,6 @@ function initReleasesActions(store, _router) {
return releases.map(release => curateRelease(release));
}
- async function searchReleases({ _commit }, { query, limit = 20 }) {
- const res = await graphql(`
- query SearchReleases(
- $query: String!
- $limit: Int = 20
- ) {
- releases: searchReleases(
- query: $query
- first: $limit
- ) {
- id
- title
- slug
- date
- url
- type
- isNew
- site {
- id
- slug
- name
- url
- network {
- id
- slug
- name
- url
- }
- }
- actors: releasesActors {
- actor {
- id
- slug
- name
- }
- }
- tags: releasesTags(orderBy: TAG_BY_TAG_ID__PRIORITY_DESC) {
- tag {
- id
- name
- slug
- }
- }
- poster: releasesPosterByReleaseId {
- media {
- id
- thumbnail
- lazy
- }
- }
- covers: releasesCovers {
- media {
- id
- thumbnail
- lazy
- }
- }
- }
- }
- `, {
- query,
- limit,
- });
-
- if (!res) return [];
-
- return res.releases.map(release => curateRelease(release));
- }
-
async function fetchReleaseById({ _commit }, releaseId) {
// const release = await get(`/releases/${releaseId}`);
@@ -114,7 +45,6 @@ function initReleasesActions(store, _router) {
return {
fetchReleases,
fetchReleaseById,
- searchReleases,
};
}
diff --git a/assets/js/ui/actions.js b/assets/js/ui/actions.js
index a53d2b3a..5364b7eb 100644
--- a/assets/js/ui/actions.js
+++ b/assets/js/ui/actions.js
@@ -1,3 +1,6 @@
+import { graphql } from '../api';
+import { curateRelease, curateActor } from '../curate';
+
function initUiActions(_store, _router) {
function setFilter({ commit }, filter) {
commit('setFilter', filter);
@@ -23,7 +26,133 @@ function initUiActions(_store, _router) {
localStorage.setItem('sfw', sfw);
}
+ async function search({ _commit }, { query, limit = 20 }) {
+ const res = await graphql(`
+ query SearchReleases(
+ $query: String!
+ $limit: Int = 20
+ ) {
+ releases: searchReleases(
+ query: $query
+ first: $limit
+ ) {
+ id
+ title
+ slug
+ date
+ url
+ type
+ isNew
+ site {
+ id
+ slug
+ name
+ url
+ network {
+ id
+ slug
+ name
+ url
+ }
+ }
+ actors: releasesActors {
+ actor {
+ id
+ slug
+ name
+ }
+ }
+ tags: releasesTags(orderBy: TAG_BY_TAG_ID__PRIORITY_DESC) {
+ tag {
+ id
+ name
+ slug
+ }
+ }
+ poster: releasesPosterByReleaseId {
+ media {
+ id
+ thumbnail
+ lazy
+ }
+ }
+ covers: releasesCovers {
+ media {
+ id
+ thumbnail
+ lazy
+ }
+ }
+ }
+ actors: searchActors(
+ search: $query,
+ first: $limit
+ ) {
+ id
+ name
+ slug
+ age
+ dateOfBirth
+ gender
+ aliasFor: actorByAliasFor {
+ id
+ name
+ slug
+ age
+ dateOfBirth
+ gender
+ network {
+ id
+ name
+ slug
+ }
+ avatar: avatarMedia {
+ id
+ path
+ thumbnail
+ lazy
+ comment
+ copyright
+ }
+ birthCountry: countryByBirthCountryAlpha2 {
+ alpha2
+ name
+ alias
+ }
+ }
+ network {
+ id
+ name
+ slug
+ }
+ avatar: avatarMedia {
+ id
+ path
+ thumbnail
+ lazy
+ comment
+ copyright
+ }
+ birthCountry: countryByBirthCountryAlpha2 {
+ alpha2
+ name
+ alias
+ }
+ }
+ }
+ `, {
+ query,
+ limit,
+ });
+
+ return {
+ releases: res.releases.map(release => curateRelease(release)),
+ actors: res.actors.map(actor => curateActor(actor)),
+ };
+ }
+
return {
+ search,
setFilter,
setRange,
setBatch,
diff --git a/migrations/20190325001339_releases.js b/migrations/20190325001339_releases.js
index 91b8ede5..13ce67e8 100644
--- a/migrations/20190325001339_releases.js
+++ b/migrations/20190325001339_releases.js
@@ -270,8 +270,6 @@ exports.up = knex => Promise.resolve()
.references('id')
.inTable('networks');
- table.unique(['slug', 'network_id']);
-
table.integer('alias_for', 12)
.references('id')
.inTable('actors');
@@ -794,6 +792,9 @@ exports.up = knex => Promise.resolve()
ALTER TABLE releases_search
ADD COLUMN document tsvector;
+ CREATE UNIQUE INDEX unique_actor_slugs_network ON actors (slug, network_id);
+ CREATE UNIQUE INDEX unique_actor_slugs ON actors (slug, (network_id IS NULL));
+
CREATE TEXT SEARCH DICTIONARY traxxx_dict (
TEMPLATE = pg_catalog.simple,
stopwords = traxxx
@@ -825,6 +826,12 @@ exports.up = knex => Promise.resolve()
url ILIKE ('%' || search || '%')
$$ LANGUAGE SQL STABLE;
+ CREATE FUNCTION search_actors(search text, min_length numeric DEFAULT 2) RETURNS SETOF actors AS $$
+ SELECT * FROM actors
+ WHERE length(search) >= min_length
+ AND name ILIKE ('%' || search || '%')
+ $$ LANGUAGE SQL STABLE;
+
CREATE FUNCTION releases_is_new(release releases) RETURNS boolean AS $$
SELECT NOT EXISTS(SELECT true FROM batches WHERE batches.id = release.created_batch_id + 1 LIMIT 1);
$$ LANGUAGE sql STABLE;
@@ -894,8 +901,8 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
DROP TABLE IF EXISTS countries CASCADE;
DROP TABLE IF EXISTS networks CASCADE;
- DROP FUNCTION IF EXISTS releases_by_tag_slugs;
DROP FUNCTION IF EXISTS search_sites;
+ DROP FUNCTION IF EXISTS search_actors;
DROP FUNCTION IF EXISTS get_random_sfw_media_id;
DROP TEXT SEARCH CONFIGURATION IF EXISTS traxxx;
diff --git a/public/img/logos/penthouse/misc/penthouse_dark.png b/public/img/logos/penthouse/misc/penthouse_dark.png
new file mode 100644
index 00000000..a2b225ff
Binary files /dev/null and b/public/img/logos/penthouse/misc/penthouse_dark.png differ
diff --git a/public/img/logos/penthouse/penthouse.png b/public/img/logos/penthouse/penthouse.png
new file mode 100644
index 00000000..b4427e0a
Binary files /dev/null and b/public/img/logos/penthouse/penthouse.png differ
diff --git a/seeds/04_media.js b/seeds/04_media.js
index e38931e4..c5eb17cc 100644
--- a/seeds/04_media.js
+++ b/seeds/04_media.js
@@ -357,7 +357,6 @@ const sfw = Object.entries({
['NI_fJ15rIfI', 'Szabo Viktor'],
['LymVMRIUwPQ', 'Happy Films'],
['mrNVnLEphdo', 'Greg Nunes'],
- ['FKvoEKSV2LY', 'zhou yu'],
['CKLF34baCTQ', 'Willian Justen de Vasconcellos'],
['7uGCN9qshsY', 'Siora Photography'],
['xBTnaTgleQE', 'Glen Carrie'],
diff --git a/src/actors.js b/src/actors.js
index ca0953e3..d885f7a0 100644
--- a/src/actors.js
+++ b/src/actors.js
@@ -127,6 +127,10 @@ function curateProfileEntry(profile) {
}
async function curateProfile(profile) {
+ if (!profile) {
+ return null;
+ }
+
try {
const curatedProfile = {
id: profile.id,
@@ -161,7 +165,7 @@ async function curateProfile(profile) {
curatedProfile.dateOfDeath = Number.isNaN(Number(profile.dateOfDeath)) ? null : profile.dateOfDeath;
- curatedProfile.cup = profile.cup || profile.bust?.match(/[a-zA-Z]+/)?.[0] || null;
+ curatedProfile.cup = profile.cup || (typeof profile.bust === 'string' && profile.bust?.match(/[a-zA-Z]+/)?.[0]) || null;
curatedProfile.bust = Number(profile.bust) || profile.bust?.match(/\d+/)?.[0] || null;
curatedProfile.waist = Number(profile.waist) || profile.waist?.match(/\d+/)?.[0] || null;
curatedProfile.hip = Number(profile.hip) || profile.hip?.match(/\d+/)?.[0] || null;
@@ -257,6 +261,7 @@ async function interpolateProfiles(actors) {
profile.date_of_birth = getMostFrequentDate(valuesByProperty.date_of_birth);
profile.date_of_death = getMostFrequentDate(valuesByProperty.date_of_death);
+ // TODO: fix city, state and country not matching
profile.birth_city = getMostFrequent(valuesByProperty.birth_city);
profile.birth_state = getMostFrequent(valuesByProperty.birth_state);
profile.birth_country_alpha2 = getMostFrequent(valuesByProperty.birth_country_alpha2);
@@ -300,51 +305,6 @@ async function interpolateProfiles(actors) {
.catch(transaction.rollback);
}
-async function scrapeProfiles(actor, sources, networksBySlug, sitesBySlug) {
- const profiles = Promise.map(sources, async (source) => {
- try {
- return await [].concat(source).reduce(async (outcome, scraperSlug) => outcome.catch(async () => {
- const scraper = scrapers[scraperSlug];
- const siteOrNetwork = networksBySlug[scraperSlug] || sitesBySlug[scraperSlug];
-
- if (!scraper?.fetchProfile) {
- logger.warn(`No profile profile scraper available for ${scraperSlug}`);
- throw new Error(`No profile profile scraper available for ${scraperSlug}`);
- }
-
- if (!siteOrNetwork) {
- logger.warn(`No site or network found for ${scraperSlug}`);
- throw new Error(`No site or network found for ${scraperSlug}`);
- }
-
- logger.verbose(`Searching profile for '${actor.name}' on '${scraperSlug}'`);
-
- const profile = await scraper.fetchProfile(actor.name, scraperSlug, siteOrNetwork, include);
-
- if (!profile || typeof profile === 'number') { // scraper returns HTTP code on request failure
- logger.verbose(`Profile for '${actor.name}' not available on ${scraperSlug}, scraper returned ${profile}`);
- throw Object.assign(new Error(`Profile for '${actor.name}' not available on ${scraperSlug}`), { code: 'PROFILE_NOT_AVAILABLE' });
- }
-
- return {
- ...actor,
- ...profile,
- scraper: scraperSlug,
- site: siteOrNetwork,
- };
- }), Promise.reject(new Error()));
- } catch (error) {
- if (error.code !== 'PROFILE_NOT_AVAILABLE') {
- logger.error(`Failed to fetch profile for '${actor.name}': ${error.message}`);
- }
- }
-
- return null;
- });
-
- return profiles.filter(Boolean);
-}
-
async function upsertProfiles(profiles) {
const curatedProfileEntries = profiles.map(profile => curateProfileEntry(profile));
@@ -403,6 +363,51 @@ async function upsertProfiles(profiles) {
}
}
+async function scrapeProfiles(actor, sources, networksBySlug, sitesBySlug) {
+ const profiles = Promise.map(sources, async (source) => {
+ try {
+ return await [].concat(source).reduce(async (outcome, scraperSlug) => outcome.catch(async () => {
+ const scraper = scrapers[scraperSlug];
+ const siteOrNetwork = networksBySlug[scraperSlug] || sitesBySlug[scraperSlug];
+
+ if (!scraper?.fetchProfile) {
+ logger.warn(`No profile profile scraper available for ${scraperSlug}`);
+ throw new Error(`No profile profile scraper available for ${scraperSlug}`);
+ }
+
+ if (!siteOrNetwork) {
+ logger.warn(`No site or network found for ${scraperSlug}`);
+ throw new Error(`No site or network found for ${scraperSlug}`);
+ }
+
+ logger.verbose(`Searching profile for '${actor.name}' on '${scraperSlug}'`);
+
+ const profile = await scraper.fetchProfile(actor.name, scraperSlug, siteOrNetwork, include);
+
+ if (!profile || typeof profile === 'number') { // scraper returns HTTP code on request failure
+ logger.verbose(`Profile for '${actor.name}' not available on ${scraperSlug}, scraper returned ${profile}`);
+ throw Object.assign(new Error(`Profile for '${actor.name}' not available on ${scraperSlug}`), { code: 'PROFILE_NOT_AVAILABLE' });
+ }
+
+ return {
+ ...actor,
+ ...profile,
+ scraper: scraperSlug,
+ site: siteOrNetwork,
+ };
+ }), Promise.reject(new Error()));
+ } catch (error) {
+ if (error.code !== 'PROFILE_NOT_AVAILABLE') {
+ logger.error(`Failed to fetch profile for '${actor.name}': ${error.message}`);
+ }
+ }
+
+ return null;
+ });
+
+ return profiles.filter(Boolean);
+}
+
async function scrapeActors(actorNames) {
const baseActors = toBaseActors(actorNames);
@@ -438,7 +443,8 @@ async function scrapeActors(actorNames) {
{ concurrency: 10 },
);
- const profiles = await Promise.all(profilesPerActor.flat().map(profile => curateProfile(profile)));
+ const curatedProfiles = await Promise.all(profilesPerActor.flat().map(profile => curateProfile(profile)));
+ const profiles = curatedProfiles.filter(Boolean);
if (argv.inspect) {
console.log(profiles);
@@ -495,31 +501,25 @@ async function associateActors(releases, batchId) {
return null;
}
- const baseActorsBySlugAndNetworkId = baseActors.reduce((acc, baseActor) => ({
+ const baseActorsBySlug = baseActors.reduce((acc, baseActor) => ({
...acc,
- [baseActor.slug]: {
- ...acc[baseActor.slug],
- [baseActor.network.id]: baseActor,
- },
+ [baseActor.slug]: baseActor,
}), {});
- const uniqueBaseActors = Object.values(baseActorsBySlugAndNetworkId).map(baseActorsByNetworkId => Object.values(baseActorsByNetworkId)).flat();
+ const uniqueBaseActors = Object.values(baseActorsBySlug);
const actors = await getOrCreateActors(uniqueBaseActors, batchId);
- const actorIdsBySlugAndNetworkId = actors.reduce((acc, actor) => ({
+ const actorIdsBySlug = actors.reduce((acc, actor) => ({
...acc,
- [actor.network_id]: {
- ...acc[actor.network_id],
- [actor.slug]: actor.alias_for || actor.id,
- },
+ [actor.slug]: actor.alias_for || actor.id,
}), {});
const releaseActorAssociations = Object.entries(baseActorsByReleaseId)
.map(([releaseId, releaseActors]) => releaseActors
.map(releaseActor => ({
release_id: releaseId,
- actor_id: actorIdsBySlugAndNetworkId[releaseActor.network.id]?.[releaseActor.slug] || actorIdsBySlugAndNetworkId.null[releaseActor.slug],
+ actor_id: actorIdsBySlug[releaseActor.slug],
})))
.flat();
diff --git a/src/scrapers/bang.js b/src/scrapers/bang.js
index 7dc51fce..df8246da 100644
--- a/src/scrapers/bang.js
+++ b/src/scrapers/bang.js
@@ -252,9 +252,13 @@ async function fetchProfile(actorName) {
}, { encodeJSON: true });
if (res.ok) {
- const actor = res.body.hits.hits.find(hit => hit._source.name === actorName);
+ const actor = res.body.hits.hits.find(hit => hit._source.name.toLowerCase() === actorName.toLowerCase());
- return scrapeProfile(actor._source);
+ if (actor) {
+ return scrapeProfile(actor._source);
+ }
+
+ return null;
}
return res.status;