Improved actor scraping and display.
|
@ -44,7 +44,7 @@
|
||||||
class="avatar-link"
|
class="avatar-link"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="`/media/${actor.avatar.thumbnail}`"
|
:src="sfw ? `/img/${actor.avatar.sfw.thumbnail}` : `/media/${actor.avatar.thumbnail}`"
|
||||||
:title="actor.avatar.copyright && `© ${actor.avatar.copyright}`"
|
:title="actor.avatar.copyright && `© ${actor.avatar.copyright}`"
|
||||||
class="avatar"
|
class="avatar"
|
||||||
>
|
>
|
||||||
|
@ -153,7 +153,7 @@
|
||||||
<Icon
|
<Icon
|
||||||
v-if="actor.naturalBoobs === false"
|
v-if="actor.naturalBoobs === false"
|
||||||
v-tooltip="'Enhanced boobs'"
|
v-tooltip="'Enhanced boobs'"
|
||||||
icon="star"
|
icon="magic-wand2"
|
||||||
class="enhanced"
|
class="enhanced"
|
||||||
/>{{ actor.bust || '??' }}{{ actor.cup || '?' }}-{{ actor.waist || '??' }}-{{ actor.hip || '??' }}
|
/>{{ actor.bust || '??' }}{{ actor.cup || '?' }}-{{ actor.waist || '??' }}-{{ actor.hip || '??' }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -271,6 +271,10 @@ async function fetchActor() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sfw() {
|
||||||
|
return this.$store.state.ui.sfw;
|
||||||
|
}
|
||||||
|
|
||||||
async function route() {
|
async function route() {
|
||||||
await this.fetchActor();
|
await this.fetchActor();
|
||||||
}
|
}
|
||||||
|
@ -303,6 +307,9 @@ export default {
|
||||||
expanded: false,
|
expanded: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
sfw,
|
||||||
|
},
|
||||||
watch: {
|
watch: {
|
||||||
$route: route,
|
$route: route,
|
||||||
},
|
},
|
||||||
|
@ -495,6 +502,7 @@ export default {
|
||||||
.enhanced.icon {
|
.enhanced.icon {
|
||||||
fill: $primary;
|
fill: $primary;
|
||||||
padding: 0 .5rem;
|
padding: 0 .5rem;
|
||||||
|
transform: scaleX(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ethnicity {
|
.ethnicity {
|
||||||
|
|
|
@ -1,60 +1,60 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="actors">
|
<div class="actors">
|
||||||
<nav class="filter">
|
<nav class="filter">
|
||||||
<ul class="genders nolist">
|
<ul class="genders nolist">
|
||||||
<li class="gender">
|
<li class="gender">
|
||||||
<router-link
|
<router-link
|
||||||
:to="{ name: 'actors', params: { gender: 'female', letter } }"
|
:to="{ name: 'actors', params: { gender: 'female', letter } }"
|
||||||
:class="{ selected: gender === 'female' }"
|
:class="{ selected: gender === 'female' }"
|
||||||
class="gender-link female"
|
class="gender-link female"
|
||||||
><Gender gender="female" /></router-link>
|
><Gender gender="female" /></router-link>
|
||||||
</li>
|
</li>
|
||||||
<li class="gender">
|
<li class="gender">
|
||||||
<router-link
|
<router-link
|
||||||
:to="{ name: 'actors', params: { gender: 'male', letter } }"
|
:to="{ name: 'actors', params: { gender: 'male', letter } }"
|
||||||
:class="{ selected: gender === 'male' }"
|
:class="{ selected: gender === 'male' }"
|
||||||
class="gender-link male"
|
class="gender-link male"
|
||||||
><Gender gender="male" /></router-link>
|
><Gender gender="male" /></router-link>
|
||||||
</li>
|
</li>
|
||||||
<li class="gender">
|
<li class="gender">
|
||||||
<router-link
|
<router-link
|
||||||
:to="{ name: 'actors', params: { gender: 'trans', letter } }"
|
:to="{ name: 'actors', params: { gender: 'trans', letter } }"
|
||||||
:class="{ selected: gender === 'trans' }"
|
:class="{ selected: gender === 'trans' }"
|
||||||
class="gender-link transsexual"
|
class="gender-link transsexual"
|
||||||
><Gender gender="transsexual" /></router-link>
|
><Gender gender="transsexual" /></router-link>
|
||||||
</li>
|
</li>
|
||||||
<li class="gender">
|
<li class="gender">
|
||||||
<router-link
|
<router-link
|
||||||
:to="{ name: 'actors', params: { gender: 'other', letter } }"
|
:to="{ name: 'actors', params: { gender: 'other', letter } }"
|
||||||
:class="{ selected: gender === 'other' }"
|
:class="{ selected: gender === 'other' }"
|
||||||
class="gender-link other"
|
class="gender-link other"
|
||||||
><Icon icon="question5" /></router-link>
|
><Icon icon="question5" /></router-link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<ul class="letters nolist">
|
<ul class="letters nolist">
|
||||||
<li
|
<li
|
||||||
v-for="letterX in letters"
|
v-for="letterX in letters"
|
||||||
:key="letterX"
|
:key="letterX"
|
||||||
class="letter"
|
class="letter"
|
||||||
>
|
>
|
||||||
<router-link
|
<router-link
|
||||||
:to="{ name: 'actors', params: { gender, letter: letterX } }"
|
:to="{ name: 'actors', params: { gender, letter: letterX } }"
|
||||||
:class="{ selected: letterX === letter }"
|
:class="{ selected: letterX === letter }"
|
||||||
class="letter-link"
|
class="letter-link"
|
||||||
>{{ letterX || 'All' }}</router-link>
|
>{{ letterX || 'All' }}</router-link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="tiles">
|
<div class="tiles">
|
||||||
<Actor
|
<Actor
|
||||||
v-for="actor in actors"
|
v-for="actor in actors"
|
||||||
:key="`actor-${actor.id}`"
|
:key="`actor-${actor.id}`"
|
||||||
:actor="actor"
|
:actor="actor"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -62,56 +62,56 @@ import Actor from '../tile/actor.vue';
|
||||||
import Gender from './gender.vue';
|
import Gender from './gender.vue';
|
||||||
|
|
||||||
async function fetchActors() {
|
async function fetchActors() {
|
||||||
const curatedGender = this.gender.replace('trans', 'transsexual');
|
const curatedGender = this.gender.replace('trans', 'transsexual');
|
||||||
|
|
||||||
this.actors = await this.$store.dispatch('fetchActors', {
|
this.actors = await this.$store.dispatch('fetchActors', {
|
||||||
limit: 1000,
|
limit: 1000,
|
||||||
letter: this.letter.replace('all', ''),
|
letter: this.letter.replace('all', ''),
|
||||||
gender: curatedGender === 'other' ? null : curatedGender,
|
gender: curatedGender === 'other' ? null : curatedGender,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function letter() {
|
function letter() {
|
||||||
return this.$route.params.letter || 'all';
|
return this.$route.params.letter || 'all';
|
||||||
}
|
}
|
||||||
|
|
||||||
function gender() {
|
function gender() {
|
||||||
return this.$route.params.gender || 'female';
|
return this.$route.params.gender || 'female';
|
||||||
}
|
}
|
||||||
|
|
||||||
async function route() {
|
async function route() {
|
||||||
await this.fetchActors();
|
await this.fetchActors();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function mounted() {
|
async function mounted() {
|
||||||
this.pageTitle = 'Actors';
|
this.pageTitle = 'Actors';
|
||||||
|
|
||||||
await this.fetchActors();
|
await this.fetchActors();
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Actor,
|
Actor,
|
||||||
Gender,
|
Gender,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
actors: [],
|
actors: [],
|
||||||
pageTitle: null,
|
pageTitle: null,
|
||||||
letters: ['all'].concat(Array.from({ length: 26 }, (value, index) => String.fromCharCode(index + 97).toUpperCase())),
|
letters: ['all'].concat(Array.from({ length: 26 }, (value, index) => String.fromCharCode(index + 97).toUpperCase())),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
letter,
|
letter,
|
||||||
gender,
|
gender,
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
$route: route,
|
$route: route,
|
||||||
},
|
},
|
||||||
mounted,
|
mounted,
|
||||||
methods: {
|
methods: {
|
||||||
fetchActors,
|
fetchActors,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -226,7 +226,7 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media(max-width: $breakpoint) {
|
@media(max-width: $breakpoint0) {
|
||||||
.tiles {
|
.tiles {
|
||||||
grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
class="avatar-link photo-link"
|
class="avatar-link photo-link"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="`/media/${actor.avatar.thumbnail}`"
|
:src="sfw ? `/img/${actor.avatar.sfw.thumbnail}` : `/media/${actor.avatar.thumbnail}`"
|
||||||
:title="actor.avatar.copyright && `© ${actor.avatar.copyright}`"
|
:title="actor.avatar.copyright && `© ${actor.avatar.copyright}`"
|
||||||
class="avatar photo"
|
class="avatar photo"
|
||||||
>
|
>
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
class="photo-link"
|
class="photo-link"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="`/media/${photo.thumbnail}`"
|
:src="sfw ? `/img/${photo.sfw.thumbnail}` : `/media/${photo.thumbnail}`"
|
||||||
:title="photo.copyright && `© ${photo.copyright}`"
|
:title="photo.copyright && `© ${photo.copyright}`"
|
||||||
class="photo"
|
class="photo"
|
||||||
>
|
>
|
||||||
|
@ -35,6 +35,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
function sfw() {
|
||||||
|
return this.$store.state.ui.sfw;
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
actor: {
|
actor: {
|
||||||
|
@ -42,6 +46,9 @@ export default {
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
sfw,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,128 +1,128 @@
|
||||||
<template>
|
<template>
|
||||||
<header class="header">
|
<header class="header">
|
||||||
<div class="header-nav">
|
<div class="header-nav">
|
||||||
<Icon
|
<Icon
|
||||||
icon="menu"
|
icon="menu"
|
||||||
class="sidebar-toggle"
|
class="sidebar-toggle"
|
||||||
@click.native.stop="toggleSidebar"
|
@click.native.stop="toggleSidebar"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<router-link
|
<router-link
|
||||||
to="/"
|
to="/"
|
||||||
class="logo-link"
|
class="logo-link"
|
||||||
><h1 class="header-logo">
|
><h1 class="header-logo">
|
||||||
<div
|
<div
|
||||||
class="logo"
|
class="logo"
|
||||||
v-html="logo"
|
v-html="logo"
|
||||||
/>
|
/>
|
||||||
</h1></router-link>
|
</h1></router-link>
|
||||||
|
|
||||||
<nav class="nav">
|
<nav class="nav">
|
||||||
<ul class="nav-list nolist">
|
<ul class="nav-list nolist">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<router-link
|
<router-link
|
||||||
v-slot="{ href, isActive, navigate }"
|
v-slot="{ href, isActive, navigate }"
|
||||||
to="/actors"
|
to="/actors"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="nav-link"
|
class="nav-link"
|
||||||
:href="href"
|
:href="href"
|
||||||
:class="{ active: isActive }"
|
:class="{ active: isActive }"
|
||||||
@click="navigate"
|
@click="navigate"
|
||||||
>Actors</a>
|
>Actors</a>
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<router-link
|
<router-link
|
||||||
v-slot="{ href, isActive, navigate }"
|
v-slot="{ href, isActive, navigate }"
|
||||||
to="/networks"
|
to="/networks"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="nav-link"
|
class="nav-link"
|
||||||
:href="href"
|
:href="href"
|
||||||
:class="{ active: isActive }"
|
:class="{ active: isActive }"
|
||||||
@click="navigate"
|
@click="navigate"
|
||||||
>Sites</a>
|
>Sites</a>
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<router-link
|
<router-link
|
||||||
v-slot="{ href, isActive, navigate }"
|
v-slot="{ href, isActive, navigate }"
|
||||||
to="/tags"
|
to="/tags"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="nav-link"
|
class="nav-link"
|
||||||
:href="href"
|
:href="href"
|
||||||
:class="{ active: isActive }"
|
:class="{ active: isActive }"
|
||||||
@click="navigate"
|
@click="navigate"
|
||||||
>Tags</a>
|
>Tags</a>
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="header-section">
|
<div class="header-section">
|
||||||
<div class="header-toggles">
|
<div class="header-toggles">
|
||||||
<Icon
|
<Icon
|
||||||
v-show="!sfw"
|
v-show="!sfw"
|
||||||
v-tooltip="'Hit S to use SFW mode'"
|
v-tooltip="'Hit S to use SFW mode'"
|
||||||
icon="flower"
|
icon="flower"
|
||||||
class="toggle noselect"
|
class="toggle noselect"
|
||||||
@click.native="setSfw(true)"
|
@click.native="setSfw(true)"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Icon
|
<Icon
|
||||||
v-show="sfw"
|
v-show="sfw"
|
||||||
v-tooltip="'Hit N to use NSFW mode'"
|
v-tooltip="'Hit N to use NSFW mode'"
|
||||||
icon="flower"
|
icon="flower"
|
||||||
class="toggle active noselect"
|
class="toggle active noselect"
|
||||||
@click.native="setSfw(false)"
|
@click.native="setSfw(false)"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Icon
|
<Icon
|
||||||
v-show="theme === 'light'"
|
v-show="theme === 'light'"
|
||||||
v-tooltip="'Hit D to use dark theme'"
|
v-tooltip="'Hit D to use dark theme'"
|
||||||
icon="moon"
|
icon="moon"
|
||||||
class="toggle noselect"
|
class="toggle noselect"
|
||||||
@click.native="setTheme('dark')"
|
@click.native="setTheme('dark')"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Icon
|
<Icon
|
||||||
v-show="theme === 'dark'"
|
v-show="theme === 'dark'"
|
||||||
v-tooltip="'Hit L to use light theme'"
|
v-tooltip="'Hit L to use light theme'"
|
||||||
icon="sun"
|
icon="sun"
|
||||||
class="toggle noselect"
|
class="toggle noselect"
|
||||||
@click.native="setTheme('light')"
|
@click.native="setTheme('light')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Search class="search-full" />
|
<Search class="search-full" />
|
||||||
|
|
||||||
<v-popover
|
<v-popover
|
||||||
class="search-compact"
|
class="search-compact"
|
||||||
:open="searching"
|
:open="searching"
|
||||||
@show="searching = true"
|
@show="searching = true"
|
||||||
@hide="searching = false"
|
@hide="searching = false"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="search-button"
|
class="search-button"
|
||||||
><Icon
|
><Icon
|
||||||
icon="search"
|
icon="search"
|
||||||
/></button>
|
/></button>
|
||||||
|
|
||||||
<Search
|
<Search
|
||||||
slot="popover"
|
slot="popover"
|
||||||
:searching="searching"
|
:searching="searching"
|
||||||
class="compact"
|
class="compact"
|
||||||
@search="searching = false"
|
@search="searching = false"
|
||||||
/>
|
/>
|
||||||
</v-popover>
|
</v-popover>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -133,47 +133,47 @@ import Search from './search.vue';
|
||||||
import logo from '../../img/logo.svg';
|
import logo from '../../img/logo.svg';
|
||||||
|
|
||||||
function sfw(state) {
|
function sfw(state) {
|
||||||
return state.ui.sfw;
|
return state.ui.sfw;
|
||||||
}
|
}
|
||||||
|
|
||||||
function theme(state) {
|
function theme(state) {
|
||||||
return state.ui.theme;
|
return state.ui.theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setTheme(newTheme) {
|
function setTheme(newTheme) {
|
||||||
this.$store.dispatch('setTheme', newTheme);
|
this.$store.dispatch('setTheme', newTheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setSfw(enabled) {
|
function setSfw(enabled) {
|
||||||
this.$store.dispatch('setSfw', enabled);
|
this.$store.dispatch('setSfw', enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Search,
|
Search,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
toggleSidebar: {
|
toggleSidebar: {
|
||||||
type: Function,
|
type: Function,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
logo,
|
logo,
|
||||||
searching: false,
|
searching: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState({
|
||||||
sfw,
|
sfw,
|
||||||
theme,
|
theme,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setSfw,
|
setSfw,
|
||||||
setTheme,
|
setTheme,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,57 +1,64 @@
|
||||||
<template>
|
<template>
|
||||||
<form
|
<form
|
||||||
class="search"
|
class="search"
|
||||||
@submit.prevent="search"
|
@submit.prevent="search"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
ref="search"
|
ref="search"
|
||||||
v-model="query"
|
v-model="query"
|
||||||
type="search"
|
type="search"
|
||||||
class="search-input"
|
class="search-input"
|
||||||
placeholder="Search..."
|
placeholder="Search..."
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="search-button"
|
class="search-button"
|
||||||
><Icon
|
><Icon
|
||||||
icon="search"
|
icon="search"
|
||||||
/></button>
|
/></button>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
async function search() {
|
async function search() {
|
||||||
this.$router.push({ name: 'search', query: { q: this.query } });
|
this.$router.push({ name: 'search', query: { q: this.query } });
|
||||||
this.$emit('search');
|
this.$emit('search');
|
||||||
}
|
}
|
||||||
|
|
||||||
function searching(to) {
|
function searching(to) {
|
||||||
if (to) {
|
if (to) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// nextTick does not seem to work
|
// nextTick does not seem to work
|
||||||
this.$refs.search.focus();
|
this.$refs.search.focus();
|
||||||
}, 20);
|
}, 20);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function route(to) {
|
||||||
|
if (to.name !== 'search') {
|
||||||
|
this.query = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
searching: {
|
searching: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
query: this.$route.query ? this.$route.query.q : null,
|
query: this.$route.query ? this.$route.query.q : null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
searching,
|
$route: route,
|
||||||
},
|
searching,
|
||||||
methods: {
|
},
|
||||||
search,
|
methods: {
|
||||||
},
|
search,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,72 +1,72 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="releases">
|
<div class="releases">
|
||||||
<h3
|
<h3
|
||||||
v-if="context"
|
v-if="context"
|
||||||
class="heading"
|
class="heading"
|
||||||
><span class="range">{{ range }}</span> releases for '{{ context }}'</h3>
|
><span class="range">{{ range }}</span> releases for '{{ context }}'</h3>
|
||||||
|
|
||||||
<ul
|
<ul
|
||||||
:key="sfw"
|
:key="sfw"
|
||||||
v-lazy-container="{ selector: '.thumbnail' }"
|
v-lazy-container="{ selector: '.thumbnail' }"
|
||||||
class="nolist tiles"
|
class="nolist tiles"
|
||||||
>
|
>
|
||||||
<li
|
<li
|
||||||
v-for="(release, index) in releases"
|
v-for="(release, index) in releases"
|
||||||
:key="`release-${release.id}`"
|
:key="`release-${release.id}`"
|
||||||
>
|
>
|
||||||
<ReleaseTile
|
<ReleaseTile
|
||||||
:release="release"
|
:release="release"
|
||||||
:referer="referer"
|
:referer="referer"
|
||||||
:index="index"
|
:index="index"
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
v-if="releases.length === 0 && range !== 'all'"
|
v-if="releases.length === 0 && range !== 'all'"
|
||||||
class="empty"
|
class="empty"
|
||||||
>No {{ range }} releases</span>
|
>No {{ range }} releases</span>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
v-else-if="releases.length === 0"
|
v-else-if="releases.length === 0"
|
||||||
class="empty"
|
class="empty"
|
||||||
>No recent or upcoming releases</span>
|
>No recent or upcoming releases</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ReleaseTile from '../tile/release.vue';
|
import ReleaseTile from '../tile/release.vue';
|
||||||
|
|
||||||
function range() {
|
function range() {
|
||||||
return this.$route.params.range;
|
return this.$route.params.range;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sfw() {
|
function sfw() {
|
||||||
return this.$store.state.ui.sfw;
|
return this.$store.state.ui.sfw;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
ReleaseTile,
|
ReleaseTile,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
releases: {
|
releases: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
context: {
|
context: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
referer: {
|
referer: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
range,
|
range,
|
||||||
sfw,
|
sfw,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,47 +1,89 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="content-inner">
|
<div class="content-inner">
|
||||||
<span
|
<span
|
||||||
v-if="!loading"
|
v-if="loading"
|
||||||
class="summary"
|
class="summary"
|
||||||
>Found {{ releases.length }} results for '{{ query }}'</span>
|
>Searching...</span>
|
||||||
|
|
||||||
<span
|
<div v-if="!loading && actors.length > 0">
|
||||||
v-else
|
<span
|
||||||
class="summary"
|
v-if="!loading"
|
||||||
>Searching...</span>
|
class="summary"
|
||||||
|
>Found {{ actors.length }} actors for '{{ query }}'</span>
|
||||||
|
|
||||||
<Releases :releases="releases" />
|
<div class="tiles">
|
||||||
</div>
|
<Actor
|
||||||
|
v-for="actor in actors"
|
||||||
|
:key="`actor-${actor.id}`"
|
||||||
|
:actor="actor.aliasFor || actor"
|
||||||
|
:alias="actor.aliasFor && actor"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="!loading && actors.length > 0">
|
||||||
|
<span
|
||||||
|
v-if="!loading"
|
||||||
|
class="summary"
|
||||||
|
>Found {{ releases.length }} releases for '{{ query }}'</span>
|
||||||
|
|
||||||
|
<Releases :releases="releases" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import Actor from '../tile/actor.vue';
|
||||||
import Releases from '../releases/releases.vue';
|
import Releases from '../releases/releases.vue';
|
||||||
|
|
||||||
|
async function search() {
|
||||||
|
const results = await this.$store.dispatch('search', {
|
||||||
|
query: this.query,
|
||||||
|
limit: 100,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
|
|
||||||
|
if (results) {
|
||||||
|
this.actors = results.actors;
|
||||||
|
this.releases = results.releases;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function query() {
|
||||||
|
return this.$route.query.query || this.$route.query.q;
|
||||||
|
}
|
||||||
|
|
||||||
async function mounted() {
|
async function mounted() {
|
||||||
const results = await this.$store.dispatch('searchReleases', {
|
await this.search();
|
||||||
query: this.query,
|
}
|
||||||
limit: 100,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.loading = false;
|
async function watchQuery() {
|
||||||
|
await this.search();
|
||||||
if (results) {
|
|
||||||
this.releases = results;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Releases,
|
Actor,
|
||||||
},
|
Releases,
|
||||||
data() {
|
},
|
||||||
return {
|
data() {
|
||||||
loading: true,
|
return {
|
||||||
releases: [],
|
loading: true,
|
||||||
query: this.$route.query.query || this.$route.query.q,
|
actors: [],
|
||||||
};
|
releases: [],
|
||||||
},
|
};
|
||||||
mounted,
|
},
|
||||||
|
computed: {
|
||||||
|
query,
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
query: watchQuery,
|
||||||
|
},
|
||||||
|
mounted,
|
||||||
|
methods: {
|
||||||
|
search,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -54,4 +96,12 @@ export default {
|
||||||
color: $shadow;
|
color: $shadow;
|
||||||
font-weight: bold;
|
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;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -8,29 +8,37 @@
|
||||||
class="link"
|
class="link"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
v-if="actor.network"
|
|
||||||
v-tooltip.top="`${actor.name} (${actor.network.name})`"
|
|
||||||
class="handle"
|
class="handle"
|
||||||
>
|
>
|
||||||
<img
|
<span
|
||||||
:src="`/img/logos/${actor.network.slug}/favicon.png`"
|
v-tooltip.top="actor.name"
|
||||||
|
class="name"
|
||||||
|
>{{ actor.name }}</span>
|
||||||
|
|
||||||
|
<router-link
|
||||||
|
v-if="actor.network"
|
||||||
|
v-tooltip="actor.network.name"
|
||||||
|
:to="{ name: 'network', params: { networkSlug: actor.network.slug } }"
|
||||||
class="favicon"
|
class="favicon"
|
||||||
>
|
>
|
||||||
<span class="name">{{ actor.name }}</span>
|
<img
|
||||||
</span>
|
:src="`/img/logos/${actor.network.slug}/favicon.png`"
|
||||||
|
class="favicon-icon"
|
||||||
|
>
|
||||||
|
</router-link>
|
||||||
|
|
||||||
<span
|
<Icon
|
||||||
v-else
|
v-if="alias"
|
||||||
v-tooltip.top="actor.name"
|
v-tooltip="`Alias for ${alias.name}`"
|
||||||
class="handle"
|
icon="users3"
|
||||||
>
|
class="favicon alias"
|
||||||
<span class="name">{{ actor.name }}</span>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div class="avatar-container">
|
<div class="avatar-container">
|
||||||
<img
|
<img
|
||||||
v-if="actor.avatar"
|
v-if="actor.avatar"
|
||||||
:src="`/media/${actor.avatar.thumbnail || actor.avatar}`"
|
:src="sfw ? `/img/${actor.avatar.sfw.thumbnail}` : `/media/${actor.avatar.thumbnail}`"
|
||||||
class="avatar"
|
class="avatar"
|
||||||
>
|
>
|
||||||
|
|
||||||
|
@ -45,7 +53,9 @@
|
||||||
<span
|
<span
|
||||||
class="details"
|
class="details"
|
||||||
>
|
>
|
||||||
<span class="age">
|
<span class="gender-age">
|
||||||
|
<Gender :gender="actor.gender" />
|
||||||
|
|
||||||
<span
|
<span
|
||||||
v-if="actor.age"
|
v-if="actor.age"
|
||||||
v-tooltip="`Born on ${formatDate(actor.birthdate, 'MMMM D, YYYY')}`"
|
v-tooltip="`Born on ${formatDate(actor.birthdate, 'MMMM D, YYYY')}`"
|
||||||
|
@ -59,8 +69,6 @@
|
||||||
>{{ actor.ageThen }}</span>
|
>{{ actor.ageThen }}</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<Gender :gender="actor.gender" />
|
|
||||||
|
|
||||||
<span
|
<span
|
||||||
v-if="actor.origin"
|
v-if="actor.origin"
|
||||||
v-tooltip="`Born in ${actor.origin.country.alias || actor.origin.country.name}`"
|
v-tooltip="`Born in ${actor.origin.country.alias || actor.origin.country.name}`"
|
||||||
|
@ -86,6 +94,10 @@
|
||||||
<script>
|
<script>
|
||||||
import Gender from '../actors/gender.vue';
|
import Gender from '../actors/gender.vue';
|
||||||
|
|
||||||
|
function sfw() {
|
||||||
|
return this.$store.state.ui.sfw;
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Gender,
|
Gender,
|
||||||
|
@ -95,6 +107,13 @@ export default {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
alias: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
sfw,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -137,18 +156,29 @@ export default {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: .5rem;
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
||||||
|
.name {
|
||||||
|
padding: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alias {
|
||||||
|
fill: var(--highlight);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.favicon {
|
.favicon {
|
||||||
|
font-size: 0;
|
||||||
|
padding: .5rem .25rem;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
padding: .5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.favicon-icon {
|
||||||
width: 1rem;
|
width: 1rem;
|
||||||
height: 1rem;
|
height: 1rem;
|
||||||
margin: 0 .5rem 0 0;
|
|
||||||
|
|
||||||
& + .name {
|
|
||||||
padding: 0 1rem 0 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
|
@ -156,13 +186,13 @@ export default {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar-container {
|
.avatar-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
|
@ -191,6 +221,7 @@ export default {
|
||||||
height: 1.75rem;
|
height: 1.75rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: .5rem;
|
padding: .5rem;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -199,14 +230,13 @@ export default {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.age,
|
.gender-age {
|
||||||
.country,
|
display: flex;
|
||||||
.gender {
|
align-items: center;
|
||||||
flex: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.gender {
|
.gender {
|
||||||
text-align: center;
|
margin: .25rem .25rem 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.country {
|
.country {
|
||||||
|
@ -216,7 +246,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.flag {
|
.flag {
|
||||||
height: 1rem;
|
height: .75rem;
|
||||||
margin: 0 0 0 .5rem;
|
margin: 0 0 0 .5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,175 +1,175 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
:id="`${release.type}-${release.id}`"
|
:id="`${release.type}-${release.id}`"
|
||||||
:class="{ [release.type]: true }"
|
:class="{ [release.type]: true }"
|
||||||
class="tile"
|
class="tile"
|
||||||
>
|
>
|
||||||
<span class="poster">
|
<span class="poster">
|
||||||
<span class="details">
|
<span class="details">
|
||||||
<router-link
|
<router-link
|
||||||
v-if="release.site && release.site.independent"
|
v-if="release.site && release.site.independent"
|
||||||
:to="`/network/${release.network.slug}`"
|
:to="`/network/${release.network.slug}`"
|
||||||
class="site site-link"
|
class="site site-link"
|
||||||
><img
|
><img
|
||||||
:src="`/img/logos/${release.network.slug}/favicon.png`"
|
:src="`/img/logos/${release.network.slug}/favicon.png`"
|
||||||
class="favicon"
|
class="favicon"
|
||||||
>{{ release.network.name }}</router-link>
|
>{{ release.network.name }}</router-link>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
v-else-if="release.network"
|
v-else-if="release.network"
|
||||||
class="site"
|
class="site"
|
||||||
>
|
>
|
||||||
<router-link
|
<router-link
|
||||||
v-tooltip.bottom="`Part of ${release.network.name}`"
|
v-tooltip.bottom="`Part of ${release.network.name}`"
|
||||||
:title="`Part of ${release.network.name}`"
|
:title="`Part of ${release.network.name}`"
|
||||||
:to="`/network/${release.network.slug}`"
|
:to="`/network/${release.network.slug}`"
|
||||||
class="site-link"
|
class="site-link"
|
||||||
><img
|
><img
|
||||||
:src="`/img/logos/${release.network.slug}/favicon.png`"
|
:src="`/img/logos/${release.network.slug}/favicon.png`"
|
||||||
class="favicon"
|
class="favicon"
|
||||||
></router-link>
|
></router-link>
|
||||||
|
|
||||||
<router-link
|
<router-link
|
||||||
v-tooltip.bottom="`More from ${release.site.name}`"
|
v-tooltip.bottom="`More from ${release.site.name}`"
|
||||||
:title="`More from ${release.site.name}`"
|
:title="`More from ${release.site.name}`"
|
||||||
:to="`/site/${release.site.slug}`"
|
:to="`/site/${release.site.slug}`"
|
||||||
class="site-link"
|
class="site-link"
|
||||||
>{{ release.site.name }}</router-link>
|
>{{ release.site.name }}</router-link>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span v-else />
|
<span v-else />
|
||||||
|
|
||||||
<a
|
<a
|
||||||
v-if="release.date"
|
v-if="release.date"
|
||||||
v-tooltip.bottom="release.url && `View scene on ${release.site.name}`"
|
v-tooltip.bottom="release.url && `View scene on ${release.site.name}`"
|
||||||
:title="release.url && `View scene on ${release.site.name}`"
|
:title="release.url && `View scene on ${release.site.name}`"
|
||||||
:href="release.url"
|
:href="release.url"
|
||||||
:class="{ upcoming: isAfter(release.date, new Date()), new: release.isNew }"
|
:class="{ upcoming: isAfter(release.date, new Date()), new: release.isNew }"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="date"
|
class="date"
|
||||||
>{{ formatDate(release.date, 'MMM D, YYYY') }}</a>
|
>{{ formatDate(release.date, 'MMM D, YYYY') }}</a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
v-else
|
v-else
|
||||||
:href="release.url"
|
:href="release.url"
|
||||||
:class="{ upcoming: isAfter(release.date, new Date()), new: release.isNew }"
|
:class="{ upcoming: isAfter(release.date, new Date()), new: release.isNew }"
|
||||||
title="Scene date N/A, showing date added"
|
title="Scene date N/A, showing date added"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="date"
|
class="date"
|
||||||
>{{ `(${formatDate(release.dateAdded, 'MMM D, YYYY')})` }}</a>
|
>{{ `(${formatDate(release.dateAdded, 'MMM D, YYYY')})` }}</a>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
:href="`/${release.type || 'scene'}/${release.id}/${release.slug}`"
|
:href="`/${release.type || 'scene'}/${release.id}/${release.slug}`"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="link"
|
class="link"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
v-if="release.poster"
|
v-if="release.poster"
|
||||||
:data-src="sfw ? `/img/${release.poster.sfw.thumbnail}` : `/media/${release.poster.thumbnail}`"
|
:data-src="sfw ? `/img/${release.poster.sfw.thumbnail}` : `/media/${release.poster.thumbnail}`"
|
||||||
:data-loading="sfw ? `/img/${release.poster.sfw.lazy}` : `/media/${release.poster.lazy}`"
|
:data-loading="sfw ? `/img/${release.poster.sfw.lazy}` : `/media/${release.poster.lazy}`"
|
||||||
:alt="release.title"
|
:alt="release.title"
|
||||||
class="thumbnail"
|
class="thumbnail"
|
||||||
>
|
>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
v-else-if="release.covers && release.covers.length > 0"
|
v-else-if="release.covers && release.covers.length > 0"
|
||||||
class="covers"
|
class="covers"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
v-for="cover in release.covers"
|
v-for="cover in release.covers"
|
||||||
:key="cover.id"
|
:key="cover.id"
|
||||||
:data-src="sfw ? `/img/${cover.sfw.thumbnail}` : `/media/${cover.thumbnail}`"
|
:data-src="sfw ? `/img/${cover.sfw.thumbnail}` : `/media/${cover.thumbnail}`"
|
||||||
:data-loading="sfw ? `/img/${cover.sfw.lazy}` : `/media/${cover.lazy}`"
|
:data-loading="sfw ? `/img/${cover.sfw.lazy}` : `/media/${cover.lazy}`"
|
||||||
:alt="release.title"
|
:alt="release.title"
|
||||||
class="thumbnail cover"
|
class="thumbnail cover"
|
||||||
>
|
>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
:title="release.title"
|
:title="release.title"
|
||||||
class="thumbnail"
|
class="thumbnail"
|
||||||
>No thumbnail available</div>
|
>No thumbnail available</div>
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<a
|
<a
|
||||||
:href="`/${release.type || 'scene'}/${release.id}/${release.slug}`"
|
:href="`/${release.type || 'scene'}/${release.id}/${release.slug}`"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="row link"
|
class="row link"
|
||||||
>
|
>
|
||||||
<h3
|
<h3
|
||||||
v-tooltip.top="release.title"
|
v-tooltip.top="release.title"
|
||||||
:title="release.title"
|
:title="release.title"
|
||||||
class="title"
|
class="title"
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
v-if="release.type === 'movie'"
|
v-if="release.type === 'movie'"
|
||||||
icon="film"
|
icon="film"
|
||||||
/>{{ release.title }}
|
/>{{ release.title }}
|
||||||
</h3>
|
</h3>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<span class="row">
|
<span class="row">
|
||||||
<ul class="actors nolist">
|
<ul class="actors nolist">
|
||||||
<li
|
<li
|
||||||
v-for="actor in release.actors"
|
v-for="actor in release.actors"
|
||||||
:key="actor.id"
|
:key="actor.id"
|
||||||
class="actor"
|
class="actor"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
:href="`/actor/${actor.slug}`"
|
:href="`/actor/${actor.slug}`"
|
||||||
class="actor-link"
|
class="actor-link"
|
||||||
>{{ actor.name }}</a>
|
>{{ actor.name }}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<ul
|
<ul
|
||||||
v-if="release.tags.length > 0"
|
v-if="release.tags.length > 0"
|
||||||
:title="release.tags.map(tag => tag.name).join(', ')"
|
:title="release.tags.map(tag => tag.name).join(', ')"
|
||||||
class="tags nolist"
|
class="tags nolist"
|
||||||
>
|
>
|
||||||
<li
|
<li
|
||||||
v-for="tag in release.tags"
|
v-for="tag in release.tags"
|
||||||
:key="`tag-${tag.slug}`"
|
:key="`tag-${tag.slug}`"
|
||||||
class="tag"
|
class="tag"
|
||||||
>
|
>
|
||||||
<router-link
|
<router-link
|
||||||
:to="`/tag/${tag.slug}`"
|
:to="`/tag/${tag.slug}`"
|
||||||
class="tag-link"
|
class="tag-link"
|
||||||
>{{ tag.name }}</router-link>
|
>{{ tag.name }}</router-link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function sfw() {
|
function sfw() {
|
||||||
return this.$store.state.ui.sfw;
|
return this.$store.state.ui.sfw;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
release: {
|
release: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
referer: {
|
referer: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
sfw,
|
sfw,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,68 +1,68 @@
|
||||||
<template>
|
<template>
|
||||||
<router-link
|
<router-link
|
||||||
:to="{ name: 'tag', params: { tagSlug: tag.slug } }"
|
:to="{ name: 'tag', params: { tagSlug: tag.slug } }"
|
||||||
:title="tag.name"
|
:title="tag.name"
|
||||||
class="tile"
|
class="tile"
|
||||||
>
|
>
|
||||||
<span class="title">{{ tag.name }}</span>
|
<span class="title">{{ tag.name }}</span>
|
||||||
|
|
||||||
<template v-if="tag.poster">
|
<template v-if="tag.poster">
|
||||||
<img
|
<img
|
||||||
v-if="!lazy && !sfw"
|
v-if="!lazy && !sfw"
|
||||||
:src="`/img/${tag.poster.thumbnail}`"
|
:src="`/img/${tag.poster.thumbnail}`"
|
||||||
:title="tag.poster.comment"
|
:title="tag.poster.comment"
|
||||||
:alt="tag.name"
|
:alt="tag.name"
|
||||||
class="poster"
|
class="poster"
|
||||||
>
|
>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
v-if="!lazy && sfw"
|
v-if="!lazy && sfw"
|
||||||
:src="`/img/${tag.poster.sfw.thumbnail}`"
|
:src="`/img/${tag.poster.sfw.thumbnail}`"
|
||||||
:title="tag.poster.sfw.comment"
|
:title="tag.poster.sfw.comment"
|
||||||
:alt="tag.name"
|
:alt="tag.name"
|
||||||
class="poster"
|
class="poster"
|
||||||
>
|
>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
v-if="lazy && !sfw"
|
v-if="lazy && !sfw"
|
||||||
:data-src="`/img/${tag.poster.thumbnail}`"
|
:data-src="`/img/${tag.poster.thumbnail}`"
|
||||||
:data-loading="`/img/${tag.poster.lazy}`"
|
:data-loading="`/img/${tag.poster.lazy}`"
|
||||||
:title="tag.poster.comment"
|
:title="tag.poster.comment"
|
||||||
:alt="tag.name"
|
:alt="tag.name"
|
||||||
class="poster"
|
class="poster"
|
||||||
>
|
>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
v-if="lazy && sfw"
|
v-if="lazy && sfw"
|
||||||
:data-src="`/img/${tag.poster.sfw.thumbnail}`"
|
:data-src="`/img/${tag.poster.sfw.thumbnail}`"
|
||||||
:data-loading="`/img/${tag.poster.sfw.lazy}`"
|
:data-loading="`/img/${tag.poster.sfw.lazy}`"
|
||||||
:title="tag.poster.sfw.comment"
|
:title="tag.poster.sfw.comment"
|
||||||
:alt="tag.name"
|
:alt="tag.name"
|
||||||
class="poster"
|
class="poster"
|
||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
</router-link>
|
</router-link>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function sfw() {
|
function sfw() {
|
||||||
return this.$store.state.ui.sfw;
|
return this.$store.state.ui.sfw;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
tag: {
|
tag: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
lazy: {
|
lazy: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
sfw,
|
sfw,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
<svg width="1.5975in" height="1.5153in" version="1.1" viewBox="0 0 40.577 38.489" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g transform="translate(-84.419 -88.927)">
|
||||||
|
<circle cx="103.05" cy="110.94" r="3.4834" stroke-width="8.851"/>
|
||||||
|
<path d="m95.441 88.928c-1.3031 1.1603-2.5644 2.3924-3.75 3.7148l3.7227 3.3359c1.0397-1.1598 2.1655-2.2623 3.3516-3.3184l-3.3242-3.7324zm-7.041 7.9551c-0.99554 1.5152-1.874 3.1441-2.5547 4.8848l4.6562 1.8203c0.53361-1.3647 1.2441-2.6896 2.0781-3.959l-4.1797-2.7461zm-3.9004 10.396c-0.18456 1.9266-0.060203 3.8935 0.41211 5.8027l4.8535-1.1992c-0.32852-1.328-0.42035-2.7361-0.28711-4.127l-4.9785-0.47656zm35.586 0.21289c-0.29417 1.5186-0.69253 2.9964-1.2227 4.4043l4.6797 1.7617c0.64472-1.7122 1.1135-3.4638 1.4531-5.2168l-4.9102-0.94922zm-28.658 8.1797-4.1855 2.7363c1.061 1.6226 2.3708 3.052 3.8398 4.2617l3.1777-3.8594c-1.1031-0.90839-2.0682-1.9706-2.832-3.1387zm25.445 0.19531c-0.79748 1.2167-1.7417 2.3212-2.8106 3.2461l3.2715 3.7793c1.4528-1.2571 2.6916-2.7131 3.7207-4.2832l-4.1816-2.7422zm-18.939 5.2109-2.0449 4.5625c1.7182 0.77028 3.5357 1.3174 5.3965 1.6211l0.80468-4.9356c-1.4288-0.23319-2.833-0.65486-4.1562-1.248zm12.557 0.28907c-1.2891 0.5601-2.6795 0.92116-4.0957 1.0781l0.55078 4.9707c1.8962-0.21017 3.7684-0.6964 5.5371-1.4648l-1.9922-4.584z" color="#000000" color-rendering="auto" dominant-baseline="auto" image-rendering="auto" shape-rendering="auto" solid-color="#000000" stop-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;isolation:auto;mix-blend-mode:normal;shape-margin:0;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -0,0 +1,5 @@
|
||||||
|
<!-- Generated by IcoMoon.io -->
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<title>forward</title>
|
||||||
|
<path d="M4.096 0c-1.777 3.219-2.076 8.13 4.904 7.966v-3.966l6 6-6 6v-3.881c-8.359 0.218-9.29-7.378-4.904-12.119z"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 284 B |
|
@ -0,0 +1,5 @@
|
||||||
|
<!-- Generated by IcoMoon.io -->
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<title>redo2</title>
|
||||||
|
<path d="M9 3.881v-3.881l6 6-6 6v-3.966c-6.98-0.164-6.681 4.747-4.904 7.966-4.386-4.741-3.455-12.337 4.904-12.119z"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 283 B |
|
@ -0,0 +1,6 @@
|
||||||
|
<!-- Generated by IcoMoon.io -->
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="17" height="16" viewBox="0 0 17 16">
|
||||||
|
<title>reply-all</title>
|
||||||
|
<path d="M14.184 0c1.777 3.219 2.297 8.13-4.684 7.966v-3.466l-5.5 5.5 5.5 5.5v-3.381c7 0 9.070-7.378 4.684-12.119z"></path>
|
||||||
|
<path d="M6.5 5.5l-1-1-5.5 5.5 5 5 1-1-4-4z"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 340 B |
|
@ -0,0 +1,5 @@
|
||||||
|
<!-- Generated by IcoMoon.io -->
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<title>reply</title>
|
||||||
|
<path d="M7 12.119v3.881l-6-6 6-6v3.966c6.98 0.164 6.681-4.747 4.904-7.966 4.386 4.741 3.455 12.337-4.904 12.119z"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 282 B |
|
@ -0,0 +1,5 @@
|
||||||
|
<!-- Generated by IcoMoon.io -->
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<title>undo2</title>
|
||||||
|
<path d="M11.904 16c1.777-3.219 2.076-8.13-4.904-7.966v3.966l-6-6 6-6v3.881c8.359-0.218 9.29 7.378 4.904 12.119z"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 281 B |
|
@ -0,0 +1,31 @@
|
||||||
|
<!-- Generated by IcoMoon.io -->
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="18" height="16" viewBox="0 0 18 16">
|
||||||
|
<title>users2</title>
|
||||||
|
<path d="M12 12.041v-0.825c1.101-0.621 2-2.168 2-3.716 0-2.485 0-4.5-3-4.5s-3 2.015-3 4.5c0 1.548 0.898 3.095 2 3.716v0.825c-3.392 0.277-6 1.944-6 3.959h14c0-2.015-2.608-3.682-6-3.959z"></path>
|
||||||
|
<path d="M3.242 9.625c-0.212 0.077-0.416 0.161-0.611 0.25h4.782c-0.038-0.083-0.075-0.166-0.109-0.25h-4.062z"></path>
|
||||||
|
<path d="M0.425 11.625c-0.053 0.082-0.101 0.165-0.144 0.25h5.834c0.192-0.089 0.389-0.172 0.593-0.25h-6.284z"></path>
|
||||||
|
<path d="M1.367 10.625c-0.105 0.081-0.204 0.164-0.299 0.25h6.933c-0.060-0.081-0.118-0.165-0.174-0.25h-6.461z"></path>
|
||||||
|
<path d="M5.261 9.125c-0.424 0.062-0.833 0.146-1.222 0.25h3.17c-0.030-0.083-0.057-0.166-0.083-0.25h-1.864z"></path>
|
||||||
|
<path d="M6 8.625v0.25h1.054c-0.022-0.083-0.041-0.166-0.059-0.25h-0.995z"></path>
|
||||||
|
<path d="M5.851 8.125c0.049 0.032 0.099 0.063 0.149 0.091v0.159h0.947c-0.014-0.083-0.025-0.167-0.035-0.25h-1.062z"></path>
|
||||||
|
<path d="M0.030 12.625c-0.013 0.083-0.022 0.166-0.027 0.25h4.501c0.102-0.085 0.209-0.169 0.321-0.25h-4.795z"></path>
|
||||||
|
<path d="M1.719 10.375h5.955c-0.024-0.042-0.048-0.084-0.072-0.127-0.022-0.041-0.044-0.082-0.066-0.123h-5.402c-0.145 0.080-0.283 0.163-0.415 0.25z"></path>
|
||||||
|
<path d="M4.005 3.625c-0.002 0.083-0.003 0.166-0.003 0.25h3.339c0.004-0.009 0.008-0.018 0.012-0.028 0.035-0.076 0.073-0.15 0.112-0.222h-3.46z"></path>
|
||||||
|
<path d="M0.168 12.125c-0.032 0.082-0.060 0.166-0.083 0.25h5.109c0.136-0.087 0.277-0.17 0.423-0.25h-5.449z"></path>
|
||||||
|
<path d="M4.643 0.875h4.713c-0.070-0.089-0.148-0.173-0.234-0.25h-4.245c-0.086 0.077-0.164 0.161-0.234 0.25z"></path>
|
||||||
|
<path d="M0.602 11.375h6.845c0.256-0.076 0.519-0.145 0.787-0.204-0.013-0.015-0.025-0.030-0.038-0.046h-7.381c-0.076 0.082-0.147 0.165-0.213 0.25z"></path>
|
||||||
|
<path d="M5.255 7.625c0.085 0.089 0.173 0.173 0.263 0.25h1.371c-0.006-0.083-0.010-0.167-0.012-0.25h-1.622z"></path>
|
||||||
|
<path d="M4.848 7.125c0.060 0.086 0.123 0.17 0.188 0.25h1.84c0-0.084 0-0.167 0-0.25h-2.027z"></path>
|
||||||
|
<path d="M4.255 1.625c-0.027 0.081-0.050 0.164-0.072 0.25h5.632c-0.021-0.086-0.045-0.169-0.072-0.25h-5.489z"></path>
|
||||||
|
<path d="M4.13 2.125c-0.015 0.082-0.029 0.165-0.041 0.25h4.625c0.179-0.098 0.372-0.181 0.578-0.25h-5.162z"></path>
|
||||||
|
<path d="M4.022 3.125c-0.004 0.082-0.008 0.166-0.010 0.25h3.605c0.058-0.087 0.12-0.17 0.185-0.25h-3.78z"></path>
|
||||||
|
<path d="M4.059 2.625c-0.008 0.082-0.016 0.165-0.022 0.25h3.995c0.092-0.089 0.189-0.173 0.292-0.25h-4.265z"></path>
|
||||||
|
<path d="M5.866 0.125c-0.243 0.062-0.455 0.146-0.638 0.25h3.544c-0.184-0.104-0.396-0.188-0.639-0.25h-2.266z"></path>
|
||||||
|
<path d="M4.544 6.625c0.045 0.085 0.092 0.169 0.141 0.25h2.192c0.001-0.084 0.002-0.167 0.003-0.25h-2.336z"></path>
|
||||||
|
<path d="M4.475 1.125c-0.046 0.080-0.088 0.163-0.125 0.25h5.3c-0.037-0.087-0.079-0.17-0.125-0.25h-5.050z"></path>
|
||||||
|
<path d="M4.152 5.625c0.022 0.084 0.048 0.167 0.074 0.25h2.683c0.005-0.084 0.012-0.167 0.019-0.25h-2.777z"></path>
|
||||||
|
<path d="M4.316 6.125c0.033 0.085 0.068 0.168 0.105 0.25h2.464c0.003-0.084 0.006-0.167 0.009-0.25h-2.579z"></path>
|
||||||
|
<path d="M4 4.125c-0 0.083-0 0.166-0 0.25h3.155c0.025-0.085 0.053-0.168 0.084-0.25h-3.238z"></path>
|
||||||
|
<path d="M4.002 4.625c0.003 0.083 0.008 0.167 0.016 0.25h3.014c0.016-0.084 0.035-0.168 0.055-0.25h-3.085z"></path>
|
||||||
|
<path d="M4.048 5.125c0.013 0.084 0.027 0.167 0.045 0.25h2.862c0.010-0.084 0.021-0.167 0.034-0.25h-2.941z"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.3 KiB |
|
@ -0,0 +1,6 @@
|
||||||
|
<!-- Generated by IcoMoon.io -->
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<title>users3</title>
|
||||||
|
<path d="M15.477 16h-10.954c-0.14 0-0.274-0.059-0.369-0.163s-0.141-0.242-0.129-0.382c0.11-1.22 0.585-2.363 1.373-3.305 0.697-0.832 1.59-1.452 2.602-1.809l0-0.475c-0.562-0.385-1.037-0.926-1.385-1.582-0.402-0.758-0.615-1.634-0.615-2.535 0-1.251 0.405-2.431 1.139-3.323 0.758-0.92 1.774-1.427 2.861-1.427s2.103 0.507 2.861 1.427c0.735 0.892 1.139 2.072 1.139 3.323 0 0.901-0.213 1.777-0.615 2.535-0.348 0.655-0.823 1.197-1.385 1.582l0 0.475c1.012 0.357 1.905 0.977 2.602 1.809 0.788 0.942 1.263 2.084 1.373 3.305 0.013 0.14-0.034 0.279-0.129 0.382s-0.229 0.163-0.369 0.163zM5.1 15h9.8c-0.164-0.81-0.527-1.565-1.064-2.208-0.649-0.776-1.504-1.33-2.471-1.604-0.215-0.061-0.364-0.257-0.364-0.481l-0-1.116c0-0.179 0.095-0.344 0.25-0.433 1.063-0.613 1.75-1.951 1.75-3.408 0-2.068-1.346-3.75-3-3.75s-3 1.682-3 3.75c0 1.457 0.687 2.795 1.75 3.408 0.155 0.089 0.25 0.254 0.25 0.433l-0 1.116c0 0.224-0.149 0.42-0.364 0.481-0.967 0.274-1.822 0.828-2.471 1.604-0.538 0.642-0.9 1.397-1.064 2.208z"></path>
|
||||||
|
<path d="M4.535 11.428c0.517-0.617 1.131-1.14 1.814-1.548-0.277-0.322-0.522-0.68-0.727-1.068-0.488-0.919-0.746-1.978-0.746-3.063 0-1.511 0.496-2.945 1.396-4.038 0.429-0.521 0.926-0.939 1.469-1.244-0.535-0.306-1.126-0.468-1.74-0.468-1.087 0-2.103 0.507-2.861 1.427-0.735 0.892-1.139 2.072-1.139 3.323 0 0.901 0.213 1.778 0.615 2.535 0.348 0.655 0.823 1.197 1.385 1.582l-0 0.475c-1.012 0.357-1.905 0.977-2.602 1.809-0.788 0.942-1.263 2.084-1.373 3.305-0.013 0.14 0.034 0.279 0.129 0.382s0.229 0.163 0.369 0.163h2.423c0.185-1.316 0.73-2.545 1.59-3.572z"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
|
@ -4,53 +4,9 @@ import {
|
||||||
releaseActorsFragment,
|
releaseActorsFragment,
|
||||||
releaseTagsFragment,
|
releaseTagsFragment,
|
||||||
} from '../fragments';
|
} from '../fragments';
|
||||||
import { curateRelease } from '../curate';
|
import { curateActor, curateRelease } from '../curate';
|
||||||
import getDateRange from '../get-date-range';
|
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) {
|
function initActorActions(store, _router) {
|
||||||
async function fetchActorBySlug({ _commit }, { actorSlug, limit = 100, range = 'latest' }) {
|
async function fetchActorBySlug({ _commit }, { actorSlug, limit = 100, range = 'latest' }) {
|
||||||
const { before, after, orderBy } = getDateRange(range);
|
const { before, after, orderBy } = getDateRange(range);
|
||||||
|
@ -110,6 +66,12 @@ function initActorActions(store, _router) {
|
||||||
hash
|
hash
|
||||||
comment
|
comment
|
||||||
copyright
|
copyright
|
||||||
|
sfw: sfwMedia {
|
||||||
|
id
|
||||||
|
thumbnail
|
||||||
|
path
|
||||||
|
comment
|
||||||
|
}
|
||||||
}
|
}
|
||||||
profiles: actorsProfiles {
|
profiles: actorsProfiles {
|
||||||
description
|
description
|
||||||
|
@ -121,6 +83,12 @@ function initActorActions(store, _router) {
|
||||||
hash
|
hash
|
||||||
comment
|
comment
|
||||||
copyright
|
copyright
|
||||||
|
sfw: sfwMedia {
|
||||||
|
id
|
||||||
|
thumbnail
|
||||||
|
path
|
||||||
|
comment
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
birthCity
|
birthCity
|
||||||
|
@ -202,7 +170,7 @@ function initActorActions(store, _router) {
|
||||||
exclude: store.state.ui.filter,
|
exclude: store.state.ui.filter,
|
||||||
});
|
});
|
||||||
|
|
||||||
return curateActor(actor);
|
return curateActor(actor, null, curateRelease);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchActors({ _commit }, {
|
async function fetchActors({ _commit }, {
|
||||||
|
@ -223,13 +191,16 @@ function initActorActions(store, _router) {
|
||||||
first:$limit,
|
first:$limit,
|
||||||
orderBy: NAME_ASC,
|
orderBy: NAME_ASC,
|
||||||
filter: {
|
filter: {
|
||||||
|
aliasFor: {
|
||||||
|
isNull: true
|
||||||
|
}
|
||||||
name: {
|
name: {
|
||||||
startsWith: $letter
|
startsWith: $letter
|
||||||
},
|
}
|
||||||
gender: {
|
gender: {
|
||||||
${genderFilter}
|
${genderFilter}
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
) {
|
) {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
|
@ -249,17 +220,13 @@ function initActorActions(store, _router) {
|
||||||
lazy
|
lazy
|
||||||
comment
|
comment
|
||||||
copyright
|
copyright
|
||||||
|
sfw: sfwMedia {
|
||||||
|
id
|
||||||
|
thumbnail
|
||||||
|
path
|
||||||
|
comment
|
||||||
|
}
|
||||||
}
|
}
|
||||||
actorsProfiles {
|
|
||||||
actorsAvatarByProfileId {
|
|
||||||
media {
|
|
||||||
id
|
|
||||||
path
|
|
||||||
thumbnail
|
|
||||||
copyright
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
birthCountry: countryByBirthCountryAlpha2 {
|
birthCountry: countryByBirthCountryAlpha2 {
|
||||||
alpha2
|
alpha2
|
||||||
name
|
name
|
||||||
|
|
|
@ -1,17 +1,50 @@
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
function curateActor(actor, release) {
|
function curateActor(actor, release, curateActorRelease) {
|
||||||
|
if (!actor) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const curatedActor = {
|
const curatedActor = {
|
||||||
...actor,
|
...actor,
|
||||||
origin: actor.originCountry && {
|
height: actor.heightMetric && {
|
||||||
country: actor.originCountry,
|
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) {
|
if (release && release.date && curatedActor.birthdate) {
|
||||||
curatedActor.ageThen = dayjs(release.date).diff(actor.birthdate, 'year');
|
curatedActor.ageThen = dayjs(release.date).diff(actor.birthdate, 'year');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (actor.releases) {
|
||||||
|
curatedActor.releases = actor.releases.map(actorRelease => curateActorRelease(actorRelease.release));
|
||||||
|
}
|
||||||
|
|
||||||
return curatedActor;
|
return curatedActor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,75 +28,6 @@ function initReleasesActions(store, _router) {
|
||||||
return releases.map(release => curateRelease(release));
|
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) {
|
async function fetchReleaseById({ _commit }, releaseId) {
|
||||||
// const release = await get(`/releases/${releaseId}`);
|
// const release = await get(`/releases/${releaseId}`);
|
||||||
|
|
||||||
|
@ -114,7 +45,6 @@ function initReleasesActions(store, _router) {
|
||||||
return {
|
return {
|
||||||
fetchReleases,
|
fetchReleases,
|
||||||
fetchReleaseById,
|
fetchReleaseById,
|
||||||
searchReleases,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
import { graphql } from '../api';
|
||||||
|
import { curateRelease, curateActor } from '../curate';
|
||||||
|
|
||||||
function initUiActions(_store, _router) {
|
function initUiActions(_store, _router) {
|
||||||
function setFilter({ commit }, filter) {
|
function setFilter({ commit }, filter) {
|
||||||
commit('setFilter', filter);
|
commit('setFilter', filter);
|
||||||
|
@ -23,7 +26,133 @@ function initUiActions(_store, _router) {
|
||||||
localStorage.setItem('sfw', sfw);
|
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 {
|
return {
|
||||||
|
search,
|
||||||
setFilter,
|
setFilter,
|
||||||
setRange,
|
setRange,
|
||||||
setBatch,
|
setBatch,
|
||||||
|
|
|
@ -270,8 +270,6 @@ exports.up = knex => Promise.resolve()
|
||||||
.references('id')
|
.references('id')
|
||||||
.inTable('networks');
|
.inTable('networks');
|
||||||
|
|
||||||
table.unique(['slug', 'network_id']);
|
|
||||||
|
|
||||||
table.integer('alias_for', 12)
|
table.integer('alias_for', 12)
|
||||||
.references('id')
|
.references('id')
|
||||||
.inTable('actors');
|
.inTable('actors');
|
||||||
|
@ -794,6 +792,9 @@ exports.up = knex => Promise.resolve()
|
||||||
ALTER TABLE releases_search
|
ALTER TABLE releases_search
|
||||||
ADD COLUMN document tsvector;
|
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 (
|
CREATE TEXT SEARCH DICTIONARY traxxx_dict (
|
||||||
TEMPLATE = pg_catalog.simple,
|
TEMPLATE = pg_catalog.simple,
|
||||||
stopwords = traxxx
|
stopwords = traxxx
|
||||||
|
@ -825,6 +826,12 @@ exports.up = knex => Promise.resolve()
|
||||||
url ILIKE ('%' || search || '%')
|
url ILIKE ('%' || search || '%')
|
||||||
$$ LANGUAGE SQL STABLE;
|
$$ 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 $$
|
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);
|
SELECT NOT EXISTS(SELECT true FROM batches WHERE batches.id = release.created_batch_id + 1 LIMIT 1);
|
||||||
$$ LANGUAGE sql STABLE;
|
$$ 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 countries CASCADE;
|
||||||
DROP TABLE IF EXISTS networks 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_sites;
|
||||||
|
DROP FUNCTION IF EXISTS search_actors;
|
||||||
DROP FUNCTION IF EXISTS get_random_sfw_media_id;
|
DROP FUNCTION IF EXISTS get_random_sfw_media_id;
|
||||||
|
|
||||||
DROP TEXT SEARCH CONFIGURATION IF EXISTS traxxx;
|
DROP TEXT SEARCH CONFIGURATION IF EXISTS traxxx;
|
||||||
|
|
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 15 KiB |
|
@ -357,7 +357,6 @@ const sfw = Object.entries({
|
||||||
['NI_fJ15rIfI', 'Szabo Viktor'],
|
['NI_fJ15rIfI', 'Szabo Viktor'],
|
||||||
['LymVMRIUwPQ', 'Happy Films'],
|
['LymVMRIUwPQ', 'Happy Films'],
|
||||||
['mrNVnLEphdo', 'Greg Nunes'],
|
['mrNVnLEphdo', 'Greg Nunes'],
|
||||||
['FKvoEKSV2LY', 'zhou yu'],
|
|
||||||
['CKLF34baCTQ', 'Willian Justen de Vasconcellos'],
|
['CKLF34baCTQ', 'Willian Justen de Vasconcellos'],
|
||||||
['7uGCN9qshsY', 'Siora Photography'],
|
['7uGCN9qshsY', 'Siora Photography'],
|
||||||
['xBTnaTgleQE', 'Glen Carrie'],
|
['xBTnaTgleQE', 'Glen Carrie'],
|
||||||
|
|
118
src/actors.js
|
@ -127,6 +127,10 @@ function curateProfileEntry(profile) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function curateProfile(profile) {
|
async function curateProfile(profile) {
|
||||||
|
if (!profile) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const curatedProfile = {
|
const curatedProfile = {
|
||||||
id: profile.id,
|
id: profile.id,
|
||||||
|
@ -161,7 +165,7 @@ async function curateProfile(profile) {
|
||||||
|
|
||||||
curatedProfile.dateOfDeath = Number.isNaN(Number(profile.dateOfDeath)) ? null : profile.dateOfDeath;
|
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.bust = Number(profile.bust) || profile.bust?.match(/\d+/)?.[0] || null;
|
||||||
curatedProfile.waist = Number(profile.waist) || profile.waist?.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;
|
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_birth = getMostFrequentDate(valuesByProperty.date_of_birth);
|
||||||
profile.date_of_death = getMostFrequentDate(valuesByProperty.date_of_death);
|
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_city = getMostFrequent(valuesByProperty.birth_city);
|
||||||
profile.birth_state = getMostFrequent(valuesByProperty.birth_state);
|
profile.birth_state = getMostFrequent(valuesByProperty.birth_state);
|
||||||
profile.birth_country_alpha2 = getMostFrequent(valuesByProperty.birth_country_alpha2);
|
profile.birth_country_alpha2 = getMostFrequent(valuesByProperty.birth_country_alpha2);
|
||||||
|
@ -300,51 +305,6 @@ async function interpolateProfiles(actors) {
|
||||||
.catch(transaction.rollback);
|
.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) {
|
async function upsertProfiles(profiles) {
|
||||||
const curatedProfileEntries = profiles.map(profile => curateProfileEntry(profile));
|
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) {
|
async function scrapeActors(actorNames) {
|
||||||
const baseActors = toBaseActors(actorNames);
|
const baseActors = toBaseActors(actorNames);
|
||||||
|
|
||||||
|
@ -438,7 +443,8 @@ async function scrapeActors(actorNames) {
|
||||||
{ concurrency: 10 },
|
{ 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) {
|
if (argv.inspect) {
|
||||||
console.log(profiles);
|
console.log(profiles);
|
||||||
|
@ -495,31 +501,25 @@ async function associateActors(releases, batchId) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseActorsBySlugAndNetworkId = baseActors.reduce((acc, baseActor) => ({
|
const baseActorsBySlug = baseActors.reduce((acc, baseActor) => ({
|
||||||
...acc,
|
...acc,
|
||||||
[baseActor.slug]: {
|
[baseActor.slug]: baseActor,
|
||||||
...acc[baseActor.slug],
|
|
||||||
[baseActor.network.id]: baseActor,
|
|
||||||
},
|
|
||||||
}), {});
|
}), {});
|
||||||
|
|
||||||
const uniqueBaseActors = Object.values(baseActorsBySlugAndNetworkId).map(baseActorsByNetworkId => Object.values(baseActorsByNetworkId)).flat();
|
const uniqueBaseActors = Object.values(baseActorsBySlug);
|
||||||
|
|
||||||
const actors = await getOrCreateActors(uniqueBaseActors, batchId);
|
const actors = await getOrCreateActors(uniqueBaseActors, batchId);
|
||||||
|
|
||||||
const actorIdsBySlugAndNetworkId = actors.reduce((acc, actor) => ({
|
const actorIdsBySlug = actors.reduce((acc, actor) => ({
|
||||||
...acc,
|
...acc,
|
||||||
[actor.network_id]: {
|
[actor.slug]: actor.alias_for || actor.id,
|
||||||
...acc[actor.network_id],
|
|
||||||
[actor.slug]: actor.alias_for || actor.id,
|
|
||||||
},
|
|
||||||
}), {});
|
}), {});
|
||||||
|
|
||||||
const releaseActorAssociations = Object.entries(baseActorsByReleaseId)
|
const releaseActorAssociations = Object.entries(baseActorsByReleaseId)
|
||||||
.map(([releaseId, releaseActors]) => releaseActors
|
.map(([releaseId, releaseActors]) => releaseActors
|
||||||
.map(releaseActor => ({
|
.map(releaseActor => ({
|
||||||
release_id: releaseId,
|
release_id: releaseId,
|
||||||
actor_id: actorIdsBySlugAndNetworkId[releaseActor.network.id]?.[releaseActor.slug] || actorIdsBySlugAndNetworkId.null[releaseActor.slug],
|
actor_id: actorIdsBySlug[releaseActor.slug],
|
||||||
})))
|
})))
|
||||||
.flat();
|
.flat();
|
||||||
|
|
||||||
|
|
|
@ -252,9 +252,13 @@ async function fetchProfile(actorName) {
|
||||||
}, { encodeJSON: true });
|
}, { encodeJSON: true });
|
||||||
|
|
||||||
if (res.ok) {
|
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;
|
return res.status;
|
||||||
|
|