Compare commits
251 Commits
091b7bd119
...
master
Author | SHA1 | Date |
---|---|---|
DebaucheryLibrarian | 90aa29d2d4 | |
DebaucheryLibrarian | 0369446681 | |
DebaucheryLibrarian | 238dce78b5 | |
DebaucheryLibrarian | 124ff3f5e3 | |
DebaucheryLibrarian | 1cf06a8b49 | |
DebaucheryLibrarian | a8bdb7ab59 | |
DebaucheryLibrarian | 08fd69af39 | |
DebaucheryLibrarian | 560ff103ce | |
DebaucheryLibrarian | edc724b475 | |
DebaucheryLibrarian | ac904c849d | |
DebaucheryLibrarian | 5481b6e0a6 | |
DebaucheryLibrarian | eba96df631 | |
DebaucheryLibrarian | 7caa325c5f | |
DebaucheryLibrarian | e20641e101 | |
DebaucheryLibrarian | 8f81f71802 | |
DebaucheryLibrarian | e7a4ccecf3 | |
DebaucheryLibrarian | ec33a8b5a9 | |
DebaucheryLibrarian | 4a9aa62831 | |
DebaucheryLibrarian | 8b5eada088 | |
DebaucheryLibrarian | eeb947d311 | |
DebaucheryLibrarian | 430d7a8cdd | |
DebaucheryLibrarian | c94dcdd9e6 | |
DebaucheryLibrarian | 46c514f530 | |
DebaucheryLibrarian | b29a34c76a | |
DebaucheryLibrarian | 20ba833147 | |
DebaucheryLibrarian | 1ed47c3173 | |
DebaucheryLibrarian | ccac1f96dd | |
DebaucheryLibrarian | c860bfebc1 | |
DebaucheryLibrarian | d5806c3d31 | |
DebaucheryLibrarian | bf36825fce | |
DebaucheryLibrarian | a1780e2c4b | |
DebaucheryLibrarian | 02850eb6e3 | |
DebaucheryLibrarian | 71efd7a96f | |
DebaucheryLibrarian | 3508e47600 | |
DebaucheryLibrarian | eef0be09b3 | |
DebaucheryLibrarian | 2df20aea38 | |
DebaucheryLibrarian | 1032c3cb57 | |
DebaucheryLibrarian | 5f26f8de27 | |
DebaucheryLibrarian | 328550b9a3 | |
DebaucheryLibrarian | 28455ecd5d | |
DebaucheryLibrarian | 54dbea659a | |
DebaucheryLibrarian | 447c1d748a | |
DebaucheryLibrarian | 4a10887e48 | |
DebaucheryLibrarian | 48015a4c95 | |
DebaucheryLibrarian | abbfedf3f7 | |
DebaucheryLibrarian | fa9153deb8 | |
DebaucheryLibrarian | b287f5c2db | |
DebaucheryLibrarian | 2ebc2d441f | |
DebaucheryLibrarian | 5f2c9eb5df | |
DebaucheryLibrarian | ce41e24434 | |
DebaucheryLibrarian | c5d81e94e5 | |
DebaucheryLibrarian | 0500bdee2b | |
DebaucheryLibrarian | e9a1df9123 | |
DebaucheryLibrarian | 277a06c3de | |
DebaucheryLibrarian | c064efc812 | |
DebaucheryLibrarian | 1408074ef4 | |
DebaucheryLibrarian | 8c1f1b69ff | |
DebaucheryLibrarian | 5783507344 | |
DebaucheryLibrarian | ae64c5225f | |
DebaucheryLibrarian | d2f81d446b | |
DebaucheryLibrarian | ab5b165c68 | |
DebaucheryLibrarian | c3d4bf0e62 | |
DebaucheryLibrarian | 60f594a948 | |
DebaucheryLibrarian | 7f74d227f0 | |
DebaucheryLibrarian | f59429c30a | |
DebaucheryLibrarian | 40276a11f9 | |
DebaucheryLibrarian | 0e846bec3f | |
DebaucheryLibrarian | ba376fa074 | |
DebaucheryLibrarian | d1b54dc2c5 | |
DebaucheryLibrarian | b9c3efa24e | |
DebaucheryLibrarian | 40e613ed8d | |
DebaucheryLibrarian | d033def947 | |
DebaucheryLibrarian | 671e110d99 | |
DebaucheryLibrarian | b7a31b7933 | |
DebaucheryLibrarian | 1082effc17 | |
DebaucheryLibrarian | 693983dc29 | |
DebaucheryLibrarian | 6fe212796b | |
DebaucheryLibrarian | bb1aa4aa55 | |
DebaucheryLibrarian | 7d77e0603b | |
DebaucheryLibrarian | 9f727a0fa0 | |
DebaucheryLibrarian | 997914ec27 | |
DebaucheryLibrarian | 37f01b68e8 | |
DebaucheryLibrarian | ca695db3ba | |
DebaucheryLibrarian | 48acabac49 | |
DebaucheryLibrarian | d7ee278b02 | |
DebaucheryLibrarian | 191a3628b5 | |
DebaucheryLibrarian | 80750b44dc | |
DebaucheryLibrarian | 25f3dcf9a5 | |
DebaucheryLibrarian | c17e44e9f9 | |
DebaucheryLibrarian | 0b101dde3c | |
DebaucheryLibrarian | 4d20dae079 | |
DebaucheryLibrarian | 86e4fb7603 | |
DebaucheryLibrarian | e8d081cc67 | |
DebaucheryLibrarian | dab38c8013 | |
DebaucheryLibrarian | c29ecac41c | |
DebaucheryLibrarian | 4187f5e7e8 | |
DebaucheryLibrarian | 2b3b2d7fd2 | |
DebaucheryLibrarian | 2783de5272 | |
DebaucheryLibrarian | 77727dff77 | |
DebaucheryLibrarian | f009c90e5d | |
DebaucheryLibrarian | f4cb4ca26a | |
DebaucheryLibrarian | 1cba51fbfd | |
DebaucheryLibrarian | c5d9b93263 | |
DebaucheryLibrarian | 88a56794aa | |
DebaucheryLibrarian | c51577098a | |
DebaucheryLibrarian | 3dff352399 | |
DebaucheryLibrarian | 6cb48647a8 | |
DebaucheryLibrarian | d6c6c3435d | |
DebaucheryLibrarian | 6f4608ba23 | |
DebaucheryLibrarian | 10ba67fde1 | |
DebaucheryLibrarian | 83e22813f3 | |
DebaucheryLibrarian | f8e7ace89f | |
DebaucheryLibrarian | 8bb46c5a6d | |
DebaucheryLibrarian | 6e79112f3a | |
DebaucheryLibrarian | 51e04e7331 | |
DebaucheryLibrarian | 9331c0af52 | |
DebaucheryLibrarian | 18744372b3 | |
DebaucheryLibrarian | 43d8b93953 | |
DebaucheryLibrarian | b0c0b1a792 | |
DebaucheryLibrarian | 717f07a09a | |
DebaucheryLibrarian | f6c1910be3 | |
DebaucheryLibrarian | a6077599bb | |
DebaucheryLibrarian | 0905847ffa | |
DebaucheryLibrarian | 66439b3b17 | |
DebaucheryLibrarian | 916deff487 | |
DebaucheryLibrarian | 05788c2ed6 | |
DebaucheryLibrarian | 13d02a44e5 | |
DebaucheryLibrarian | a2ff12a636 | |
DebaucheryLibrarian | 0a27e91de7 | |
DebaucheryLibrarian | 3c8b6e6fc1 | |
DebaucheryLibrarian | 61c84e18e4 | |
DebaucheryLibrarian | a858b2409a | |
DebaucheryLibrarian | bb204f3d85 | |
DebaucheryLibrarian | 38ce9c84ba | |
DebaucheryLibrarian | 57a8b8e2f6 | |
DebaucheryLibrarian | 4a3674feac | |
DebaucheryLibrarian | e22dbb315e | |
DebaucheryLibrarian | a339c096ef | |
DebaucheryLibrarian | a8fa1f36f8 | |
DebaucheryLibrarian | 6edd587a33 | |
DebaucheryLibrarian | a2331bc913 | |
DebaucheryLibrarian | 744bdb3170 | |
DebaucheryLibrarian | aa9e3b3d1f | |
DebaucheryLibrarian | 99f2faa328 | |
DebaucheryLibrarian | 205102ff90 | |
DebaucheryLibrarian | 01b3cc42af | |
DebaucheryLibrarian | 6de6053eaa | |
DebaucheryLibrarian | fdad61465c | |
DebaucheryLibrarian | 49a08cd576 | |
DebaucheryLibrarian | 60c4f6e6c1 | |
DebaucheryLibrarian | 57e7710f25 | |
DebaucheryLibrarian | e41d1e1ad2 | |
DebaucheryLibrarian | fbcf17d1c4 | |
DebaucheryLibrarian | f4ed4fb8d8 | |
DebaucheryLibrarian | 25a90dd52c | |
DebaucheryLibrarian | ed92919c0d | |
DebaucheryLibrarian | b5309005e9 | |
DebaucheryLibrarian | 54c501e277 | |
DebaucheryLibrarian | f36d0686a1 | |
DebaucheryLibrarian | d6b44615a0 | |
DebaucheryLibrarian | 687d4aec08 | |
DebaucheryLibrarian | 18f75595da | |
DebaucheryLibrarian | 123d4155b4 | |
DebaucheryLibrarian | b362f95790 | |
DebaucheryLibrarian | 34613a92c5 | |
DebaucheryLibrarian | 1766556c49 | |
DebaucheryLibrarian | 6bf7fc5655 | |
DebaucheryLibrarian | 7bfb08f524 | |
DebaucheryLibrarian | c4e77acdee | |
DebaucheryLibrarian | 67c1bc6b1c | |
DebaucheryLibrarian | 4e6b098448 | |
DebaucheryLibrarian | dbaddfb291 | |
DebaucheryLibrarian | 85942c5d00 | |
DebaucheryLibrarian | fe460f7441 | |
DebaucheryLibrarian | 67365507b5 | |
DebaucheryLibrarian | 87a29baf8b | |
DebaucheryLibrarian | 2f4ac4e427 | |
DebaucheryLibrarian | 0056780dc4 | |
DebaucheryLibrarian | 013675d102 | |
DebaucheryLibrarian | 236d4a9427 | |
DebaucheryLibrarian | 22512833da | |
DebaucheryLibrarian | dc231527f3 | |
DebaucheryLibrarian | d4b0f2dc67 | |
DebaucheryLibrarian | 7723b2b698 | |
DebaucheryLibrarian | 682f299c8f | |
DebaucheryLibrarian | c43bef544e | |
DebaucheryLibrarian | c4424f30ec | |
DebaucheryLibrarian | 078837f276 | |
DebaucheryLibrarian | 6534692b73 | |
DebaucheryLibrarian | 20f82c4006 | |
DebaucheryLibrarian | 128f9950ec | |
DebaucheryLibrarian | c2c329e00a | |
DebaucheryLibrarian | 5d3358ed91 | |
DebaucheryLibrarian | d7f9157424 | |
DebaucheryLibrarian | f464563dae | |
DebaucheryLibrarian | 828db2a8c8 | |
DebaucheryLibrarian | bca865068a | |
DebaucheryLibrarian | 35245ca03f | |
DebaucheryLibrarian | bcc183d5b9 | |
DebaucheryLibrarian | 433498eaed | |
DebaucheryLibrarian | 80334843c9 | |
DebaucheryLibrarian | 09a48ed064 | |
DebaucheryLibrarian | bae51dd59c | |
DebaucheryLibrarian | 58175dce21 | |
DebaucheryLibrarian | c4e4f649f5 | |
DebaucheryLibrarian | bcd3c08faa | |
DebaucheryLibrarian | 0e656ea5ca | |
DebaucheryLibrarian | d847c58d24 | |
DebaucheryLibrarian | 81f504f33e | |
DebaucheryLibrarian | 914838e367 | |
DebaucheryLibrarian | 1fc441670b | |
DebaucheryLibrarian | a16ca716da | |
DebaucheryLibrarian | d0b19752e1 | |
DebaucheryLibrarian | 9c63b31dfa | |
DebaucheryLibrarian | 9bdd3ff2f3 | |
DebaucheryLibrarian | 4429169166 | |
DebaucheryLibrarian | 3dbb74a1dc | |
DebaucheryLibrarian | e7b72f5e99 | |
DebaucheryLibrarian | 5576fed590 | |
DebaucheryLibrarian | aa0fd3cf48 | |
DebaucheryLibrarian | 56534800d8 | |
DebaucheryLibrarian | f7708e0740 | |
DebaucheryLibrarian | e36ba59d27 | |
DebaucheryLibrarian | a99cee38a0 | |
DebaucheryLibrarian | d3da2359de | |
DebaucheryLibrarian | adda78f0c6 | |
DebaucheryLibrarian | 164757ee26 | |
DebaucheryLibrarian | 7e2840a00d | |
DebaucheryLibrarian | caf37ba9fb | |
DebaucheryLibrarian | 042d3be4a9 | |
DebaucheryLibrarian | 18e91d54f1 | |
DebaucheryLibrarian | 84c59bd05a | |
DebaucheryLibrarian | e0f7db8187 | |
DebaucheryLibrarian | 13e38c487f | |
DebaucheryLibrarian | 5b6911fd5c | |
DebaucheryLibrarian | 33cab26d3b | |
DebaucheryLibrarian | c9201430ea | |
DebaucheryLibrarian | 48eeac6d88 | |
DebaucheryLibrarian | a4c82a377b | |
DebaucheryLibrarian | 421e8d0763 | |
DebaucheryLibrarian | 66f4244779 | |
DebaucheryLibrarian | 7fb832028e | |
DebaucheryLibrarian | 54798f87da | |
DebaucheryLibrarian | 5ad5708e15 | |
DebaucheryLibrarian | f356135722 | |
DebaucheryLibrarian | f3abc21482 | |
DebaucheryLibrarian | 5103a07e5f | |
DebaucheryLibrarian | 3fc63b1934 | |
DebaucheryLibrarian | 4b9a0e6bab | |
DebaucheryLibrarian | 62617ec6bf | |
DebaucheryLibrarian | 1b4d973e7b |
|
@ -10,6 +10,9 @@ config/*
|
||||||
!config/default.js
|
!config/default.js
|
||||||
assets/js/config/
|
assets/js/config/
|
||||||
!assets/js/config/default.js
|
!assets/js/config/default.js
|
||||||
|
/export*
|
||||||
|
/stashes*
|
||||||
|
/alerts*
|
||||||
*.heapprofile
|
*.heapprofile
|
||||||
*.heapsnapshot
|
*.heapsnapshot
|
||||||
.vscode
|
.vscode
|
||||||
|
|
|
@ -65,6 +65,7 @@
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<ul class="bio nolist">
|
<ul class="bio nolist">
|
||||||
|
<!-- probably not a good idea
|
||||||
<li
|
<li
|
||||||
v-if="actor.realName"
|
v-if="actor.realName"
|
||||||
class="bio-item"
|
class="bio-item"
|
||||||
|
@ -72,6 +73,7 @@
|
||||||
<dfn class="bio-label"><Icon icon="vcard" />Real name</dfn>
|
<dfn class="bio-label"><Icon icon="vcard" />Real name</dfn>
|
||||||
<span class="bio-value">{{ actor.realName }}</span>
|
<span class="bio-value">{{ actor.realName }}</span>
|
||||||
</li>
|
</li>
|
||||||
|
-->
|
||||||
|
|
||||||
<li
|
<li
|
||||||
v-if="actor.dateOfBirth"
|
v-if="actor.dateOfBirth"
|
||||||
|
@ -109,6 +111,15 @@
|
||||||
>{{ actor.ageAtDeath }}</span></span>
|
>{{ actor.ageAtDeath }}</span></span>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li
|
||||||
|
v-if="actor.orientation"
|
||||||
|
class="bio-item"
|
||||||
|
>
|
||||||
|
<dfn class="bio-label"><Icon icon="heart7" />Orientation</dfn>
|
||||||
|
|
||||||
|
<span class="orientation">{{ actor.orientation }}</span>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li
|
<li
|
||||||
v-if="actor.origin"
|
v-if="actor.origin"
|
||||||
class="bio-item birth"
|
class="bio-item birth"
|
||||||
|
@ -294,7 +305,7 @@
|
||||||
class="description"
|
class="description"
|
||||||
>
|
>
|
||||||
{{ description.text }}
|
{{ description.text }}
|
||||||
<router-link :to="`/${description.entity.type}/${description.entity.slug}`">
|
<RouterLink :to="`/${description.entity.type}/${description.entity.slug}`">
|
||||||
<img
|
<img
|
||||||
v-if="description.entity.type === 'network' || !description.entity.parent || description.entity.independent"
|
v-if="description.entity.type === 'network' || !description.entity.parent || description.entity.independent"
|
||||||
:src="`/img/logos/${description.entity.slug}/thumbs/network.png`"
|
:src="`/img/logos/${description.entity.slug}/thumbs/network.png`"
|
||||||
|
@ -306,7 +317,7 @@
|
||||||
:src="`/img/logos/${description.entity.parent.slug}/thumbs/${description.entity.slug}.png`"
|
:src="`/img/logos/${description.entity.parent.slug}/thumbs/${description.entity.slug}.png`"
|
||||||
class="description-logo"
|
class="description-logo"
|
||||||
>
|
>
|
||||||
</router-link>
|
</RouterLink>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -380,6 +391,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import config from 'config';
|
||||||
|
|
||||||
import Pagination from '../pagination/pagination.vue';
|
import Pagination from '../pagination/pagination.vue';
|
||||||
import FilterBar from '../filters/filter-bar.vue';
|
import FilterBar from '../filters/filter-bar.vue';
|
||||||
import Releases from '../releases/releases.vue';
|
import Releases from '../releases/releases.vue';
|
||||||
|
@ -471,7 +484,7 @@ export default {
|
||||||
releases: null,
|
releases: null,
|
||||||
done: false,
|
done: false,
|
||||||
totalCount: 0,
|
totalCount: 0,
|
||||||
limit: 20,
|
limit: Number(this.$route.query.limit || 30) - config.campaigns.tiles, // reserve one campaign spot
|
||||||
pageTitle: null,
|
pageTitle: null,
|
||||||
bioExpanded: false,
|
bioExpanded: false,
|
||||||
photosExpanded: false,
|
photosExpanded: false,
|
||||||
|
@ -685,7 +698,8 @@ export default {
|
||||||
|
|
||||||
.ethnicity,
|
.ethnicity,
|
||||||
.hair,
|
.hair,
|
||||||
.eyes {
|
.eyes,
|
||||||
|
.orientation {
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<router-link
|
<RouterLink
|
||||||
:to="`/actor/${actor.id}/${actor.slug}`"
|
:to="`/actor/${actor.id}/${actor.slug}`"
|
||||||
|
:target="target"
|
||||||
class="actor nolink"
|
class="actor nolink"
|
||||||
>
|
>
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
|
@ -18,7 +19,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="name">{{ actor.name }}</span>
|
<span class="name">{{ actor.name }}</span>
|
||||||
</router-link>
|
</RouterLink>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -36,6 +37,10 @@ export default {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
target: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
unstashActor,
|
unstashActor,
|
||||||
|
|
|
@ -41,9 +41,9 @@
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<a
|
<RouterLink
|
||||||
|
:to="hasScrolled ? '' : { name: 'actor', params: { actorId: actor.id, actorSlug: actor.slug } }"
|
||||||
class="avatar-container"
|
class="avatar-container"
|
||||||
@click="goToActor"
|
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
v-if="actor.avatar"
|
v-if="actor.avatar"
|
||||||
|
@ -67,21 +67,21 @@
|
||||||
v-show="(!stash || stash.primary) && favorited"
|
v-show="(!stash || stash.primary) && favorited"
|
||||||
icon="heart7"
|
icon="heart7"
|
||||||
class="stash stashed"
|
class="stash stashed"
|
||||||
@click.prevent.native="unstashActor"
|
@click.stop.native="unstashActor"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Icon
|
<Icon
|
||||||
v-show="(!stash || stash.primary) && favorited === false"
|
v-show="(!stash || stash.primary) && favorited === false"
|
||||||
icon="heart8"
|
icon="heart8"
|
||||||
class="stash unstashed"
|
class="stash unstashed"
|
||||||
@click.prevent.native="stashActor"
|
@click.stop.native="stashActor"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Icon
|
<Icon
|
||||||
v-show="stash && !stash.primary"
|
v-show="stash && !stash.primary"
|
||||||
icon="cross2"
|
icon="cross2"
|
||||||
class="stash unstash"
|
class="stash unstash"
|
||||||
@click.prevent.native="unstashActor"
|
@click.stop.native="unstashActor"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<span class="details">
|
<span class="details">
|
||||||
|
@ -130,7 +130,7 @@
|
||||||
class="country"
|
class="country"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -138,13 +138,6 @@
|
||||||
<script>
|
<script>
|
||||||
import Gender from './gender.vue';
|
import Gender from './gender.vue';
|
||||||
|
|
||||||
function goToActor() {
|
|
||||||
// can't seem to control behavior with RouterLink
|
|
||||||
if (!this.hasScrolled) {
|
|
||||||
this.$router.push({ name: 'actor', params: { actorId: this.actor.id, actorSlug: this.actor.slug } });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function stashActor() {
|
async function stashActor() {
|
||||||
this.favorited = true;
|
this.favorited = true;
|
||||||
|
|
||||||
|
@ -215,7 +208,6 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
stashActor,
|
stashActor,
|
||||||
unstashActor,
|
unstashActor,
|
||||||
goToActor,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -3,13 +3,28 @@
|
||||||
title="Add alert"
|
title="Add alert"
|
||||||
@close="$emit('close')"
|
@close="$emit('close')"
|
||||||
>
|
>
|
||||||
|
<div
|
||||||
|
v-if="error"
|
||||||
|
class="dialog-error"
|
||||||
|
>{{ error }}</div>
|
||||||
|
|
||||||
<form
|
<form
|
||||||
class="dialog-body"
|
class="dialog-body"
|
||||||
@submit.prevent="addAlert"
|
@submit.prevent="addAlert"
|
||||||
>
|
>
|
||||||
<div class="dialog-section">
|
<div class="dialog-section">
|
||||||
<h3 class="dialog-heading">
|
<h3 class="dialog-heading">
|
||||||
When<span class="dialog-description">All to appear in the same scene</span>
|
When
|
||||||
|
|
||||||
|
<label class="dialog-description noselect">
|
||||||
|
<template v-if="all">Scene must match <strong>all</strong> fields</template>
|
||||||
|
<template v-else>Scene must match <strong>any</strong> field</template>
|
||||||
|
|
||||||
|
<Toggle
|
||||||
|
:checked="all"
|
||||||
|
@change="(checked) => all = checked"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div class="alert-section">
|
<div class="alert-section">
|
||||||
|
@ -29,7 +44,10 @@
|
||||||
:key="`actor-${actor.id}`"
|
:key="`actor-${actor.id}`"
|
||||||
class="actor"
|
class="actor"
|
||||||
>
|
>
|
||||||
<ActorPreview :actor="actor" />
|
<ActorPreview
|
||||||
|
:actor="actor"
|
||||||
|
target="_blank"
|
||||||
|
/>
|
||||||
|
|
||||||
<Icon
|
<Icon
|
||||||
icon="cross3"
|
icon="cross3"
|
||||||
|
@ -104,10 +122,15 @@
|
||||||
|
|
||||||
<div class="entities">
|
<div class="entities">
|
||||||
<div
|
<div
|
||||||
v-if="entity"
|
v-for="(entity, index) in entities"
|
||||||
|
:key="`entity-${entity.id}`"
|
||||||
|
:class="{ invalid: all && index > 0 }"
|
||||||
class="entity"
|
class="entity"
|
||||||
>
|
>
|
||||||
<Entity :entity="entity" />
|
<Entity
|
||||||
|
:entity="entity"
|
||||||
|
target="_blank"
|
||||||
|
/>
|
||||||
|
|
||||||
<Icon
|
<Icon
|
||||||
icon="cross3"
|
icon="cross3"
|
||||||
|
@ -116,7 +139,7 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Tooltip v-if="!entity">
|
<Tooltip v-if="entities.length < 1 || !all">
|
||||||
<div class="entity placeholder">
|
<div class="entity placeholder">
|
||||||
Any channel
|
Any channel
|
||||||
|
|
||||||
|
@ -136,6 +159,72 @@
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="alert-section">
|
||||||
|
<h4 class="alert-heading">Matching</h4>
|
||||||
|
|
||||||
|
<ul class="matches nolist">
|
||||||
|
<li
|
||||||
|
v-for="(match, index) in matches"
|
||||||
|
:key="`match-${index}`"
|
||||||
|
class="match"
|
||||||
|
>
|
||||||
|
<span class="match-property">{{ match.property }}: </span>
|
||||||
|
|
||||||
|
<span
|
||||||
|
v-if="match.expression.slice(0, 1) === '/' && match.expression.slice(-1) === '/'"
|
||||||
|
class="match-expression"
|
||||||
|
><span class="match-slash">/</span>{{ match.expression.slice(1, -1) }}<span class="match-slash">/</span></span>
|
||||||
|
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
class="match-expression"
|
||||||
|
>{{ match.expression }}</span>
|
||||||
|
|
||||||
|
<Icon
|
||||||
|
icon="cross3"
|
||||||
|
class="remove"
|
||||||
|
@click.native="removeMatch(index)"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<Tooltip
|
||||||
|
v-if="!entity"
|
||||||
|
@open="$refs.expression?.focus()"
|
||||||
|
>
|
||||||
|
<li class="match placeholder">
|
||||||
|
Anything
|
||||||
|
|
||||||
|
<Icon
|
||||||
|
icon="plus3"
|
||||||
|
class="add"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<template #tooltip>
|
||||||
|
<form
|
||||||
|
class="pattern-tooltip"
|
||||||
|
@submit.prevent="addMatch"
|
||||||
|
>
|
||||||
|
<select
|
||||||
|
v-model="matchProperty"
|
||||||
|
class="input"
|
||||||
|
>
|
||||||
|
<option value="title">Title</option>
|
||||||
|
<option value="description">Description</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<input
|
||||||
|
ref="expression"
|
||||||
|
v-model="matchExpression"
|
||||||
|
class="input"
|
||||||
|
placeholder="Expression, // for RegExp"
|
||||||
|
>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
</Tooltip>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dialog-section">
|
<div class="dialog-section">
|
||||||
|
@ -194,7 +283,7 @@
|
||||||
|
|
||||||
<div class="dialog-actions right">
|
<div class="dialog-actions right">
|
||||||
<button
|
<button
|
||||||
:disabled="actors.length === 0 && tags.length === 0 && !entity"
|
:disabled="actors.length === 0 && tags.length === 0 && !entity && matches.length === 0"
|
||||||
type="submit"
|
type="submit"
|
||||||
class="button button-primary"
|
class="button button-primary"
|
||||||
>Add alert</button>
|
>Add alert</button>
|
||||||
|
@ -207,19 +296,28 @@
|
||||||
import ActorPreview from '../actors/preview.vue';
|
import ActorPreview from '../actors/preview.vue';
|
||||||
import Entity from '../entities/tile.vue';
|
import Entity from '../entities/tile.vue';
|
||||||
import Checkbox from '../form/checkbox.vue';
|
import Checkbox from '../form/checkbox.vue';
|
||||||
|
import Toggle from '../form/toggle.vue';
|
||||||
import Search from './search.vue';
|
import Search from './search.vue';
|
||||||
|
|
||||||
async function addAlert() {
|
async function addAlert() {
|
||||||
|
this.error = null;
|
||||||
|
|
||||||
|
try {
|
||||||
await this.$store.dispatch('addAlert', {
|
await this.$store.dispatch('addAlert', {
|
||||||
|
all: this.all,
|
||||||
actors: this.actors.map((actor) => actor.id),
|
actors: this.actors.map((actor) => actor.id),
|
||||||
tags: this.tags.map((tag) => tag.id),
|
tags: this.tags.map((tag) => tag.id),
|
||||||
entity: this.entity?.id,
|
matches: this.matches,
|
||||||
|
entities: this.entities.map((entity) => entity.id),
|
||||||
notify: this.notify,
|
notify: this.notify,
|
||||||
email: this.email,
|
email: this.email,
|
||||||
stashes: this.stashes.map((stash) => stash.id),
|
stashes: this.stashes.map((stash) => stash.id),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$emit('close', true);
|
this.$emit('close', true);
|
||||||
|
} catch (error) {
|
||||||
|
this.error = error.message;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addActor(actor) {
|
function addActor(actor) {
|
||||||
|
@ -231,7 +329,7 @@ function addActor(actor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function addEntity(entity) {
|
function addEntity(entity) {
|
||||||
this.entity = entity;
|
this.entities = this.entities.concat(entity);
|
||||||
this.events.emit('blur');
|
this.events.emit('blur');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,14 +345,34 @@ function removeActor(actor) {
|
||||||
this.actors = this.actors.filter((listedActor) => listedActor.id !== actor.id);
|
this.actors = this.actors.filter((listedActor) => listedActor.id !== actor.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeEntity() {
|
function removeEntity(entity) {
|
||||||
this.entity = null;
|
this.entities = this.entities.filter((alertEntity) => alertEntity.id !== entity.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeTag(tag) {
|
function removeTag(tag) {
|
||||||
this.tags = this.tags.filter((listedTag) => listedTag.id !== tag.id);
|
this.tags = this.tags.filter((listedTag) => listedTag.id !== tag.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addMatch() {
|
||||||
|
if (!this.matchExpression) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.matches = this.matches.concat({
|
||||||
|
property: this.matchProperty,
|
||||||
|
expression: this.matchExpression,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.matchProperty = 'title';
|
||||||
|
this.matchExpression = null;
|
||||||
|
|
||||||
|
this.events.emit('blur');
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeMatch(removeIndex) {
|
||||||
|
this.matches = this.matches.filter((match, matchIndex) => matchIndex !== removeIndex);
|
||||||
|
}
|
||||||
|
|
||||||
function addStash(stash) {
|
function addStash(stash) {
|
||||||
if (!this.stashes.some((selectedStash) => selectedStash.id === stash.id)) {
|
if (!this.stashes.some((selectedStash) => selectedStash.id === stash.id)) {
|
||||||
this.stashes = this.stashes.concat(stash);
|
this.stashes = this.stashes.concat(stash);
|
||||||
|
@ -273,13 +391,19 @@ export default {
|
||||||
Checkbox,
|
Checkbox,
|
||||||
Entity,
|
Entity,
|
||||||
Search,
|
Search,
|
||||||
|
Toggle,
|
||||||
},
|
},
|
||||||
emits: ['close'],
|
emits: ['close'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
error: null,
|
||||||
actors: [],
|
actors: [],
|
||||||
tags: [],
|
tags: [],
|
||||||
entity: null,
|
all: true,
|
||||||
|
entities: [],
|
||||||
|
matches: [],
|
||||||
|
matchProperty: 'title',
|
||||||
|
matchExpression: null,
|
||||||
notify: true,
|
notify: true,
|
||||||
email: false,
|
email: false,
|
||||||
stashes: [],
|
stashes: [],
|
||||||
|
@ -290,10 +414,12 @@ export default {
|
||||||
addActor,
|
addActor,
|
||||||
addAlert,
|
addAlert,
|
||||||
addEntity,
|
addEntity,
|
||||||
|
addMatch,
|
||||||
addTag,
|
addTag,
|
||||||
addStash,
|
addStash,
|
||||||
removeActor,
|
removeActor,
|
||||||
removeEntity,
|
removeEntity,
|
||||||
|
removeMatch,
|
||||||
removeTag,
|
removeTag,
|
||||||
removeStash,
|
removeStash,
|
||||||
},
|
},
|
||||||
|
@ -320,9 +446,24 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-description {
|
.dialog-description {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
color: var(--shadow);
|
color: var(--shadow);
|
||||||
font-size: .9rem;
|
font-size: .9rem;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
|
|
||||||
|
.toggle-container {
|
||||||
|
margin-left: .5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-error {
|
||||||
|
padding: 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
background: var(--error);
|
||||||
|
color: var(--text-light);
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.alert-heading {
|
.alert-heading {
|
||||||
|
@ -338,6 +479,34 @@ export default {
|
||||||
font-size: 0;
|
font-size: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.match {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: .25rem 0;
|
||||||
|
font-family: inherit;
|
||||||
|
|
||||||
|
.remove {
|
||||||
|
position: relative;
|
||||||
|
top: -.1rem;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.match-property {
|
||||||
|
text-transform: capitalize;
|
||||||
|
color: var(--shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.match-expression {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.match-slash {
|
||||||
|
padding: 0 .1rem;
|
||||||
|
color: var(--primary);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
.actors > .actor,
|
.actors > .actor,
|
||||||
.entity,
|
.entity,
|
||||||
.tag,
|
.tag,
|
||||||
|
@ -347,6 +516,11 @@ export default {
|
||||||
margin: 0 .5rem .5rem 0;
|
margin: 0 .5rem .5rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.entity.invalid {
|
||||||
|
opacity: .5;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
.entity .tile {
|
.entity .tile {
|
||||||
width: 10rem;
|
width: 10rem;
|
||||||
height: 2.5rem;
|
height: 2.5rem;
|
||||||
|
@ -366,6 +540,14 @@ export default {
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pattern-tooltip {
|
||||||
|
display: flex;
|
||||||
|
gap: .5rem;
|
||||||
|
position: relative;
|
||||||
|
padding: .5rem;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.remove {
|
.remove {
|
||||||
width: 1rem;
|
width: 1rem;
|
||||||
height: 1rem;
|
height: 1rem;
|
||||||
|
|
|
@ -1,21 +1,28 @@
|
||||||
<template>
|
<template>
|
||||||
|
<iframe
|
||||||
|
v-if="campaign?.banner?.type === 'html'"
|
||||||
|
ref="iframe"
|
||||||
|
:width="campaign.banner.width"
|
||||||
|
:height="campaign.banner.height"
|
||||||
|
:src="getSource(campaign)"
|
||||||
|
scrolling="none"
|
||||||
|
marginwidth="0"
|
||||||
|
marginheight="0"
|
||||||
|
class="campaign frame"
|
||||||
|
data-umami-event="campaign-click"
|
||||||
|
:data-umami-event-campaign-id="`${campaign.entity.slug}-${campaign.id}`"
|
||||||
|
/>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
v-if="campaign"
|
v-else-if="campaign"
|
||||||
:href="campaign.url || campaign.affiliate?.url"
|
:href="campaign.url || campaign.affiliate?.url"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="campaign"
|
class="campaign"
|
||||||
|
data-umami-event="campaign-click"
|
||||||
|
:data-umami-event-campaign-id="`${campaign.entity.slug}-${campaign.id}`"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
v-if="campaign.banner.entity.type === 'network' || !campaign.banner.entity.parent"
|
:src="getSource(campaign)"
|
||||||
:src="`/img/banners/${campaign.banner.entity.slug}/${campaign.banner.id}.${campaign.banner.type || 'jpg'}`"
|
|
||||||
:width="campaign.banner.width"
|
|
||||||
:height="campaign.banner.height"
|
|
||||||
class="campaign-banner"
|
|
||||||
>
|
|
||||||
|
|
||||||
<img
|
|
||||||
v-if="campaign.banner.entity.type === 'channel' && campaign.banner.entity.parent?.type === 'network'"
|
|
||||||
:src="`/img/banners/${campaign.banner.entity.parent.slug}/${campaign.banner.entity.slug}/${campaign.banner.id}.${campaign.banner.type || 'jpg'}`"
|
|
||||||
:width="campaign.banner.width"
|
:width="campaign.banner.width"
|
||||||
:height="campaign.banner.height"
|
:height="campaign.banner.height"
|
||||||
class="campaign-banner"
|
class="campaign-banner"
|
||||||
|
@ -37,6 +44,11 @@ function ratioFilter(banner) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (banner.type === 'html' && banner.width > window.innerWidth) {
|
||||||
|
// usually non-scalable iframes
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.minRatio && banner.ratio < this.minRatio) {
|
if (this.minRatio && banner.ratio < this.minRatio) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -48,6 +60,18 @@ function ratioFilter(banner) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSource(campaign) {
|
||||||
|
if (campaign.banner.entity.type === 'network' || !campaign.banner.entity.parent) {
|
||||||
|
return `/banners/${campaign.banner.entity.slug}/${campaign.banner.id}.${campaign.banner.type || 'jpg'}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (campaign.banner.entity.type === 'channel' && campaign.banner.entity.parent?.type === 'network') {
|
||||||
|
return `/banners/${campaign.banner.entity.parent.slug}/${campaign.banner.entity.slug}/${campaign.banner.id}.${campaign.banner.type || 'jpg'}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
function entityCampaign() {
|
function entityCampaign() {
|
||||||
const bannerCampaigns = this.entity.campaigns
|
const bannerCampaigns = this.entity.campaigns
|
||||||
.concat(this.entity.children?.flatMap((child) => child.campaigns))
|
.concat(this.entity.children?.flatMap((child) => child.campaigns))
|
||||||
|
@ -63,6 +87,10 @@ function entityCampaign() {
|
||||||
return randomCampaign;
|
return randomCampaign;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.allowGeneric) {
|
||||||
|
return this.genericCampaign();
|
||||||
|
}
|
||||||
|
|
||||||
this.$emit('campaign', null);
|
this.$emit('campaign', null);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -84,6 +112,10 @@ function tagCampaign() {
|
||||||
return randomCampaign;
|
return randomCampaign;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.allowGeneric) {
|
||||||
|
return this.genericCampaign();
|
||||||
|
}
|
||||||
|
|
||||||
this.$emit('campaign', null);
|
this.$emit('campaign', null);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -98,7 +130,21 @@ async function genericCampaign() {
|
||||||
return randomCampaign;
|
return randomCampaign;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function specificCampaign(campaignId) {
|
||||||
|
const campaign = await this.$store.dispatch('fetchCampaign', campaignId);
|
||||||
|
|
||||||
|
this.campaign = campaign;
|
||||||
|
this.$emit('campaign', campaign);
|
||||||
|
|
||||||
|
return campaign;
|
||||||
|
}
|
||||||
|
|
||||||
async function mounted() {
|
async function mounted() {
|
||||||
|
if (this.$route.query.campaign) {
|
||||||
|
await this.specificCampaign(this.$route.query.campaign);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.entity) {
|
if (this.entity) {
|
||||||
await this.entityCampaign();
|
await this.entityCampaign();
|
||||||
return;
|
return;
|
||||||
|
@ -134,6 +180,10 @@ export default {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
allowGeneric: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
maxRatio: {
|
maxRatio: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: null,
|
default: null,
|
||||||
|
@ -149,7 +199,9 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
entityCampaign,
|
entityCampaign,
|
||||||
genericCampaign,
|
genericCampaign,
|
||||||
|
getSource,
|
||||||
ratioFilter,
|
ratioFilter,
|
||||||
|
specificCampaign,
|
||||||
tagCampaign,
|
tagCampaign,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -157,15 +209,29 @@ export default {
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.campaign {
|
.campaign {
|
||||||
height: 100%;
|
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.campaign-banner {
|
.campaign-banner {
|
||||||
height: auto;
|
height: auto;
|
||||||
|
width: auto;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.frame-container {
|
||||||
|
position: relative;
|
||||||
|
font-size: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame-target {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -16,7 +16,14 @@
|
||||||
<p
|
<p
|
||||||
v-if="config.showDisclaimer"
|
v-if="config.showDisclaimer"
|
||||||
class="disclaimer"
|
class="disclaimer"
|
||||||
>{{ config.disclaimer }}</p>
|
v-html="config.disclaimer"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<p
|
||||||
|
v-if="config.showAnnouncement"
|
||||||
|
class="announcement"
|
||||||
|
v-html="config.announcement"
|
||||||
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
ref="content"
|
ref="content"
|
||||||
|
@ -89,17 +96,29 @@ function scrollToTop() {
|
||||||
this.$refs.content.scrollTop = 0;
|
this.$refs.content.scrollTop = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function trackIframeCampaign() {
|
||||||
|
// no way to capture clicks from an iframe directly
|
||||||
|
if (window.umami && document.activeElement.tagName === 'IFRAME' && document.activeElement.dataset.umamiEvent === 'campaign-click') {
|
||||||
|
window.umami.track('campaign-click', {
|
||||||
|
'campaign-id': document.activeElement.dataset.umamiEventCampaignId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function mounted() {
|
function mounted() {
|
||||||
document.addEventListener('click', this.blur);
|
document.addEventListener('click', this.blur);
|
||||||
window.addEventListener('resize', this.resize);
|
window.addEventListener('resize', this.resize);
|
||||||
|
|
||||||
this.events.on('toggleSettings', this.toggleSettings);
|
this.events.on('toggleSettings', this.toggleSettings);
|
||||||
this.events.on('toggleSidebar', this.toggleSidebar);
|
this.events.on('toggleSidebar', this.toggleSidebar);
|
||||||
|
|
||||||
|
window.addEventListener('blur', this.trackIframeCampaign);
|
||||||
}
|
}
|
||||||
|
|
||||||
function beforeUnmount() {
|
function beforeUnmount() {
|
||||||
document.removeEventListener('click', this.blur);
|
document.removeEventListener('click', this.blur);
|
||||||
window.removeEventListener('resize', this.resize);
|
window.removeEventListener('resize', this.resize);
|
||||||
|
window.removeEventListener('blur', this.trackIframeCampaign);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -122,10 +141,11 @@ export default {
|
||||||
mounted,
|
mounted,
|
||||||
beforeUnmount,
|
beforeUnmount,
|
||||||
methods: {
|
methods: {
|
||||||
|
setConsent,
|
||||||
toggleSidebar,
|
toggleSidebar,
|
||||||
toggleFilters,
|
toggleFilters,
|
||||||
toggleSettings,
|
toggleSettings,
|
||||||
setConsent,
|
trackIframeCampaign,
|
||||||
blur,
|
blur,
|
||||||
resize,
|
resize,
|
||||||
scroll,
|
scroll,
|
||||||
|
@ -194,13 +214,21 @@ export default {
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.disclaimer {
|
.disclaimer,
|
||||||
|
.announcement {
|
||||||
padding: .5rem 1rem;
|
padding: .5rem 1rem;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--text-light);
|
color: var(--text-light);
|
||||||
background: var(--warn);
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
box-shadow: inset 0 0 3px var(--darken-weak);
|
box-shadow: inset 0 0 3px var(--darken-weak);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.disclaimer {
|
||||||
|
background: var(--warn);
|
||||||
|
}
|
||||||
|
|
||||||
|
.announcement {
|
||||||
|
background: var(--notice);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -96,7 +96,10 @@
|
||||||
/>
|
/>
|
||||||
</Scroll>
|
</Scroll>
|
||||||
|
|
||||||
<div class="campaign-container">
|
<div
|
||||||
|
v-if="config.campaigns.entity"
|
||||||
|
class="campaign-container"
|
||||||
|
>
|
||||||
<Campaign
|
<Campaign
|
||||||
:entity="entity"
|
:entity="entity"
|
||||||
:min-ratio="3"
|
:min-ratio="3"
|
||||||
|
@ -114,6 +117,7 @@
|
||||||
<div class="releases">
|
<div class="releases">
|
||||||
<Releases
|
<Releases
|
||||||
:releases="entity.releases"
|
:releases="entity.releases"
|
||||||
|
:entity="entity"
|
||||||
:done="done"
|
:done="done"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -130,6 +134,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import config from 'config';
|
||||||
|
|
||||||
import FilterBar from '../filters/filter-bar.vue';
|
import FilterBar from '../filters/filter-bar.vue';
|
||||||
import Pagination from '../pagination/pagination.vue';
|
import Pagination from '../pagination/pagination.vue';
|
||||||
import Releases from '../releases/releases.vue';
|
import Releases from '../releases/releases.vue';
|
||||||
|
@ -157,12 +163,17 @@ async function fetchEntity(scroll = true) {
|
||||||
const campaign = entity.campaigns.find((campaignX) => !campaignX.banner)
|
const campaign = entity.campaigns.find((campaignX) => !campaignX.banner)
|
||||||
|| entity.parent?.campaigns.find((campaignX) => !campaignX.banner);
|
|| entity.parent?.campaigns.find((campaignX) => !campaignX.banner);
|
||||||
|
|
||||||
|
if (entity.url) {
|
||||||
|
const { searchParams, pathname, origin } = new URL(entity.url);
|
||||||
|
|
||||||
const affiliateParams = new URLSearchParams({
|
const affiliateParams = new URLSearchParams({
|
||||||
...(entity.url && Object.fromEntries(new URL(entity.url).searchParams)), // preserve any query in entity URL, e.g. ?siteId=5
|
...(entity.url && Object.fromEntries(searchParams)), // preserve any query in entity URL, e.g. ?siteId=5
|
||||||
...(campaign?.affiliate?.parameters && Object.fromEntries(new URLSearchParams(campaign.affiliate.parameters))), // append affiliate parameters
|
...(campaign?.affiliate?.parameters && Object.fromEntries(new URLSearchParams(campaign.affiliate.parameters))), // append affiliate parameters
|
||||||
}).toString();
|
}).toString();
|
||||||
|
|
||||||
this.entityUrl = campaign?.url || campaign?.affiliate?.url || `${entity.url}${campaign?.affiliate?.parameters ? `?${affiliateParams}` : ''}`;
|
this.entityUrl = campaign?.url || campaign?.affiliate?.url || `${origin}${pathname}${campaign?.affiliate?.parameters ? `?${affiliateParams}` : ''}`;
|
||||||
|
}
|
||||||
|
|
||||||
this.done = true;
|
this.done = true;
|
||||||
|
|
||||||
if (scroll && this.$refs.filter?.$el) {
|
if (scroll && this.$refs.filter?.$el) {
|
||||||
|
@ -196,7 +207,7 @@ export default {
|
||||||
pageTitle: null,
|
pageTitle: null,
|
||||||
totalCount: null,
|
totalCount: null,
|
||||||
done: false,
|
done: false,
|
||||||
limit: Number(this.$route.query.limit) || 20,
|
limit: Number(this.$route.query.limit || 30) - config.campaigns.tiles, // reserve one campaign spot
|
||||||
expanded: false,
|
expanded: false,
|
||||||
entityUrl: null,
|
entityUrl: null,
|
||||||
};
|
};
|
||||||
|
@ -286,6 +297,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.campaign-container {
|
.campaign-container {
|
||||||
|
max-height: 150px;
|
||||||
background: var(--background-dim);
|
background: var(--background-dim);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: .5rem;
|
padding: .5rem;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<router-link
|
<RouterLink
|
||||||
:to="`/${entity.type}/${entity.slug}`"
|
:to="`/${entity.type}/${entity.slug}`"
|
||||||
:title="entity.name"
|
:title="entity.name"
|
||||||
|
:target="target"
|
||||||
class="tile"
|
class="tile"
|
||||||
>
|
>
|
||||||
<div class="tile-logo">
|
<div class="tile-logo">
|
||||||
|
@ -47,7 +48,7 @@
|
||||||
<span v-if="typeof entity.sceneTotal !== 'undefined'">{{ entity.sceneTotal }} scenes</span>
|
<span v-if="typeof entity.sceneTotal !== 'undefined'">{{ entity.sceneTotal }} scenes</span>
|
||||||
<span v-if="entity.type === 'network'">{{ entity.childrenTotal }} channels</span>
|
<span v-if="entity.type === 'network'">{{ entity.childrenTotal }} channels</span>
|
||||||
</span>
|
</span>
|
||||||
</router-link>
|
</RouterLink>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -57,6 +58,10 @@ export default {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
target: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
emits: ['load'],
|
emits: ['load'],
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
<RouterLink
|
<RouterLink
|
||||||
v-if="me && favorites"
|
v-if="me && favorites"
|
||||||
:to="{ name: 'stash', params: { stashId: favorites.id, range: 'scenes', pageNumber: 1 } }"
|
:to="{ name: 'stash', params: { stashId: favorites.id, stashSlug: favorites.slug, username: me.username, range: 'scenes', pageNumber: 1 } }"
|
||||||
class="menu-item"
|
class="menu-item"
|
||||||
><Icon icon="heart7" />Favorites</RouterLink>
|
><Icon icon="heart7" />Favorites</RouterLink>
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,7 @@
|
||||||
|
|
||||||
<Icon
|
<Icon
|
||||||
v-if="notification.alert"
|
v-if="notification.alert"
|
||||||
v-tooltip="`You set an alert for <strong>${notification.alert.tags.map(tag => tag.name).join(', ') || 'all'}</strong> scenes with <strong>${notification.alert.actors.map(actor => actor.name).join(', ') || 'any actor'}</strong> for <strong>${notification.alert.entity?.name || 'any channel'}</strong>`"
|
v-tooltip="`You set an alert for scenes with <strong>${notification.alert.all ? 'all of' : 'any of'}</strong> <strong>${notification.alert.actors.map(actor => actor.name).join(', ') || 'any actor'}</strong> containing <strong>${notification.alert.tags.map(tag => tag.name).join(', ') || 'any tags'}</strong> from <strong>${notification.alert.entities.map((entity) => entity.name).join(', ') || 'any channel'}</strong> matching <strong>${notification.alert.matches.map((match) => `${match.property}: ${match.expression}`).join(', ') || 'any text'}</strong>`"
|
||||||
icon="question5"
|
icon="question5"
|
||||||
@click.prevent.stop
|
@click.prevent.stop
|
||||||
/>
|
/>
|
||||||
|
@ -145,12 +145,12 @@ export default {
|
||||||
default: 0,
|
default: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
emits: ['addAlert'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showAddAlert: false,
|
showAddAlert: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
emits: ['addAlert'],
|
|
||||||
methods: {
|
methods: {
|
||||||
checkNotifications,
|
checkNotifications,
|
||||||
checkNotification,
|
checkNotification,
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="home">
|
<div class="home">
|
||||||
<div class="content-inner">
|
<div class="content-inner">
|
||||||
<div class="campaign-container">
|
<div
|
||||||
|
v-if="config.campaigns.home"
|
||||||
|
class="campaign-container"
|
||||||
|
>
|
||||||
<Campaign
|
<Campaign
|
||||||
:min-ratio="6"
|
:min-ratio="6"
|
||||||
/>
|
/>
|
||||||
|
@ -35,6 +38,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import config from 'config';
|
||||||
|
|
||||||
import FilterBar from '../filters/filter-bar.vue';
|
import FilterBar from '../filters/filter-bar.vue';
|
||||||
import Releases from '../releases/releases.vue';
|
import Releases from '../releases/releases.vue';
|
||||||
import Pagination from '../pagination/pagination.vue';
|
import Pagination from '../pagination/pagination.vue';
|
||||||
|
@ -76,7 +81,7 @@ export default {
|
||||||
releases: [],
|
releases: [],
|
||||||
networks: [],
|
networks: [],
|
||||||
pageTitle: null,
|
pageTitle: null,
|
||||||
limit: 30,
|
limit: Number(this.$route.query.limit || 30) - config.campaigns.tiles, // reserve one campaign spot
|
||||||
totalCount: 0,
|
totalCount: 0,
|
||||||
from: null,
|
from: null,
|
||||||
done: false,
|
done: false,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
:class="{ active }"
|
:class="{ active }"
|
||||||
|
:title="title"
|
||||||
class="icon"
|
class="icon"
|
||||||
v-html="svg"
|
v-html="svg"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="media-container">
|
<div class="media-container">
|
||||||
<div
|
<div
|
||||||
class="media"
|
class="media"
|
||||||
:class="{ center: (release.photos?.length || 0) + (release.scenesPhotos?.length || 0) < 2, preview: !me }"
|
:class="{ center: (release.photos?.length || 0) + (release.caps?.length || 0) + (release.scenesPhotos?.length || 0) < 2, preview: !me }"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="release.trailer || release.teaser"
|
v-if="release.trailer || release.teaser"
|
||||||
|
@ -169,9 +169,10 @@ function photos() {
|
||||||
const clips = this.release.clips || [];
|
const clips = this.release.clips || [];
|
||||||
const clipPostersById = clips.reduce((acc, clip) => ({ ...acc, [clip.poster.id]: clip.poster }), {});
|
const clipPostersById = clips.reduce((acc, clip) => ({ ...acc, [clip.poster.id]: clip.poster }), {});
|
||||||
const uniqueClipPosters = Array.from(new Set(clips.map((clip) => clip.poster.id) || [])).map((posterId) => clipPostersById[posterId]);
|
const uniqueClipPosters = Array.from(new Set(clips.map((clip) => clip.poster.id) || [])).map((posterId) => clipPostersById[posterId]);
|
||||||
const photosWithClipPosters = (this.release.photos || []).concat(this.release.scenesPhotos || []).concat(uniqueClipPosters);
|
const photosWithClipPosters = (this.release.photos || []).concat(this.release.caps || []).concat(this.release.scenesPhotos || []).concat(uniqueClipPosters);
|
||||||
|
|
||||||
if (this.release.trailer || (this.release.teaser && this.release.teaser.mime !== 'image/gif')) {
|
if (this.release.trailer || (this.release.teaser && this.release.teaser.mime !== 'image/gif')) {
|
||||||
|
// if (this.release.trailer) {
|
||||||
// poster will be on trailer video
|
// poster will be on trailer video
|
||||||
return photosWithClipPosters;
|
return photosWithClipPosters;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,21 +21,24 @@
|
||||||
<Details :release="release" />
|
<Details :release="release" />
|
||||||
|
|
||||||
<button
|
<button
|
||||||
v-if="release.photos?.length > 0 || release.scenesPhotos?.length > 0"
|
v-if="showAlbum"
|
||||||
class="album-toggle"
|
class="album-toggle"
|
||||||
@click="$router.push({ hash: '#album' })"
|
@click="$router.push({ hash: '#album' })"
|
||||||
><Icon icon="grid3" />View album</button>
|
><Icon icon="grid3" />View album</button>
|
||||||
|
|
||||||
<Album
|
<Album
|
||||||
v-if="showAlbum"
|
v-if="showAlbum && $route.hash === '#album'"
|
||||||
:items="[release.poster, ...(release.photos || []), ...(release.scenesPhotos || [])]"
|
:items="[release.poster, ...(release.photos || []), ...(release.caps || []), ...(release.scenesPhotos || [])]"
|
||||||
:title="release.title"
|
:title="release.title"
|
||||||
:path="config.media.mediaPath"
|
:path="config.media.mediaPath"
|
||||||
@close="$router.replace({ hash: undefined })"
|
@close="$router.replace({ hash: undefined })"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="info column">
|
<div class="info column">
|
||||||
<div class="row row-title">
|
<div
|
||||||
|
class="row row-title"
|
||||||
|
:class="{ 'has-alt': release.altTitles?.length > 0 }"
|
||||||
|
>
|
||||||
<h2
|
<h2
|
||||||
v-if="release.title"
|
v-if="release.title"
|
||||||
class="title"
|
class="title"
|
||||||
|
@ -62,6 +65,24 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="release.altTitles?.length > 0"
|
||||||
|
class="row alttitles"
|
||||||
|
>
|
||||||
|
<h2
|
||||||
|
v-for="(altTitle, index) in release.altTitles"
|
||||||
|
:key="`altitle-${index}`"
|
||||||
|
class="alttitle"
|
||||||
|
>
|
||||||
|
{{ altTitle }}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Releases
|
||||||
|
v-if="release.scenes && release.scenes.length > 0"
|
||||||
|
:releases="release.scenes"
|
||||||
|
/>
|
||||||
|
|
||||||
<div class="row associations">
|
<div class="row associations">
|
||||||
<ul
|
<ul
|
||||||
ref="actors"
|
ref="actors"
|
||||||
|
@ -109,11 +130,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Releases
|
|
||||||
v-if="release.scenes && release.scenes.length > 0"
|
|
||||||
:releases="release.scenes"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="release.directors && release.directors.length > 0"
|
v-if="release.directors && release.directors.length > 0"
|
||||||
class="row"
|
class="row"
|
||||||
|
@ -155,6 +171,14 @@
|
||||||
<div class="duration">{{ formatDuration(release.duration) }}</div>
|
<div class="duration">{{ formatDuration(release.duration) }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="release.photoCount"
|
||||||
|
class="row-tidbit"
|
||||||
|
>
|
||||||
|
<span class="row-label">Photos</span>
|
||||||
|
{{ release.photoCount }}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="release.shootId"
|
v-if="release.shootId"
|
||||||
class="row-tidbit"
|
class="row-tidbit"
|
||||||
|
@ -167,7 +191,13 @@
|
||||||
v-if="release.studio"
|
v-if="release.studio"
|
||||||
class="row-tidbit"
|
class="row-tidbit"
|
||||||
>
|
>
|
||||||
<span class="row-label">Studio</span>
|
<span class="row-label">Studio
|
||||||
|
<Icon
|
||||||
|
v-if="release.studio.showcased === false"
|
||||||
|
icon="eye-blocked"
|
||||||
|
title="This studio does not appear on main pages"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
|
||||||
<RouterLink
|
<RouterLink
|
||||||
:to="`/studio/${release.studio.slug}`"
|
:to="`/studio/${release.studio.slug}`"
|
||||||
|
@ -385,7 +415,7 @@ function pageTitle() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function showAlbum() {
|
function showAlbum() {
|
||||||
return (this.release.photos?.length > 0 || this.release.scenesPhotos?.length > 0) && this.$route.hash === '#album';
|
return this.release.photos?.length > 0 || this.release.caps?.length > 0 || this.release.scenesPhotos?.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function mounted() {
|
async function mounted() {
|
||||||
|
@ -471,13 +501,14 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-label {
|
.row-label {
|
||||||
display: block;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
margin: 0 0 .5rem 0;
|
margin: 0 0 .5rem 0;
|
||||||
color: var(--shadow);
|
color: var(--shadow);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
margin: 0 .5rem 0 0;
|
margin: 0 .5rem;
|
||||||
fill: var(--shadow);
|
fill: var(--shadow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -513,6 +544,11 @@ export default {
|
||||||
color: var(--shadow);
|
color: var(--shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.alttitle {
|
||||||
|
color: var(--shadow);
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.album-toggle {
|
.album-toggle {
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
|
|
@ -12,18 +12,34 @@
|
||||||
:key="sfw"
|
:key="sfw"
|
||||||
class="nolist tiles"
|
class="nolist tiles"
|
||||||
>
|
>
|
||||||
|
<template v-for="(item, index) in items">
|
||||||
<li
|
<li
|
||||||
v-for="(release, index) in releases"
|
v-if="item === 'campaign'"
|
||||||
:key="`release-${release.id}`"
|
:key="`campaign-${index}`"
|
||||||
|
class="campaign"
|
||||||
|
>
|
||||||
|
<Campaign
|
||||||
|
v-if="item === 'campaign'"
|
||||||
|
:entity="entity"
|
||||||
|
:min-ratio="0.75"
|
||||||
|
:max-ratio="1.25"
|
||||||
|
:allow-generic="true"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li
|
||||||
|
v-else
|
||||||
|
:key="`release-${item.id}`"
|
||||||
>
|
>
|
||||||
<SceneTile
|
<SceneTile
|
||||||
:release="release"
|
:release="item"
|
||||||
:referer="referer"
|
:referer="referer"
|
||||||
:index="index"
|
:index="index"
|
||||||
:stash="stash"
|
:stash="stash"
|
||||||
@stash="isStashed => $emit('stash', isStashed)"
|
@stash="isStashed => $emit('stash', isStashed)"
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
|
</template>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
|
@ -38,28 +54,36 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import config from 'config';
|
||||||
|
|
||||||
|
import {
|
||||||
|
defineProps,
|
||||||
|
defineEmits,
|
||||||
|
computed,
|
||||||
|
} from 'vue';
|
||||||
|
|
||||||
|
import { useStore } from 'vuex';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import Campaign from '../campaigns/campaign.vue';
|
||||||
import Ellipsis from '../loading/ellipsis.vue';
|
import Ellipsis from '../loading/ellipsis.vue';
|
||||||
import SceneTile from './scene-tile.vue';
|
import SceneTile from './scene-tile.vue';
|
||||||
|
|
||||||
function range() {
|
const router = useRouter();
|
||||||
return this.$route.params.range;
|
const store = useStore();
|
||||||
}
|
|
||||||
|
|
||||||
function sfw() {
|
defineEmits(['stash']);
|
||||||
return this.$store.state.ui.sfw;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
components: {
|
|
||||||
Ellipsis,
|
|
||||||
SceneTile,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
releases: {
|
releases: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
|
entity: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
context: {
|
context: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null,
|
default: null,
|
||||||
|
@ -76,13 +100,14 @@ export default {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
emits: ['stash'],
|
|
||||||
computed: {
|
const campaignIndex = computed(() => Math.floor(Math.random() * props.releases.length - 5) + 5);
|
||||||
range,
|
|
||||||
sfw,
|
const items = computed(() => props.releases.flatMap((release, index) => (config.campaigns.tiles && props.releases.length > 10 && index === campaignIndex.value ? ['campaign', release] : release)));
|
||||||
},
|
|
||||||
};
|
const range = computed(() => router.route?.params.range);
|
||||||
|
const sfw = computed(() => store.state.ui.sfw);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -126,6 +151,12 @@ export default {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.campaign {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
@media(max-width: $breakpoint-mega) {
|
@media(max-width: $breakpoint-mega) {
|
||||||
.tiles {
|
.tiles {
|
||||||
grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
|
||||||
|
|
|
@ -100,10 +100,7 @@
|
||||||
>{{ release.entity.name }}</h3>
|
>{{ release.entity.name }}</h3>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<span
|
<span class="row">
|
||||||
v-if="release.actors?.length > 0"
|
|
||||||
class="row"
|
|
||||||
>
|
|
||||||
<ul
|
<ul
|
||||||
class="actors nolist"
|
class="actors nolist"
|
||||||
:title="release.actors.map(actor => actor.name).join(', ')"
|
:title="release.actors.map(actor => actor.name).join(', ')"
|
||||||
|
@ -357,9 +354,9 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.actors {
|
.actors {
|
||||||
|
height: 1.5rem;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
max-height: 1.5rem;
|
|
||||||
line-height: 1.5rem;
|
line-height: 1.5rem;
|
||||||
margin: 0 0 .25rem 0;
|
margin: 0 0 .25rem 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,12 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
v-if="!loading && actors.length === 0 && releases.length === 0"
|
v-if="error"
|
||||||
|
class="error summary"
|
||||||
|
>{{ error }}</span>
|
||||||
|
|
||||||
|
<span
|
||||||
|
v-else-if="!loading && actors.length === 0 && releases.length === 0"
|
||||||
class="summary"
|
class="summary"
|
||||||
>No results</span>
|
>No results</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -39,6 +44,7 @@ import Actor from '../actors/tile.vue';
|
||||||
import Releases from '../releases/releases.vue';
|
import Releases from '../releases/releases.vue';
|
||||||
|
|
||||||
async function search() {
|
async function search() {
|
||||||
|
try {
|
||||||
const results = await this.$store.dispatch('search', {
|
const results = await this.$store.dispatch('search', {
|
||||||
query: this.query,
|
query: this.query,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
|
@ -50,6 +56,12 @@ async function search() {
|
||||||
this.actors = results.actors;
|
this.actors = results.actors;
|
||||||
this.releases = results.releases;
|
this.releases = results.releases;
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.loading = false;
|
||||||
|
this.error = 'Failed to retrieve search results, sorry about that.';
|
||||||
|
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function query() {
|
function query() {
|
||||||
|
@ -101,6 +113,10 @@ export default {
|
||||||
margin: 0 0 1rem 0;
|
margin: 0 0 1rem 0;
|
||||||
color: var(--shadow);
|
color: var(--shadow);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
color: var(--error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tiles {
|
.tiles {
|
||||||
|
|
|
@ -42,7 +42,7 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
tags: ['anal', 'gay', 'transsexual', 'bisexual', 'pissing', 'anal prolapse'],
|
tags: ['gay', 'transsexual', 'bisexual', 'anal', 'anal prolapse', 'pissing'],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
|
@ -7,6 +7,11 @@
|
||||||
class="dialog-body"
|
class="dialog-body"
|
||||||
@submit.prevent="addStash"
|
@submit.prevent="addStash"
|
||||||
>
|
>
|
||||||
|
<div
|
||||||
|
v-if="errorMsg"
|
||||||
|
class="form-error"
|
||||||
|
>{{ errorMsg }}</div>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
ref="name"
|
ref="name"
|
||||||
v-model="name"
|
v-model="name"
|
||||||
|
@ -27,11 +32,17 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
async function addStash() {
|
async function addStash() {
|
||||||
|
this.errorMsg = null;
|
||||||
|
|
||||||
|
try {
|
||||||
await this.$store.dispatch('createStash', {
|
await this.$store.dispatch('createStash', {
|
||||||
name: this.name,
|
name: this.name,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$emit('close', true);
|
this.$emit('close', true);
|
||||||
|
} catch (error) {
|
||||||
|
this.errorMsg = error.message;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mounted() {
|
function mounted() {
|
||||||
|
@ -39,15 +50,22 @@ function mounted() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
emits: ['close'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
errorMsg: null,
|
||||||
name: null,
|
name: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
emits: ['close'],
|
|
||||||
mounted,
|
mounted,
|
||||||
methods: {
|
methods: {
|
||||||
addStash,
|
addStash,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -115,6 +115,8 @@ import Pagination from '../pagination/pagination.vue';
|
||||||
async function fetchStash() {
|
async function fetchStash() {
|
||||||
this.stash = await this.$store.dispatch('fetchStash', {
|
this.stash = await this.$store.dispatch('fetchStash', {
|
||||||
stashId: this.$route.params.stashId,
|
stashId: this.$route.params.stashId,
|
||||||
|
stashSlug: this.$route.params.stashSlug,
|
||||||
|
username: this.$route.params.username,
|
||||||
section: this.$route.params.range,
|
section: this.$route.params.range,
|
||||||
pageNumber: this.$route.params.pageNumber || 1,
|
pageNumber: this.$route.params.pageNumber || 1,
|
||||||
limit: this.limit,
|
limit: this.limit,
|
||||||
|
|
|
@ -8,71 +8,84 @@
|
||||||
<dt class="stat-label">Version</dt>
|
<dt class="stat-label">Version</dt>
|
||||||
<dd class="stat-value">{{ version }}</dd>
|
<dd class="stat-value">{{ version }}</dd>
|
||||||
</div>
|
</div>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<template v-if="loaded">
|
||||||
|
<dl class="stat-table">
|
||||||
<div class="stat-row">
|
<div class="stat-row">
|
||||||
<dt class="stat-label">Content updated</dt>
|
<dt class="stat-label">Content updated</dt>
|
||||||
<dd class="stat-value">{{ formatDate(lastScrape, 'YYYY-MM-DD HH:mm') }}</dd>
|
<dd
|
||||||
|
class="stat-value"
|
||||||
|
:title="format(lastScrape, 'yyyy-MM-dd HH:mm')"
|
||||||
|
>{{ formatDistance(lastScrape, new Date(), { includeSeconds: true }) }} ago</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
<dl class="stat-table">
|
<dl class="stat-table">
|
||||||
<div class="stat-row">
|
<div class="stat-row">
|
||||||
<dt class="stat-label">Networks</dt>
|
<dt class="stat-label">Networks</dt>
|
||||||
<dd class="stat-value">{{ totalNetworks }}</dd>
|
<dd class="stat-value">{{ totalNetworks.toLocaleString() }}</dd>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="stat-row">
|
<div class="stat-row">
|
||||||
<dt class="stat-label">Channels</dt>
|
<dt class="stat-label">Channels</dt>
|
||||||
<dd class="stat-value">{{ totalChannels }}</dd>
|
<dd class="stat-value">{{ totalChannels.toLocaleString() }}</dd>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="stat-row">
|
<div class="stat-row">
|
||||||
<dt class="stat-label">Scenes</dt>
|
<dt class="stat-label">Scenes</dt>
|
||||||
<dd class="stat-value">{{ totalScenes }}</dd>
|
<dd class="stat-value">{{ totalScenes.toLocaleString() }}</dd>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="stat-row">
|
<div class="stat-row">
|
||||||
<dt class="stat-label">Movies</dt>
|
<dt class="stat-label">Movies</dt>
|
||||||
<dd class="stat-value">{{ totalMovies }}</dd>
|
<dd class="stat-value">{{ totalMovies.toLocaleString() }}</dd>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="stat-row">
|
<div class="stat-row">
|
||||||
<dt class="stat-label">Actors</dt>
|
<dt class="stat-label">Actors</dt>
|
||||||
<dd class="stat-value">{{ totalActors }}</dd>
|
<dd class="stat-value">{{ totalActors.toLocaleString() }}</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<Ellipsis v-else />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
async function mounted() {
|
import { ref, onMounted } from 'vue';
|
||||||
const stats = await this.$store.dispatch('fetchStats');
|
import { useStore } from 'vuex';
|
||||||
|
import { format, formatDistance } from 'date-fns';
|
||||||
|
|
||||||
this.totalScenes = stats.totalScenes;
|
import Ellipsis from '../loading/ellipsis.vue';
|
||||||
this.totalMovies = stats.totalMovies;
|
|
||||||
this.totalActors = stats.totalActors;
|
|
||||||
this.totalNetworks = stats.totalNetworks;
|
|
||||||
this.totalChannels = stats.totalChannels;
|
|
||||||
this.lastScrape = stats.lastScrape;
|
|
||||||
|
|
||||||
this.version = VERSION; // eslint-disable-line no-undef
|
const store = useStore();
|
||||||
}
|
const version = VERSION; // eslint-disable-line no-undef
|
||||||
|
|
||||||
export default {
|
const loaded = ref(false);
|
||||||
data() {
|
const totalScenes = ref(0);
|
||||||
return {
|
const totalMovies = ref(0);
|
||||||
totalScenes: 0,
|
const totalActors = ref(0);
|
||||||
totalMovies: 0,
|
const totalNetworks = ref(0);
|
||||||
totalActors: 0,
|
const totalChannels = ref(0);
|
||||||
totalNetworks: 0,
|
const lastScrape = ref(null);
|
||||||
totalChannels: 0,
|
|
||||||
};
|
onMounted(async () => {
|
||||||
},
|
const stats = await store.dispatch('fetchStats');
|
||||||
mounted,
|
|
||||||
};
|
totalScenes.value = stats.totalScenes;
|
||||||
|
totalMovies.value = stats.totalMovies;
|
||||||
|
totalActors.value = stats.totalActors;
|
||||||
|
totalNetworks.value = stats.totalNetworks;
|
||||||
|
totalChannels.value = stats.totalChannels;
|
||||||
|
lastScrape.value = stats.lastScrape;
|
||||||
|
|
||||||
|
loaded.value = true;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -49,11 +49,11 @@ import Campaign from '../campaigns/campaign.vue';
|
||||||
|
|
||||||
function photos() {
|
function photos() {
|
||||||
if (this.tag.poster && this.$store.state.ui.sfw) {
|
if (this.tag.poster && this.$store.state.ui.sfw) {
|
||||||
return [this.tag.poster].concat(this.tag.photos).map(photo => photo.sfw);
|
return [this.tag.poster].concat(this.tag.photos).map((photo) => photo.sfw);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.$store.state.ui.sfw) {
|
if (this.$store.state.ui.sfw) {
|
||||||
return this.tag.photos.map(photo => photo.sfw);
|
return this.tag.photos.map((photo) => photo.sfw);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.tag.poster) {
|
if (this.tag.poster) {
|
||||||
|
|
|
@ -81,6 +81,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import config from 'config';
|
||||||
import { Converter } from 'showdown';
|
import { Converter } from 'showdown';
|
||||||
|
|
||||||
import escapeHtml from '../../../src/utils/escape-html';
|
import escapeHtml from '../../../src/utils/escape-html';
|
||||||
|
@ -155,7 +156,7 @@ export default {
|
||||||
releases: null,
|
releases: null,
|
||||||
done: false,
|
done: false,
|
||||||
totalCount: 0,
|
totalCount: 0,
|
||||||
limit: 20,
|
limit: Number(this.$route.query.limit || 30) - config.campaigns.tiles, // reserve one campaign spot
|
||||||
pageTitle: null,
|
pageTitle: null,
|
||||||
hasMedia: false,
|
hasMedia: false,
|
||||||
expanded: false,
|
expanded: false,
|
||||||
|
|
|
@ -125,6 +125,7 @@ function mounted() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
emits: ['open', 'close'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
opened: false,
|
opened: false,
|
||||||
|
@ -133,7 +134,6 @@ export default {
|
||||||
arrowOffset: 0,
|
arrowOffset: 0,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
emits: ['open', 'close'],
|
|
||||||
mounted,
|
mounted,
|
||||||
methods: {
|
methods: {
|
||||||
calculate,
|
calculate,
|
||||||
|
|
|
@ -93,17 +93,43 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="alert.entity"
|
v-if="alert.entities.length > 0"
|
||||||
class="alert-section alert-trigger"
|
class="alert-section alert-trigger"
|
||||||
>
|
>
|
||||||
<h4 class="alert-heading">Channel</h4>
|
<h4 class="alert-heading">{{ alert.entities.length > 1 ? 'Channels' : 'Channel' }}</h4>
|
||||||
|
|
||||||
<Entity
|
<Entity
|
||||||
v-if="alert.entity"
|
v-for="entity in alert.entities"
|
||||||
:entity="alert.entity"
|
:key="`${alert.id}${entity.id}`"
|
||||||
|
:entity="entity"
|
||||||
class="entity"
|
class="entity"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="alert.matches.length > 0"
|
||||||
|
class="alert-section alert-trigger"
|
||||||
|
>
|
||||||
|
<h4 class="alert-heading">Matches</h4>
|
||||||
|
|
||||||
|
<ul class="alert-matches nolist">
|
||||||
|
<li
|
||||||
|
v-for="match in alert.matches"
|
||||||
|
:key="`match-${match.id}`"
|
||||||
|
class="match"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
v-if="match.expression.slice(0, 1) === '/' && match.expression.slice(-1) === '/'"
|
||||||
|
class="match-expression"
|
||||||
|
><span class="match-slash">/</span>{{ match.expression.slice(1, -1) }}<span class="match-slash">/</span></span>
|
||||||
|
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
class="match-expression"
|
||||||
|
>{{ match.expression }}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -227,26 +253,34 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.alert-actors,
|
.alert-actors,
|
||||||
.alert-tags {
|
.alert-tags,
|
||||||
|
.alert-matches {
|
||||||
display: flex;
|
display: flex;
|
||||||
grid-gap: .5rem;
|
grid-gap: .5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag {
|
.tag,
|
||||||
|
.match {
|
||||||
color: var(--shadow-strong);
|
color: var(--shadow-strong);
|
||||||
padding: .5rem;
|
padding: .5rem;
|
||||||
border: solid 1px var(--shadow-hint);
|
border: solid 1px var(--shadow-hint);
|
||||||
font-size: .9rem;
|
font-size: .9rem;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
.tag:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border: solid 1px var(--primary);
|
border: solid 1px var(--primary);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.entity {
|
.entity {
|
||||||
width: 10rem;
|
width: 10rem;
|
||||||
height: 2.5rem;
|
height: 2.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.match-slash {
|
||||||
|
padding: 0 .1rem;
|
||||||
|
color: var(--primary);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="stash">
|
<div class="stash">
|
||||||
<div class="stash-section stash-header">
|
<div class="stash-section stash-header">
|
||||||
<router-link
|
<RouterLink
|
||||||
:to="{ name: 'stash', params: { stashId: stash.id, stashSlug: stash.slug, range: 'scenes', pageNumber: 1 } }"
|
:to="{ name: 'stash', params: { stashId: stash.id, stashSlug: stash.slug, range: 'scenes', pageNumber: 1 } }"
|
||||||
class="stash-link nolink"
|
class="stash-link nolink"
|
||||||
>
|
>
|
||||||
<h4 class="stash-name">{{ stash.name }}</h4>
|
<h4 class="stash-name">{{ stash.name }}</h4>
|
||||||
<span class="stash-more">Browse</span>
|
<span class="stash-more">Browse</span>
|
||||||
</router-link>
|
</RouterLink>
|
||||||
|
|
||||||
<span class="header-actions noselect">
|
<span class="header-actions noselect">
|
||||||
<label
|
<label
|
||||||
|
|
|
@ -14,11 +14,13 @@
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<h3 class="section-heading">Stashes</h3>
|
<h3 class="section-heading">Stashes</h3>
|
||||||
|
|
||||||
<Icon
|
<button
|
||||||
icon="plus3"
|
v-if="isMe"
|
||||||
class="header-add"
|
class="button button-secondary header-add"
|
||||||
@click="showAddStash = true"
|
@click="showAddStash = true"
|
||||||
/>
|
>
|
||||||
|
<Icon icon="plus3" />Add stash
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul class="section-body stashes nolist">
|
<ul class="section-body stashes nolist">
|
||||||
|
@ -35,6 +37,7 @@
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<!--
|
||||||
<li
|
<li
|
||||||
v-if="isMe"
|
v-if="isMe"
|
||||||
class="stashes-stash stashes-add"
|
class="stashes-stash stashes-add"
|
||||||
|
@ -42,6 +45,7 @@
|
||||||
>
|
>
|
||||||
<Icon icon="plus2" />
|
<Icon icon="plus2" />
|
||||||
</li>
|
</li>
|
||||||
|
-->
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<AddStash
|
<AddStash
|
||||||
|
@ -54,11 +58,13 @@
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<h3 class="section-heading">Alerts</h3>
|
<h3 class="section-heading">Alerts</h3>
|
||||||
|
|
||||||
<Icon
|
<button
|
||||||
icon="plus3"
|
v-if="isMe"
|
||||||
class="header-add"
|
class="button button-secondary header-add"
|
||||||
@click="showAddAlert = true"
|
@click="showAddAlert = true"
|
||||||
/>
|
>
|
||||||
|
<Icon icon="plus3" />Set alert
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul class="section-body alerts nolist">
|
<ul class="section-body alerts nolist">
|
||||||
|
@ -74,12 +80,14 @@
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<!--
|
||||||
<li
|
<li
|
||||||
class="alerts-add"
|
class="alerts-add"
|
||||||
@click="showAddAlert = true"
|
@click="showAddAlert = true"
|
||||||
>
|
>
|
||||||
<Icon icon="plus2" />
|
<Icon icon="plus2" />
|
||||||
</li>
|
</li>
|
||||||
|
-->
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<AddAlert
|
<AddAlert
|
||||||
|
@ -186,7 +194,7 @@ export default {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin: 0 0 1rem 0;
|
margin: 0 1rem 1rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-body {
|
.section-body {
|
||||||
|
@ -201,14 +209,6 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-add {
|
.header-add {
|
||||||
height: auto;
|
|
||||||
padding: .5rem 1rem;
|
|
||||||
fill: var(--shadow);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
fill: var(--primary);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.stashes-stash {
|
.stashes-stash {
|
||||||
|
|
|
@ -3,3 +3,11 @@
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-error {
|
||||||
|
padding: .5rem;
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
color: var(--text-light);
|
||||||
|
background: var(--error);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
|
@ -26,6 +26,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
border: none;
|
border: none;
|
||||||
background: none;
|
background: none;
|
||||||
padding: .5rem;
|
padding: .5rem;
|
||||||
|
@ -49,6 +52,11 @@
|
||||||
background: var(--shadow-weak);
|
background: var(--shadow-weak);
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
fill: var(--text-light);
|
||||||
|
margin-right: .5rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-secondary {
|
.button-secondary {
|
||||||
|
@ -57,12 +65,21 @@
|
||||||
&:hover:not(:disabled) {
|
&:hover:not(:disabled) {
|
||||||
color: var(--text-light);
|
color: var(--text-light);
|
||||||
background: var(--primary);
|
background: var(--primary);
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
fill: var(--text-light);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
color: var(--shadow-strong);
|
color: var(--shadow-strong);
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
fill: var(--primary);
|
||||||
|
margin-right: .5rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.album-toggle {
|
.album-toggle {
|
||||||
|
|
|
@ -52,9 +52,10 @@ $breakpoint4: 1500px;
|
||||||
--female: #f0a;
|
--female: #f0a;
|
||||||
|
|
||||||
--alert: #f00;
|
--alert: #f00;
|
||||||
--error: #f00;
|
--error: #fd5555;
|
||||||
--warn: #fa0;
|
--warn: #fa0;
|
||||||
--success: #5c2;
|
--success: #5c2;
|
||||||
|
--notice: #25c;
|
||||||
|
|
||||||
--enabled: #5c2;
|
--enabled: #5c2;
|
||||||
--enabled-background: rgba(0, 255, 0, .1);
|
--enabled-background: rgba(0, 255, 0, .1);
|
||||||
|
|
|
@ -18,6 +18,10 @@
|
||||||
<link rel="stylesheet" href="/css/style.css">
|
<link rel="stylesheet" href="/css/style.css">
|
||||||
|
|
||||||
<script src="/js/bundle.js" defer></script>
|
<script src="/js/bundle.js" defer></script>
|
||||||
|
|
||||||
|
<% if (analytics.enabled) { %>
|
||||||
|
<script async src="<%- analytics.address %>" data-website-id="<%- analytics.siteId %>"></script>
|
||||||
|
<% } %>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="container"></div>
|
<div id="container"></div>
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
actorStashesFields,
|
actorStashesFields,
|
||||||
getIncludedEntities,
|
getIncludedEntities,
|
||||||
getIncludedActors,
|
getIncludedActors,
|
||||||
|
batchFragment,
|
||||||
} from '../fragments';
|
} from '../fragments';
|
||||||
|
|
||||||
function initActorActions(store, router) {
|
function initActorActions(store, router) {
|
||||||
|
@ -27,7 +28,7 @@ function initActorActions(store, router) {
|
||||||
const includedTags = router.currentRoute.value.query.tags ? router.currentRoute.value.query.tags.split(',') : [];
|
const includedTags = router.currentRoute.value.query.tags ? router.currentRoute.value.query.tags.split(',') : [];
|
||||||
const mode = router.currentRoute.value.query.mode || 'all';
|
const mode = router.currentRoute.value.query.mode || 'all';
|
||||||
|
|
||||||
const { actor } = await graphql(`
|
const { actor, batches: [lastBatch] } = await graphql(`
|
||||||
query Actor(
|
query Actor(
|
||||||
$actorId: Int!
|
$actorId: Int!
|
||||||
$userId: Int,
|
$userId: Int,
|
||||||
|
@ -36,8 +37,6 @@ function initActorActions(store, router) {
|
||||||
$offset:Int = 0,
|
$offset:Int = 0,
|
||||||
$after:Datetime = "1900-01-01",
|
$after:Datetime = "1900-01-01",
|
||||||
$before:Datetime = "2100-01-01",
|
$before:Datetime = "2100-01-01",
|
||||||
$afterTime:Datetime = "1900-01-01",
|
|
||||||
$beforeTime:Datetime = "2100-01-01",
|
|
||||||
$orderBy:[ReleasesOrderBy!]
|
$orderBy:[ReleasesOrderBy!]
|
||||||
$selectableTags: [String],
|
$selectableTags: [String],
|
||||||
$includedTags: [String!],
|
$includedTags: [String!],
|
||||||
|
@ -52,6 +51,7 @@ function initActorActions(store, router) {
|
||||||
slug
|
slug
|
||||||
realName
|
realName
|
||||||
gender
|
gender
|
||||||
|
orientation
|
||||||
dateOfBirth
|
dateOfBirth
|
||||||
dateOfDeath
|
dateOfDeath
|
||||||
age
|
age
|
||||||
|
@ -204,23 +204,10 @@ function initActorActions(store, router) {
|
||||||
}
|
}
|
||||||
scenesConnection(
|
scenesConnection(
|
||||||
filter: {
|
filter: {
|
||||||
or: [
|
effectiveDate: {
|
||||||
{
|
|
||||||
date: {
|
|
||||||
lessThan: $before,
|
lessThan: $before,
|
||||||
greaterThan: $after
|
greaterThan: $after
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
date: {
|
|
||||||
isNull: true
|
|
||||||
},
|
|
||||||
createdAt: {
|
|
||||||
lessThan: $beforeTime,
|
|
||||||
greaterThan: $afterTime,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
and: [
|
and: [
|
||||||
{
|
{
|
||||||
or: $includedEntities
|
or: $includedEntities
|
||||||
|
@ -254,6 +241,7 @@ function initActorActions(store, router) {
|
||||||
}
|
}
|
||||||
${actorStashesFields}
|
${actorStashesFields}
|
||||||
}
|
}
|
||||||
|
${batchFragment}
|
||||||
}
|
}
|
||||||
`, {
|
`, {
|
||||||
actorId,
|
actorId,
|
||||||
|
@ -261,8 +249,6 @@ function initActorActions(store, router) {
|
||||||
offset: Math.max(0, (pageNumber - 1)) * limit,
|
offset: Math.max(0, (pageNumber - 1)) * limit,
|
||||||
after,
|
after,
|
||||||
before,
|
before,
|
||||||
afterTime: store.getters.after,
|
|
||||||
beforeTime: store.getters.before,
|
|
||||||
selectableTags: config.selectableTags,
|
selectableTags: config.selectableTags,
|
||||||
orderBy,
|
orderBy,
|
||||||
exclude: store.state.ui.tagFilter,
|
exclude: store.state.ui.tagFilter,
|
||||||
|
@ -281,7 +267,7 @@ function initActorActions(store, router) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
actor: curateActor(actor, null, curateRelease),
|
actor: curateActor(actor, null, curateRelease),
|
||||||
releases: actor.scenesConnection.releases.map((release) => curateRelease(release)),
|
releases: actor.scenesConnection.releases.map((release) => curateRelease(release, 'scene', { lastBatch: lastBatch.id })),
|
||||||
totalCount: actor.scenesConnection.totalCount,
|
totalCount: actor.scenesConnection.totalCount,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,11 @@ export default {
|
||||||
mediaPath: '/media',
|
mediaPath: '/media',
|
||||||
s3Path: 'https://s3.wasabisys.com',
|
s3Path: 'https://s3.wasabisys.com',
|
||||||
},
|
},
|
||||||
|
campaigns: {
|
||||||
|
home: true,
|
||||||
|
entity: true,
|
||||||
|
tiles: 0,
|
||||||
|
},
|
||||||
showDisclaimer: false,
|
showDisclaimer: false,
|
||||||
disclaimer: 'This site is in early development, and content may occasionally disappear. Please stay tuned, you will be able to use traxxx to its full potential in the near future!',
|
disclaimer: 'This site is in early development, and content may occasionally disappear. Please stay tuned, you will be able to use traxxx to its full potential in the near future!',
|
||||||
selectableTags: [
|
selectableTags: [
|
||||||
|
|
|
@ -65,20 +65,22 @@ function curateActor(actor, release) {
|
||||||
return curatedActor;
|
return curatedActor;
|
||||||
}
|
}
|
||||||
|
|
||||||
function curateRelease(release, type = 'scene') {
|
function curateRelease(release, type = 'scene', context = {}) {
|
||||||
const curatedRelease = {
|
const curatedRelease = {
|
||||||
...release,
|
...release,
|
||||||
type: release.type || type,
|
type: release.type || type,
|
||||||
actors: [],
|
actors: [],
|
||||||
poster: release.poster && release.poster.media,
|
poster: release.poster && release.poster.media,
|
||||||
tags: release.tags ? release.tags.map((tag) => tag.tag || tag) : [],
|
tags: release.tags ? release.tags.map((tag) => tag.tag || tag) : [],
|
||||||
|
isNew: release.createdBatchId === context.lastBatch,
|
||||||
};
|
};
|
||||||
|
|
||||||
curatedRelease.scenes = release.scenes?.filter(Boolean).map(({ scene }) => curateRelease(scene, 'scene')) || [];
|
curatedRelease.scenes = release.scenes?.filter(Boolean).map(({ scene }) => curateRelease(scene, 'scene', context)) || [];
|
||||||
curatedRelease.movies = release.movies?.filter(Boolean).map(({ movie }) => curateRelease(movie, 'movie')) || [];
|
curatedRelease.movies = release.movies?.filter(Boolean).map(({ movie }) => curateRelease(movie, 'movie', context)) || [];
|
||||||
curatedRelease.series = release.series?.filter(Boolean).map(({ serie }) => curateRelease(serie, 'serie')) || [];
|
curatedRelease.series = release.series?.filter(Boolean).map(({ serie }) => curateRelease(serie, 'serie', context)) || [];
|
||||||
curatedRelease.chapters = release.chapters?.filter(Boolean).map((chapter) => curateRelease(chapter)) || [];
|
curatedRelease.chapters = release.chapters?.filter(Boolean).map((chapter) => curateRelease(chapter, 'chapter', context)) || [];
|
||||||
curatedRelease.photos = release.photos?.filter(Boolean).map((photo) => photo.media || photo) || [];
|
curatedRelease.photos = release.photos?.filter(Boolean).map((photo) => photo.media || photo) || [];
|
||||||
|
curatedRelease.caps = release.caps?.filter(Boolean).map((cap) => cap.media || cap) || [];
|
||||||
curatedRelease.scenesPhotos = release.scenesPhotos?.filter(Boolean).map((photo) => photo.media || photo) || [];
|
curatedRelease.scenesPhotos = release.scenesPhotos?.filter(Boolean).map((photo) => photo.media || photo) || [];
|
||||||
curatedRelease.covers = release.covers?.filter(Boolean).map(({ media }) => media) || [];
|
curatedRelease.covers = release.covers?.filter(Boolean).map(({ media }) => media) || [];
|
||||||
|
|
||||||
|
@ -102,7 +104,7 @@ function curateRelease(release, type = 'scene') {
|
||||||
return curatedRelease;
|
return curatedRelease;
|
||||||
}
|
}
|
||||||
|
|
||||||
function curateEntity(entity, parent, releases) {
|
function curateEntity(entity, parent, releases, context) {
|
||||||
const curatedEntity = {
|
const curatedEntity = {
|
||||||
...entity,
|
...entity,
|
||||||
children: [],
|
children: [],
|
||||||
|
@ -120,19 +122,19 @@ function curateEntity(entity, parent, releases) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entity.parent || parent) curatedEntity.parent = curateEntity(entity.parent || parent);
|
if (entity.parent || parent) curatedEntity.parent = curateEntity(entity.parent || parent);
|
||||||
if (releases) curatedEntity.releases = releases.map((release) => curateRelease(release));
|
if (releases) curatedEntity.releases = releases.map((release) => curateRelease(release, 'scene', context));
|
||||||
|
|
||||||
curatedEntity.sceneTotal = entity.sceneTotal;
|
curatedEntity.sceneTotal = entity.sceneTotal;
|
||||||
|
|
||||||
return curatedEntity;
|
return curatedEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
function curateTag(tag) {
|
function curateTag(tag, context) {
|
||||||
const curatedTag = {
|
const curatedTag = {
|
||||||
...tag,
|
...tag,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (tag.releases) curatedTag.releases = tag.releases.map(({ release }) => curateRelease(release));
|
if (tag.releases) curatedTag.releases = tag.releases.map(({ release }) => curateRelease(release, 'scene', context));
|
||||||
if (tag.banners) curatedTag.banners = tag.banners.map(({ banner }) => banner);
|
if (tag.banners) curatedTag.banners = tag.banners.map(({ banner }) => banner);
|
||||||
if (tag.photos) curatedTag.photos = tag.photos.map(({ media }) => media);
|
if (tag.photos) curatedTag.photos = tag.photos.map(({ media }) => media);
|
||||||
if (tag.poster) curatedTag.poster = tag.poster.media;
|
if (tag.poster) curatedTag.poster = tag.poster.media;
|
||||||
|
@ -140,14 +142,14 @@ function curateTag(tag) {
|
||||||
return curatedTag;
|
return curatedTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
function curateStash(stash) {
|
function curateStash(stash, context) {
|
||||||
const curatedStash = stash;
|
const curatedStash = stash;
|
||||||
|
|
||||||
if (stash.scenes || stash.scenesConnection?.scenes) {
|
if (stash.scenes || stash.scenesConnection?.scenes) {
|
||||||
curatedStash.sceneTotal = stash.scenesConnection?.totalCount || null;
|
curatedStash.sceneTotal = stash.scenesConnection?.totalCount || null;
|
||||||
curatedStash.scenes = (stash.scenesConnection?.scenes || stash.scenes).map((item) => ({
|
curatedStash.scenes = (stash.scenesConnection?.scenes || stash.scenes).map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
scene: curateRelease(item.scene),
|
scene: curateRelease(item.scene, 'scene', context),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +165,7 @@ function curateStash(stash) {
|
||||||
curatedStash.movieTotal = stash.moviesConnection?.totalCount || null;
|
curatedStash.movieTotal = stash.moviesConnection?.totalCount || null;
|
||||||
curatedStash.movies = (stash.moviesConnection?.movies || stash.movies).map((item) => ({
|
curatedStash.movies = (stash.moviesConnection?.movies || stash.movies).map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
movie: curateRelease(item.movie),
|
movie: curateRelease(item.movie, 'movie', context),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,6 +191,10 @@ function curateAlert(alert) {
|
||||||
curatedAlert.entity = curateEntity(alert.entity.entity || alert.entity);
|
curatedAlert.entity = curateEntity(alert.entity.entity || alert.entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (alert.entities) {
|
||||||
|
curatedAlert.entities = alert.entities.map((entity) => curateEntity(entity.entity || entity));
|
||||||
|
}
|
||||||
|
|
||||||
if (alert.stashes) {
|
if (alert.stashes) {
|
||||||
curatedAlert.stashes = alert.stashes.map((stash) => curateStash(stash.stash || stash));
|
curatedAlert.stashes = alert.stashes.map((stash) => curateStash(stash.stash || stash));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { graphql } from '../api';
|
import { graphql } from '../api';
|
||||||
// import { sitesFragment, releaseFields } from '../fragments';
|
// import { sitesFragment, releaseFields } from '../fragments';
|
||||||
import { releaseFields, campaignsFragment } from '../fragments';
|
import { releaseFields, batchFragment, campaignsFragment } from '../fragments';
|
||||||
import { curateEntity } from '../curate';
|
import { curateEntity } from '../curate';
|
||||||
import getDateRange from '../get-date-range';
|
import getDateRange from '../get-date-range';
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ function initEntitiesActions(store, router) {
|
||||||
}) {
|
}) {
|
||||||
const { before, after, orderBy } = getDateRange(range);
|
const { before, after, orderBy } = getDateRange(range);
|
||||||
|
|
||||||
const { entity } = await graphql(`
|
const { entity, connection, batches: [lastBatch] } = await graphql(`
|
||||||
query Entity(
|
query Entity(
|
||||||
$entitySlug: String!
|
$entitySlug: String!
|
||||||
$entityType: String! = "channel"
|
$entityType: String! = "channel"
|
||||||
|
@ -22,7 +22,7 @@ function initEntitiesActions(store, router) {
|
||||||
$offset: Int = 0,
|
$offset: Int = 0,
|
||||||
$after: Datetime = "1900-01-01",
|
$after: Datetime = "1900-01-01",
|
||||||
$before: Datetime = "2100-01-01",
|
$before: Datetime = "2100-01-01",
|
||||||
$orderBy: [ReleasesOrderBy!]
|
$orderBy: [ReleasesSummariesOrderBy!]
|
||||||
$exclude: [String!]
|
$exclude: [String!]
|
||||||
$hasAuth: Boolean!
|
$hasAuth: Boolean!
|
||||||
$userId: Int
|
$userId: Int
|
||||||
|
@ -67,7 +67,6 @@ function initEntitiesActions(store, router) {
|
||||||
independent
|
independent
|
||||||
hasLogo
|
hasLogo
|
||||||
${campaignsFragment}
|
${campaignsFragment}
|
||||||
sceneTotal
|
|
||||||
children: childEntitiesConnection {
|
children: childEntitiesConnection {
|
||||||
totalCount
|
totalCount
|
||||||
}
|
}
|
||||||
|
@ -84,32 +83,52 @@ function initEntitiesActions(store, router) {
|
||||||
hasLogo
|
hasLogo
|
||||||
${campaignsFragment}
|
${campaignsFragment}
|
||||||
}
|
}
|
||||||
connection: scenesConnection(
|
}
|
||||||
|
connection: releasesSummariesConnection(
|
||||||
first: $limit
|
first: $limit
|
||||||
offset: $offset
|
offset: $offset
|
||||||
orderBy: $orderBy
|
orderBy: $orderBy
|
||||||
filter: {
|
filter: {
|
||||||
effectiveDate: {
|
effectiveDate: { lessThan: $before, greaterThan: $after }
|
||||||
lessThan: $before,
|
showcased: { equalTo: true }
|
||||||
greaterThan: $after
|
and: [
|
||||||
|
{
|
||||||
|
or: [
|
||||||
|
{
|
||||||
|
not: { tags: { overlaps: $exclude } }
|
||||||
}
|
}
|
||||||
releasesTagsConnection: {
|
{
|
||||||
none: {
|
tags: { isNull: true }
|
||||||
tag: {
|
|
||||||
slug: {
|
|
||||||
in: $exclude
|
|
||||||
}
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
or: [
|
||||||
|
{
|
||||||
|
channelSlug: { equalTo: $entitySlug }
|
||||||
|
channelType: { equalTo: $entityType }
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
networkSlug: { equalTo: $entitySlug }
|
||||||
|
networkType: { equalTo: $entityType }
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
parentNetworkSlug: { equalTo: $entitySlug }
|
||||||
|
parentNetworkType: { equalTo: $entityType }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
releases: nodes {
|
releases: nodes {
|
||||||
|
release {
|
||||||
${releaseFields}
|
${releaseFields}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
totalCount
|
totalCount
|
||||||
}
|
}
|
||||||
}
|
${batchFragment}
|
||||||
}
|
}
|
||||||
`, {
|
`, {
|
||||||
entitySlug,
|
entitySlug,
|
||||||
|
@ -130,8 +149,8 @@ function initEntitiesActions(store, router) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
entity: curateEntity(entity, null, entity.connection.releases),
|
entity: curateEntity(entity, null, connection.releases.map(({ release }) => release), { lastBatch: lastBatch?.id }),
|
||||||
totalCount: entity.connection.totalCount,
|
totalCount: connection.totalCount,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,7 @@ const actorFields = `
|
||||||
ageFromBirth
|
ageFromBirth
|
||||||
dateOfBirth
|
dateOfBirth
|
||||||
gender
|
gender
|
||||||
|
orientation
|
||||||
avatar: avatarMedia {
|
avatar: avatarMedia {
|
||||||
id
|
id
|
||||||
path
|
path
|
||||||
|
@ -96,6 +97,13 @@ const actorFields = `
|
||||||
${actorStashesFields}
|
${actorStashesFields}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const basicActorFields = `
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
gender
|
||||||
|
`;
|
||||||
|
|
||||||
const movieFields = `
|
const movieFields = `
|
||||||
id
|
id
|
||||||
title
|
title
|
||||||
|
@ -165,27 +173,7 @@ const movieFields = `
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const campaignsFragment = `
|
const campaignFields = `
|
||||||
campaigns(filter: {
|
|
||||||
or: [
|
|
||||||
{
|
|
||||||
banner: {
|
|
||||||
bannersTagsConnection: {
|
|
||||||
none: {
|
|
||||||
tag: {
|
|
||||||
slug: {
|
|
||||||
in: $exclude
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
bannerExists: false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}) {
|
|
||||||
id
|
id
|
||||||
url
|
url
|
||||||
affiliate {
|
affiliate {
|
||||||
|
@ -224,6 +212,30 @@ const campaignsFragment = `
|
||||||
slug
|
slug
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const campaignsFragment = `
|
||||||
|
campaigns(filter: {
|
||||||
|
or: [
|
||||||
|
{
|
||||||
|
banner: {
|
||||||
|
bannersTagsConnection: {
|
||||||
|
none: {
|
||||||
|
tag: {
|
||||||
|
slug: {
|
||||||
|
in: $exclude
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
bannerExists: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}) {
|
||||||
|
${campaignFields}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -235,6 +247,14 @@ const releaseActorsFragment = `
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const releaseBasicActorsFragment = `
|
||||||
|
actors: releasesActors(orderBy: ACTOR_BY_ACTOR_ID__GENDER_ASC) {
|
||||||
|
actor {
|
||||||
|
${basicActorFields}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
const releaseDirectorFragment = `
|
const releaseDirectorFragment = `
|
||||||
directors: releasesDirectors(orderBy: ACTOR_BY_DIRECTOR_ID__NAME_ASC) {
|
directors: releasesDirectors(orderBy: ACTOR_BY_DIRECTOR_ID__NAME_ASC) {
|
||||||
director {
|
director {
|
||||||
|
@ -334,6 +354,31 @@ const releasePhotosFragment = `
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const releaseCapsFragment = `
|
||||||
|
caps: releasesCaps(orderBy: MEDIA_BY_MEDIA_ID__INDEX_ASC) {
|
||||||
|
media {
|
||||||
|
id
|
||||||
|
index
|
||||||
|
path
|
||||||
|
thumbnail
|
||||||
|
width
|
||||||
|
height
|
||||||
|
thumbnailWidth
|
||||||
|
thumbnailHeight
|
||||||
|
lazy
|
||||||
|
isS3
|
||||||
|
comment
|
||||||
|
sfw: sfwMedia {
|
||||||
|
id
|
||||||
|
thumbnail
|
||||||
|
lazy
|
||||||
|
path
|
||||||
|
comment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
const releaseTrailerFragment = `
|
const releaseTrailerFragment = `
|
||||||
trailer: releasesTrailer {
|
trailer: releasesTrailer {
|
||||||
media {
|
media {
|
||||||
|
@ -374,11 +419,11 @@ const releaseFields = `
|
||||||
createdAt
|
createdAt
|
||||||
url
|
url
|
||||||
createdBatchId
|
createdBatchId
|
||||||
${releaseActorsFragment}
|
${releaseBasicActorsFragment}
|
||||||
${releaseTagsFragment}
|
${releaseTagsFragment}
|
||||||
${releasePosterFragment}
|
${releasePosterFragment}
|
||||||
${releaseCoversFragment}
|
|
||||||
${releasePhotosFragment}
|
${releasePhotosFragment}
|
||||||
|
${releaseCapsFragment}
|
||||||
${siteFragment}
|
${siteFragment}
|
||||||
studio {
|
studio {
|
||||||
id
|
id
|
||||||
|
@ -393,7 +438,6 @@ const releaseFields = `
|
||||||
slug
|
slug
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
isNew
|
|
||||||
isFavorited
|
isFavorited
|
||||||
isStashed(includeFavorites: false)
|
isStashed(includeFavorites: false)
|
||||||
stashes: stashesScenesBySceneId(
|
stashes: stashesScenesBySceneId(
|
||||||
|
@ -413,22 +457,23 @@ const releaseFields = `
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
// isNew too performance-intensive
|
||||||
|
|
||||||
|
/*
|
||||||
const releasesFragment = `
|
const releasesFragment = `
|
||||||
connection: releasesConnection(
|
connection: releasesConnection(
|
||||||
filter: {
|
filter: {
|
||||||
releasesNotShowcasedsConnectionExist: false
|
releasesNotShowcasedsConnectionExist: false
|
||||||
date: {
|
effectiveDate: {
|
||||||
lessThan: $before,
|
lessThan: $before,
|
||||||
greaterThan: $after
|
greaterThan: $after
|
||||||
}
|
}
|
||||||
releasesTagsConnection: {
|
releasesTagsConnection: {
|
||||||
none: {
|
none: {
|
||||||
tag: {
|
tag: {
|
||||||
or: [
|
slug: {
|
||||||
{ slug: { in: $exclude } }
|
in: $exclude
|
||||||
{ name: { in: $exclude } }
|
}
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -443,6 +488,35 @@ const releasesFragment = `
|
||||||
totalCount
|
totalCount
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
*/
|
||||||
|
|
||||||
|
const releasesFragment = `
|
||||||
|
connection: releasesSummariesConnection(
|
||||||
|
first: $limit
|
||||||
|
offset: $offset
|
||||||
|
orderBy: $orderBy
|
||||||
|
filter: {
|
||||||
|
or: [
|
||||||
|
{
|
||||||
|
not: { tags: { overlaps: $exclude } }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
tags: { isNull: true }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
effectiveDate: { lessThan: $before, greaterThan: $after }
|
||||||
|
showcased: { equalTo: true }
|
||||||
|
batchShowcased: { in: $batchShowcased }
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
releases: nodes {
|
||||||
|
release {
|
||||||
|
${releaseFields}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
totalCount
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
const mediaFields = `
|
const mediaFields = `
|
||||||
id
|
id
|
||||||
|
@ -471,6 +545,7 @@ const releaseFragment = `
|
||||||
release(id: $releaseId) {
|
release(id: $releaseId) {
|
||||||
id
|
id
|
||||||
title
|
title
|
||||||
|
altTitles
|
||||||
description
|
description
|
||||||
date
|
date
|
||||||
datePrecision
|
datePrecision
|
||||||
|
@ -478,6 +553,7 @@ const releaseFragment = `
|
||||||
createdAt
|
createdAt
|
||||||
shootId
|
shootId
|
||||||
qualities
|
qualities
|
||||||
|
photoCount
|
||||||
productionDate
|
productionDate
|
||||||
createdBatchId
|
createdBatchId
|
||||||
productionLocation
|
productionLocation
|
||||||
|
@ -495,6 +571,7 @@ const releaseFragment = `
|
||||||
${releaseTagsFragment}
|
${releaseTagsFragment}
|
||||||
${releasePosterFragment}
|
${releasePosterFragment}
|
||||||
${releasePhotosFragment}
|
${releasePhotosFragment}
|
||||||
|
${releaseCapsFragment}
|
||||||
${releaseCoversFragment}
|
${releaseCoversFragment}
|
||||||
${releaseTrailerFragment}
|
${releaseTrailerFragment}
|
||||||
${releaseTeaserFragment}
|
${releaseTeaserFragment}
|
||||||
|
@ -536,6 +613,7 @@ const releaseFragment = `
|
||||||
name
|
name
|
||||||
slug
|
slug
|
||||||
url
|
url
|
||||||
|
showcased
|
||||||
}
|
}
|
||||||
movies: moviesScenesBySceneId {
|
movies: moviesScenesBySceneId {
|
||||||
movie {
|
movie {
|
||||||
|
@ -596,6 +674,16 @@ const releaseFragment = `
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const batchFragment = `
|
||||||
|
batches(
|
||||||
|
first: 1,
|
||||||
|
orderBy: CREATED_AT_DESC,
|
||||||
|
filter: { showcased: { equalTo: true } }
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
function getIncludedEntities(router) {
|
function getIncludedEntities(router) {
|
||||||
const includedChannels = router.currentRoute.value.query.channels ? router.currentRoute.value.query.channels.split(',') : [];
|
const includedChannels = router.currentRoute.value.query.channels ? router.currentRoute.value.query.channels.split(',') : [];
|
||||||
const includedNetworks = router.currentRoute.value.query.networks ? router.currentRoute.value.query.networks.split(',') : [];
|
const includedNetworks = router.currentRoute.value.query.networks ? router.currentRoute.value.query.networks.split(',') : [];
|
||||||
|
@ -662,6 +750,8 @@ function getIncludedActors(router) {
|
||||||
export {
|
export {
|
||||||
actorFields,
|
actorFields,
|
||||||
actorStashesFields,
|
actorStashesFields,
|
||||||
|
batchFragment,
|
||||||
|
campaignFields,
|
||||||
campaignsFragment,
|
campaignsFragment,
|
||||||
mediaFields,
|
mediaFields,
|
||||||
mediaFragment,
|
mediaFragment,
|
||||||
|
|
|
@ -12,7 +12,7 @@ const dateRanges = {
|
||||||
upcoming: () => ({
|
upcoming: () => ({
|
||||||
after: dayjs.utc().toDate(),
|
after: dayjs.utc().toDate(),
|
||||||
before: '2100-01-01',
|
before: '2100-01-01',
|
||||||
orderBy: ['EFFECTIVE_DATE_DESC'],
|
orderBy: ['EFFECTIVE_DATE_ASC'],
|
||||||
}),
|
}),
|
||||||
new: () => ({
|
new: () => ({
|
||||||
after: '1900-01-01 00:00:00',
|
after: '1900-01-01 00:00:00',
|
||||||
|
|
|
@ -8,6 +8,7 @@ import router from './router';
|
||||||
import initStore from './store';
|
import initStore from './store';
|
||||||
import initUiObservers from './ui/observers';
|
import initUiObservers from './ui/observers';
|
||||||
import initAuthObservers from './auth/observers';
|
import initAuthObservers from './auth/observers';
|
||||||
|
import setPageTitle from './set-page-title';
|
||||||
|
|
||||||
import { formatDate, formatDuration } from './format';
|
import { formatDate, formatDuration } from './format';
|
||||||
|
|
||||||
|
@ -101,12 +102,7 @@ async function init() {
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
pageTitle(title) {
|
pageTitle(title) {
|
||||||
if (title) {
|
setPageTitle(title); // for Options API components
|
||||||
document.title = `traxxx - ${title}`;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.title = 'traxxx';
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
beforeCreate() {
|
beforeCreate() {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { graphql } from '../api';
|
import { graphql } from '../api';
|
||||||
// import { sitesFragment, releaseFields } from '../fragments';
|
// import { sitesFragment, releaseFields } from '../fragments';
|
||||||
import { releaseFields } from '../fragments';
|
import { releaseFields } from '../fragments';
|
||||||
import { curateNetwork } from '../curate';
|
import { curateEntity } from '../curate';
|
||||||
import getDateRange from '../get-date-range';
|
import getDateRange from '../get-date-range';
|
||||||
|
|
||||||
function initNetworksActions(store, _router) {
|
function initNetworksActions(store, _router) {
|
||||||
|
@ -59,23 +59,10 @@ function initNetworksActions(store, _router) {
|
||||||
{ parent: { parent: { slug: { equalTo: $networkSlug } } } }
|
{ parent: { parent: { slug: { equalTo: $networkSlug } } } }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
or: [
|
effectiveDate: {
|
||||||
{
|
|
||||||
date: {
|
|
||||||
lessThan: $before,
|
lessThan: $before,
|
||||||
greaterThan: $after
|
greaterThan: $after
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
date: {
|
|
||||||
isNull: true
|
|
||||||
},
|
|
||||||
createdAt: {
|
|
||||||
lessThan: $beforeTime,
|
|
||||||
greaterThan: $afterTime,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
releasesTagsConnection: {
|
releasesTagsConnection: {
|
||||||
none: {
|
none: {
|
||||||
tag: {
|
tag: {
|
||||||
|
@ -106,7 +93,7 @@ function initNetworksActions(store, _router) {
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
network: curateNetwork(network, releases),
|
network: curateEntity(network, releases),
|
||||||
totalCount,
|
totalCount,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -131,7 +118,7 @@ function initNetworksActions(store, _router) {
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
return networks.map(network => curateNetwork(network));
|
return networks.map((network) => curateEntity(network));
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { graphql } from '../api';
|
import { graphql } from '../api';
|
||||||
import {
|
import {
|
||||||
|
batchFragment,
|
||||||
releasesFragment,
|
releasesFragment,
|
||||||
releaseFragment,
|
releaseFragment,
|
||||||
releaseFields,
|
releaseFields,
|
||||||
|
@ -14,7 +15,7 @@ function initReleasesActions(store, router) {
|
||||||
async function fetchReleases({ _commit }, { limit = 10, pageNumber = 1, range = 'latest' }) {
|
async function fetchReleases({ _commit }, { limit = 10, pageNumber = 1, range = 'latest' }) {
|
||||||
const { before, after, orderBy } = getDateRange(range);
|
const { before, after, orderBy } = getDateRange(range);
|
||||||
|
|
||||||
const { connection: { releases, totalCount } } = await graphql(`
|
const { connection: { releases, totalCount }, batches: [lastBatch] } = await graphql(`
|
||||||
query Releases(
|
query Releases(
|
||||||
$hasAuth: Boolean!
|
$hasAuth: Boolean!
|
||||||
$userId: Int
|
$userId: Int
|
||||||
|
@ -22,10 +23,12 @@ function initReleasesActions(store, router) {
|
||||||
$offset:Int = 0,
|
$offset:Int = 0,
|
||||||
$after:Datetime = "1900-01-01 00:00:00",
|
$after:Datetime = "1900-01-01 00:00:00",
|
||||||
$before:Datetime = "2100-01-01 00:00:00",
|
$before:Datetime = "2100-01-01 00:00:00",
|
||||||
$orderBy: [ReleasesOrderBy!],
|
$orderBy: [ReleasesSummariesOrderBy!],
|
||||||
$exclude: [String!]
|
$exclude: [String!],
|
||||||
|
$batchShowcased: [Boolean!]
|
||||||
) {
|
) {
|
||||||
${releasesFragment}
|
${releasesFragment}
|
||||||
|
${batchFragment}
|
||||||
}
|
}
|
||||||
`, {
|
`, {
|
||||||
hasAuth: !!store.state.auth.user,
|
hasAuth: !!store.state.auth.user,
|
||||||
|
@ -36,10 +39,11 @@ function initReleasesActions(store, router) {
|
||||||
before,
|
before,
|
||||||
orderBy,
|
orderBy,
|
||||||
exclude: store.state.ui.tagFilter,
|
exclude: store.state.ui.tagFilter,
|
||||||
|
batchShowcased: range === 'new' ? true : [true, false],
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
releases: releases.map((release) => curateRelease(release.release || release)),
|
releases: releases.map((release) => curateRelease(release.release || release, 'scene', { lastBatch: lastBatch.id })),
|
||||||
totalCount,
|
totalCount,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -80,12 +84,7 @@ function initReleasesActions(store, router) {
|
||||||
connection: moviesConnection(
|
connection: moviesConnection(
|
||||||
first: $limit
|
first: $limit
|
||||||
offset: $offset
|
offset: $offset
|
||||||
orderBy: DATE_DESC
|
orderBy: EFFECTIVE_DATE_DESC
|
||||||
filter: {
|
|
||||||
date: {
|
|
||||||
isNull: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) {
|
) {
|
||||||
movies: nodes {
|
movies: nodes {
|
||||||
${movieFields}
|
${movieFields}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import { createRouter, createWebHistory } from 'vue-router';
|
import { createRouter, createWebHistory } from 'vue-router';
|
||||||
|
|
||||||
|
import setPageTitle from './set-page-title';
|
||||||
|
|
||||||
import Home from '../components/home/home.vue';
|
import Home from '../components/home/home.vue';
|
||||||
import Login from '../components/auth/login.vue';
|
import Login from '../components/auth/login.vue';
|
||||||
import Signup from '../components/auth/signup.vue';
|
import Signup from '../components/auth/signup.vue';
|
||||||
|
@ -233,7 +235,7 @@ const routes = [
|
||||||
name: 'notifications',
|
name: 'notifications',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/stash/:stashId/:stashSlug',
|
path: '/stash/:username/:stashSlug',
|
||||||
redirect: (from) => ({
|
redirect: (from) => ({
|
||||||
name: 'stash',
|
name: 'stash',
|
||||||
params: {
|
params: {
|
||||||
|
@ -244,7 +246,7 @@ const routes = [
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/stash/:stashId/:stashSlug?/:range/:pageNumber',
|
path: '/stash/:username/:stashSlug/:range/:pageNumber',
|
||||||
component: Stash,
|
component: Stash,
|
||||||
name: 'stash',
|
name: 'stash',
|
||||||
},
|
},
|
||||||
|
@ -257,11 +259,17 @@ const routes = [
|
||||||
path: '/stats',
|
path: '/stats',
|
||||||
component: Stats,
|
component: Stats,
|
||||||
name: 'stats',
|
name: 'stats',
|
||||||
|
meta: {
|
||||||
|
title: 'Stats',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/not-found',
|
path: '/not-found',
|
||||||
name: 'not-found',
|
name: 'not-found',
|
||||||
component: NotFound,
|
component: NotFound,
|
||||||
|
meta: {
|
||||||
|
title: 'Not Found',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/:catchAll(.*)',
|
path: '/:catchAll(.*)',
|
||||||
|
@ -276,4 +284,6 @@ const router = createRouter({
|
||||||
routes,
|
routes,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.beforeEach((to) => setPageTitle(to.meta.title));
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
function setPageTitle(name) {
|
||||||
|
if (name) {
|
||||||
|
document.title = `traxxx - ${name}`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.title = 'traxxx';
|
||||||
|
}
|
||||||
|
|
||||||
|
export default setPageTitle;
|
|
@ -1,6 +1,6 @@
|
||||||
import { graphql } from '../api';
|
import { graphql } from '../api';
|
||||||
import { releaseFields } from '../fragments';
|
import { releaseFields } from '../fragments';
|
||||||
import { curateSite, curateRelease } from '../curate';
|
import { curateEntity, curateRelease } from '../curate';
|
||||||
import getDateRange from '../get-date-range';
|
import getDateRange from '../get-date-range';
|
||||||
|
|
||||||
function initSitesActions(store, _router) {
|
function initSitesActions(store, _router) {
|
||||||
|
@ -41,7 +41,7 @@ function initSitesActions(store, _router) {
|
||||||
}
|
}
|
||||||
releasesConnection(
|
releasesConnection(
|
||||||
filter: {
|
filter: {
|
||||||
date: {
|
effectiveDate: {
|
||||||
lessThan: $before,
|
lessThan: $before,
|
||||||
greaterThan: $after,
|
greaterThan: $after,
|
||||||
},
|
},
|
||||||
|
@ -75,7 +75,7 @@ function initSitesActions(store, _router) {
|
||||||
equalTo: $siteSlug
|
equalTo: $siteSlug
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
date: {
|
effectiveDate: {
|
||||||
lessThan: $before,
|
lessThan: $before,
|
||||||
greaterThan: $after
|
greaterThan: $after
|
||||||
}
|
}
|
||||||
|
@ -103,13 +103,12 @@ function initSitesActions(store, _router) {
|
||||||
after,
|
after,
|
||||||
before,
|
before,
|
||||||
orderBy,
|
orderBy,
|
||||||
isNew: store.getters.isNew,
|
|
||||||
exclude: store.state.ui.filter,
|
exclude: store.state.ui.filter,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
site: curateSite(site),
|
site: curateEntity(site),
|
||||||
releases: releases.map(release => curateRelease(release)),
|
releases: releases.map((release) => curateRelease(release)),
|
||||||
totalCount,
|
totalCount,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,14 +10,16 @@ import { curateStash } from '../curate';
|
||||||
|
|
||||||
function initStashesActions(store, _router) {
|
function initStashesActions(store, _router) {
|
||||||
async function fetchStash(context, {
|
async function fetchStash(context, {
|
||||||
stashId,
|
stashSlug,
|
||||||
|
username,
|
||||||
section = 'scenes',
|
section = 'scenes',
|
||||||
pageNumber = 1,
|
pageNumber = 1,
|
||||||
limit = 20,
|
limit = 20,
|
||||||
}) {
|
}) {
|
||||||
const { stash } = await graphql(`
|
const { stashes: [stash] } = await graphql(`
|
||||||
query Stash(
|
query Stash(
|
||||||
$stashId: Int!
|
$stashSlug: String!
|
||||||
|
$username: String!
|
||||||
$includeScenes: Boolean!
|
$includeScenes: Boolean!
|
||||||
$includeActors: Boolean!
|
$includeActors: Boolean!
|
||||||
$includeMovies: Boolean!
|
$includeMovies: Boolean!
|
||||||
|
@ -26,7 +28,12 @@ function initStashesActions(store, _router) {
|
||||||
$hasAuth: Boolean!
|
$hasAuth: Boolean!
|
||||||
$userId: Int
|
$userId: Int
|
||||||
) {
|
) {
|
||||||
stash(id: $stashId) {
|
stashes(
|
||||||
|
filter: {
|
||||||
|
user: { username: { equalTo: $username } }
|
||||||
|
slug: { equalTo: $stashSlug }
|
||||||
|
}
|
||||||
|
) {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
slug
|
slug
|
||||||
|
@ -101,7 +108,8 @@ function initStashesActions(store, _router) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`, {
|
`, {
|
||||||
stashId: Number(stashId),
|
stashSlug,
|
||||||
|
username,
|
||||||
includeScenes: section === 'scenes',
|
includeScenes: section === 'scenes',
|
||||||
includeActors: section === 'actors',
|
includeActors: section === 'actors',
|
||||||
includeMovies: section === 'movies',
|
includeMovies: section === 'movies',
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { graphql, get } from '../api';
|
import { graphql, get } from '../api';
|
||||||
import {
|
import {
|
||||||
releaseFields,
|
releaseFields,
|
||||||
|
batchFragment,
|
||||||
} from '../fragments';
|
} from '../fragments';
|
||||||
import { curateTag, curateRelease } from '../curate';
|
import { curateTag, curateRelease } from '../curate';
|
||||||
import getDateRange from '../get-date-range';
|
import getDateRange from '../get-date-range';
|
||||||
|
@ -14,14 +15,14 @@ function initTagsActions(store, _router) {
|
||||||
}) {
|
}) {
|
||||||
const { before, after, orderBy } = getDateRange(range);
|
const { before, after, orderBy } = getDateRange(range);
|
||||||
|
|
||||||
const { tagBySlug } = await graphql(`
|
const { tagBySlug, scenesConnection, batches: [lastBatch] } = await graphql(`
|
||||||
query Tag(
|
query Tag(
|
||||||
$tagSlug: String!
|
$tagSlug: String!
|
||||||
$offset: Int = 0,
|
$offset: Int = 0,
|
||||||
$limit:Int = 1000,
|
$limit:Int = 1000,
|
||||||
$after:Datetime = "1900-01-01",
|
$after:Datetime = "1900-01-01",
|
||||||
$before:Datetime = "2100-01-01",
|
$before:Datetime = "2100-01-01",
|
||||||
$orderBy: [ReleasesOrderBy!],
|
$orderBy: [ReleasesSummariesOrderBy!],
|
||||||
$exclude: [String!]
|
$exclude: [String!]
|
||||||
$hasAuth: Boolean!
|
$hasAuth: Boolean!
|
||||||
$userId: Int
|
$userId: Int
|
||||||
|
@ -154,32 +155,33 @@ function initTagsActions(store, _router) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scenesConnection(
|
|
||||||
filter: {
|
|
||||||
date: {
|
|
||||||
lessThan: $before,
|
|
||||||
greaterThan: $after,
|
|
||||||
},
|
|
||||||
releasesTagsConnection: {
|
|
||||||
none: {
|
|
||||||
tag: {
|
|
||||||
slug: {
|
|
||||||
in: $exclude
|
|
||||||
}
|
}
|
||||||
}
|
scenesConnection: releasesSummariesConnection(
|
||||||
}
|
first: $limit
|
||||||
}
|
|
||||||
},
|
|
||||||
first: $limit,
|
|
||||||
orderBy: $orderBy,
|
|
||||||
offset: $offset
|
offset: $offset
|
||||||
|
orderBy: $orderBy
|
||||||
|
filter: {
|
||||||
|
or: [
|
||||||
|
{
|
||||||
|
not: { tags: { overlaps: $exclude } }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
tags: { isNull: true }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
tags: { anyEqualTo: $tagSlug }
|
||||||
|
effectiveDate: { lessThan: $before, greaterThan: $after }
|
||||||
|
showcased: { equalTo: true }
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
releases: nodes {
|
releases: nodes {
|
||||||
|
release {
|
||||||
${releaseFields}
|
${releaseFields}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
totalCount
|
totalCount
|
||||||
}
|
}
|
||||||
}
|
${batchFragment}
|
||||||
}
|
}
|
||||||
`, {
|
`, {
|
||||||
tagSlug,
|
tagSlug,
|
||||||
|
@ -195,8 +197,8 @@ function initTagsActions(store, _router) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tag: curateTag(tagBySlug, null, curateRelease),
|
tag: curateTag(tagBySlug, null, curateRelease),
|
||||||
releases: tagBySlug.scenesConnection.releases.map((release) => curateRelease(release)),
|
releases: scenesConnection.releases.map(({ release }) => curateRelease(release, 'scene', { lastBatch: lastBatch.id })),
|
||||||
totalCount: tagBySlug.scenesConnection.totalCount,
|
totalCount: scenesConnection.totalCount,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
import { graphql, patch } from '../api';
|
import { graphql, patch } from '../api';
|
||||||
import { releaseFields, actorStashesFields } from '../fragments';
|
|
||||||
|
import {
|
||||||
|
releaseFields,
|
||||||
|
batchFragment,
|
||||||
|
campaignFields,
|
||||||
|
actorStashesFields,
|
||||||
|
} from '../fragments';
|
||||||
|
|
||||||
import { curateRelease, curateActor, curateNotification } from '../curate';
|
import { curateRelease, curateActor, curateNotification } from '../curate';
|
||||||
|
|
||||||
function initUiActions(store, _router) {
|
function initUiActions(store, _router) {
|
||||||
|
@ -61,6 +68,7 @@ function initUiActions(store, _router) {
|
||||||
${releaseFields}
|
${releaseFields}
|
||||||
}
|
}
|
||||||
alert {
|
alert {
|
||||||
|
all
|
||||||
tags: alertsTags {
|
tags: alertsTags {
|
||||||
tag {
|
tag {
|
||||||
id
|
id
|
||||||
|
@ -75,7 +83,7 @@ function initUiActions(store, _router) {
|
||||||
slug
|
slug
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entity: alertsEntity {
|
entities: alertsEntities {
|
||||||
entity {
|
entity {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
|
@ -83,6 +91,10 @@ function initUiActions(store, _router) {
|
||||||
independent
|
independent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
matches: alertsMatches {
|
||||||
|
property
|
||||||
|
expression
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
totalCount
|
totalCount
|
||||||
|
@ -218,6 +230,7 @@ function initUiActions(store, _router) {
|
||||||
}
|
}
|
||||||
${actorStashesFields}
|
${actorStashesFields}
|
||||||
}
|
}
|
||||||
|
${batchFragment}
|
||||||
}
|
}
|
||||||
`, {
|
`, {
|
||||||
query,
|
query,
|
||||||
|
@ -227,7 +240,7 @@ function initUiActions(store, _router) {
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
releases: res?.results.map((result) => curateRelease(result.release)) || [],
|
releases: res?.results.map((result) => curateRelease(result.release, 'scene', { lastBatch: res?.batches[0].id })) || [],
|
||||||
actors: res?.actors.map((actor) => curateActor(actor)) || [],
|
actors: res?.actors.map((actor) => curateActor(actor)) || [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -239,29 +252,7 @@ function initUiActions(store, _router) {
|
||||||
$maxRatio: BigFloat
|
$maxRatio: BigFloat
|
||||||
) {
|
) {
|
||||||
randomCampaign: getRandomCampaign(minRatio: $minRatio, maxRatio: $maxRatio) {
|
randomCampaign: getRandomCampaign(minRatio: $minRatio, maxRatio: $maxRatio) {
|
||||||
url
|
${campaignFields}
|
||||||
affiliate {
|
|
||||||
url
|
|
||||||
}
|
|
||||||
banner {
|
|
||||||
id
|
|
||||||
type
|
|
||||||
ratio
|
|
||||||
entity {
|
|
||||||
type
|
|
||||||
slug
|
|
||||||
parent {
|
|
||||||
type
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
entity {
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
parent {
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`, {
|
`, {
|
||||||
|
@ -272,6 +263,22 @@ function initUiActions(store, _router) {
|
||||||
return randomCampaign;
|
return randomCampaign;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fetchCampaign(context, campaignId) {
|
||||||
|
const { campaign } = await graphql(`
|
||||||
|
query Campaign(
|
||||||
|
$campaignId: Int!
|
||||||
|
) {
|
||||||
|
campaign(id: $campaignId) {
|
||||||
|
${campaignFields}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, {
|
||||||
|
campaignId: Number(campaignId),
|
||||||
|
});
|
||||||
|
|
||||||
|
return campaign;
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchStats() {
|
async function fetchStats() {
|
||||||
const {
|
const {
|
||||||
scenes,
|
scenes,
|
||||||
|
@ -279,23 +286,15 @@ function initUiActions(store, _router) {
|
||||||
actors,
|
actors,
|
||||||
networks,
|
networks,
|
||||||
channels,
|
channels,
|
||||||
|
batches: [batch],
|
||||||
} = await graphql(`
|
} = await graphql(`
|
||||||
query Stats {
|
query Stats {
|
||||||
scenes: releasesConnection(
|
scenes: releasesConnection { totalCount }
|
||||||
last: 1,
|
|
||||||
orderBy: BATCH_BY_CREATED_BATCH_ID__CREATED_AT_ASC
|
|
||||||
) {
|
|
||||||
totalCount
|
|
||||||
scenes: nodes {
|
|
||||||
batch: createdBatch {
|
|
||||||
createdAt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
movies: moviesConnection { totalCount }
|
movies: moviesConnection { totalCount }
|
||||||
actors: actorsConnection { totalCount }
|
actors: actorsConnection { totalCount }
|
||||||
networks: entitiesConnection(filter: { type: { equalTo: "network" } }) { totalCount }
|
networks: entitiesConnection(filter: { type: { equalTo: "network" } }) { totalCount }
|
||||||
channels: entitiesConnection(filter: { type: { equalTo: "channel" } }) { totalCount }
|
channels: entitiesConnection(filter: { type: { equalTo: "channel" } }) { totalCount }
|
||||||
|
batches(orderBy: CREATED_AT_DESC, first: 1) { createdAt }
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
@ -305,7 +304,7 @@ function initUiActions(store, _router) {
|
||||||
totalActors: actors.totalCount,
|
totalActors: actors.totalCount,
|
||||||
totalNetworks: networks.totalCount,
|
totalNetworks: networks.totalCount,
|
||||||
totalChannels: channels.totalCount,
|
totalChannels: channels.totalCount,
|
||||||
lastScrape: new Date(scenes.scenes[0]?.batch.createdAt),
|
lastScrape: new Date(batch.createdAt),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,6 +318,7 @@ function initUiActions(store, _router) {
|
||||||
setBatch,
|
setBatch,
|
||||||
setSfw,
|
setSfw,
|
||||||
setTheme,
|
setTheme,
|
||||||
|
fetchCampaign,
|
||||||
fetchRandomCampaign,
|
fetchRandomCampaign,
|
||||||
fetchNotifications,
|
fetchNotifications,
|
||||||
fetchStats,
|
fetchStats,
|
||||||
|
|
|
@ -71,6 +71,7 @@ function initUsersActions(store, _router) {
|
||||||
id
|
id
|
||||||
notify
|
notify
|
||||||
email
|
email
|
||||||
|
all
|
||||||
stashes: alertsStashes {
|
stashes: alertsStashes {
|
||||||
stash {
|
stash {
|
||||||
id
|
id
|
||||||
|
@ -85,12 +86,17 @@ function initUsersActions(store, _router) {
|
||||||
slug
|
slug
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
matches: alertsMatches {
|
||||||
|
id
|
||||||
|
property
|
||||||
|
expression
|
||||||
|
}
|
||||||
actors: alertsActors {
|
actors: alertsActors {
|
||||||
actor {
|
actor {
|
||||||
${actorFields}
|
${actorFields}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entity: alertsEntity {
|
entities: alertsEntities {
|
||||||
entity {
|
entity {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
|
|
|
@ -12,6 +12,8 @@ module.exports = {
|
||||||
password: 'password',
|
password: 'password',
|
||||||
database: 'traxxx',
|
database: 'traxxx',
|
||||||
},
|
},
|
||||||
|
timeout: 5000,
|
||||||
|
graphiql: false,
|
||||||
},
|
},
|
||||||
web: {
|
web: {
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
|
@ -28,6 +30,20 @@ module.exports = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
redis: {
|
||||||
|
host: 'localhost',
|
||||||
|
port: 6379,
|
||||||
|
username: null,
|
||||||
|
password: null,
|
||||||
|
},
|
||||||
|
location: {
|
||||||
|
userAgent: 'contact via https://traxxx.me/',
|
||||||
|
},
|
||||||
|
analytics: {
|
||||||
|
enabled: false,
|
||||||
|
address: 'http://localhost:3000/script.js',
|
||||||
|
siteId: '1b28ac3b-d229-43bf-aec9-75cf0a72a466',
|
||||||
|
},
|
||||||
s3: {
|
s3: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
bucket: 'traxxx',
|
bucket: 'traxxx',
|
||||||
|
@ -37,6 +53,8 @@ module.exports = {
|
||||||
auth: {
|
auth: {
|
||||||
login: true,
|
login: true,
|
||||||
signup: true,
|
signup: true,
|
||||||
|
usernameLength: [2, 24],
|
||||||
|
usernamePattern: /^[a-zA-Z0-9_-]+$/,
|
||||||
},
|
},
|
||||||
exclude: {
|
exclude: {
|
||||||
channels: [
|
channels: [
|
||||||
|
@ -53,6 +71,7 @@ module.exports = {
|
||||||
'amberathome',
|
'amberathome',
|
||||||
'marycarey',
|
'marycarey',
|
||||||
'racqueldevonshire',
|
'racqueldevonshire',
|
||||||
|
'aziani',
|
||||||
// blowpass
|
// blowpass
|
||||||
'sunlustxxx',
|
'sunlustxxx',
|
||||||
// ddfnetwork
|
// ddfnetwork
|
||||||
|
@ -63,6 +82,10 @@ module.exports = {
|
||||||
'lowartfilms',
|
'lowartfilms',
|
||||||
// freeones
|
// freeones
|
||||||
'freeones',
|
'freeones',
|
||||||
|
// new sesations
|
||||||
|
'tabutales',
|
||||||
|
'talesfromtheedge',
|
||||||
|
'shanedieselsbangingbabes',
|
||||||
// pornpros
|
// pornpros
|
||||||
'milfhumiliation',
|
'milfhumiliation',
|
||||||
'humiliated',
|
'humiliated',
|
||||||
|
@ -254,18 +277,21 @@ module.exports = {
|
||||||
'www.tushyraw.com',
|
'www.tushyraw.com',
|
||||||
'www.deeper.com',
|
'www.deeper.com',
|
||||||
'www.slayed.com',
|
'www.slayed.com',
|
||||||
|
'www.milfy.com',
|
||||||
'sthw-trailer-vixen.ssl-cdn.com',
|
'sthw-trailer-vixen.ssl-cdn.com',
|
||||||
'sthw-trailer-tushy.ssl-cdn.com',
|
'sthw-trailer-tushy.ssl-cdn.com',
|
||||||
'sthw-trailer-tushyraw.ssl-cdn.com',
|
'sthw-trailer-tushyraw.ssl-cdn.com',
|
||||||
'sthw-trailer-blacked.ssl-cdn.com',
|
'sthw-trailer-blacked.ssl-cdn.com',
|
||||||
'sthw-trailer-blackedraw.ssl-cdn.com',
|
'sthw-trailer-blackedraw.ssl-cdn.com',
|
||||||
'sthw-trailer-deeper.ssl-cdn.com',
|
'sthw-trailer-deeper.ssl-cdn.com',
|
||||||
|
'sthw-trailer-milfy.ssl-cdn.com',
|
||||||
'streamhw-trailer-vixen.ssl-cdn.com',
|
'streamhw-trailer-vixen.ssl-cdn.com',
|
||||||
'streamhw-trailer-tushy.ssl-cdn.com',
|
'streamhw-trailer-tushy.ssl-cdn.com',
|
||||||
'streamhw-trailer-tushyraw.ssl-cdn.com',
|
'streamhw-trailer-tushyraw.ssl-cdn.com',
|
||||||
'streamhw-trailer-blacked.ssl-cdn.com',
|
'streamhw-trailer-blacked.ssl-cdn.com',
|
||||||
'streamhw-trailer-blackedraw.ssl-cdn.com',
|
'streamhw-trailer-blackedraw.ssl-cdn.com',
|
||||||
'streamhw-trailer-deeper.ssl-cdn.com',
|
'streamhw-trailer-deeper.ssl-cdn.com',
|
||||||
|
'streamhw-trailer-milfy.ssl-cdn.com',
|
||||||
'cdn.vixen.com',
|
'cdn.vixen.com',
|
||||||
'cdn.tushy.com',
|
'cdn.tushy.com',
|
||||||
'cdn.blacked.com',
|
'cdn.blacked.com',
|
||||||
|
@ -273,6 +299,7 @@ module.exports = {
|
||||||
'cdn.blackedraw.com',
|
'cdn.blackedraw.com',
|
||||||
'cdn.tushyraw.com',
|
'cdn.tushyraw.com',
|
||||||
'cdn.slayed.com',
|
'cdn.slayed.com',
|
||||||
|
'cdn.milfy.com',
|
||||||
'www.vogov.com',
|
'www.vogov.com',
|
||||||
'www.vogov.com',
|
'www.vogov.com',
|
||||||
'www.nubiles.net',
|
'www.nubiles.net',
|
||||||
|
@ -297,6 +324,7 @@ module.exports = {
|
||||||
enable: false,
|
enable: false,
|
||||||
hostnames: [ // these can run in the same browser session
|
hostnames: [ // these can run in the same browser session
|
||||||
'www.kink.com',
|
'www.kink.com',
|
||||||
|
'store2.psmcdn.net', // Team Skeet API
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
cloudflare: {
|
cloudflare: {
|
||||||
|
@ -337,14 +365,20 @@ module.exports = {
|
||||||
},
|
},
|
||||||
media: {
|
media: {
|
||||||
path: './media',
|
path: './media',
|
||||||
|
transferSources: {
|
||||||
|
local: 'http://localhost:5000/media',
|
||||||
|
s3: 'https://cdn.traxxx.me',
|
||||||
|
},
|
||||||
maxSize: 1000,
|
maxSize: 1000,
|
||||||
quality: 80,
|
quality: 80,
|
||||||
thumbnailSize: 320, // width for 16:9 will be exactly 576px
|
thumbnailSize: 320, // width for 16:9 will be exactly 576px
|
||||||
thumbnailQuality: 100,
|
thumbnailQuality: 100,
|
||||||
lazySize: 90,
|
lazySize: 90,
|
||||||
lazyQuality: 90,
|
lazyQuality: 90,
|
||||||
trailerQuality: [480, 540, 360, 720, 960, 1080, 320, 1440, 1600, 1920, 2160, 270, 240, 180],
|
trailerQuality: [540, 720, 480, 360, 960, 1080, 320, 1440, 1600, 1920, 2160, 270, 240, 180],
|
||||||
limit: 25, // max number of photos per release
|
limit: 25, // max number of photos per release
|
||||||
|
attempts: 2,
|
||||||
|
fetchStreams: true,
|
||||||
streamConcurrency: 2, // max number of video streams (m3u8 etc.) to fetch and process at once
|
streamConcurrency: 2, // max number of video streams (m3u8 etc.) to fetch and process at once
|
||||||
},
|
},
|
||||||
titleSlugLength: 50,
|
titleSlugLength: 50,
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
const config = require('config');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
apps: [
|
||||||
|
{
|
||||||
|
name: 'traxxx',
|
||||||
|
script: 'src/init.js',
|
||||||
|
exec_mode: 'cluster',
|
||||||
|
instances: 2,
|
||||||
|
restart_delay: 3000,
|
||||||
|
args: '--server',
|
||||||
|
merge_logs: true,
|
||||||
|
env: {
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...(config.analytics.enabled ? [{
|
||||||
|
name: 'umami',
|
||||||
|
script: 'npm start',
|
||||||
|
cwd: process.env.UMAMI_DIR || '../umami',
|
||||||
|
restart_delay: 3000,
|
||||||
|
merge_logs: true,
|
||||||
|
env: {
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
},
|
||||||
|
}] : []),
|
||||||
|
],
|
||||||
|
};
|
|
@ -64,6 +64,7 @@ exports.up = (knex) => Promise.resolve()
|
||||||
table.boolean('independent')
|
table.boolean('independent')
|
||||||
.defaultTo(false);
|
.defaultTo(false);
|
||||||
|
|
||||||
|
table.boolean('showcased');
|
||||||
table.boolean('visible')
|
table.boolean('visible')
|
||||||
.defaultTo(true);
|
.defaultTo(true);
|
||||||
|
|
||||||
|
@ -272,6 +273,7 @@ exports.up = (knex) => Promise.resolve()
|
||||||
table.integer('age', 3);
|
table.integer('age', 3);
|
||||||
|
|
||||||
table.text('gender', 18);
|
table.text('gender', 18);
|
||||||
|
table.text('orientation');
|
||||||
table.text('description');
|
table.text('description');
|
||||||
|
|
||||||
table.text('birth_city');
|
table.text('birth_city');
|
||||||
|
@ -345,6 +347,7 @@ exports.up = (knex) => Promise.resolve()
|
||||||
|
|
||||||
table.text('real_name');
|
table.text('real_name');
|
||||||
table.text('gender', 18);
|
table.text('gender', 18);
|
||||||
|
table.text('orientation');
|
||||||
|
|
||||||
table.date('date_of_birth');
|
table.date('date_of_birth');
|
||||||
table.date('date_of_death');
|
table.date('date_of_death');
|
||||||
|
@ -651,6 +654,8 @@ exports.up = (knex) => Promise.resolve()
|
||||||
table.integer('duration')
|
table.integer('duration')
|
||||||
.unsigned();
|
.unsigned();
|
||||||
|
|
||||||
|
table.specificType('qualities', 'text[]');
|
||||||
|
|
||||||
table.boolean('deep');
|
table.boolean('deep');
|
||||||
table.text('deep_url', 1000);
|
table.text('deep_url', 1000);
|
||||||
|
|
||||||
|
@ -804,6 +809,8 @@ exports.up = (knex) => Promise.resolve()
|
||||||
table.text('original_tag');
|
table.text('original_tag');
|
||||||
|
|
||||||
table.unique(['tag_id', 'release_id']);
|
table.unique(['tag_id', 'release_id']);
|
||||||
|
table.index('tag_id');
|
||||||
|
table.index('release_id');
|
||||||
}))
|
}))
|
||||||
.then(() => knex.schema.createTable('releases_search', (table) => {
|
.then(() => knex.schema.createTable('releases_search', (table) => {
|
||||||
table.integer('release_id', 16)
|
table.integer('release_id', 16)
|
||||||
|
@ -873,6 +880,8 @@ exports.up = (knex) => Promise.resolve()
|
||||||
|
|
||||||
table.datetime('created_at')
|
table.datetime('created_at')
|
||||||
.defaultTo(knex.fn.now());
|
.defaultTo(knex.fn.now());
|
||||||
|
|
||||||
|
table.index('scene_id');
|
||||||
}))
|
}))
|
||||||
.then(() => knex.schema.createTable('movies_covers', (table) => {
|
.then(() => knex.schema.createTable('movies_covers', (table) => {
|
||||||
table.integer('movie_id', 16)
|
table.integer('movie_id', 16)
|
||||||
|
@ -916,12 +925,151 @@ exports.up = (knex) => Promise.resolve()
|
||||||
|
|
||||||
table.unique('movie_id');
|
table.unique('movie_id');
|
||||||
}))
|
}))
|
||||||
|
.then(() => knex.schema.createTable('movies_photos', (table) => {
|
||||||
|
table.integer('movie_id', 16)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('movies')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.text('media_id', 21)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('media');
|
||||||
|
|
||||||
|
table.unique(['movie_id', 'media_id']);
|
||||||
|
}))
|
||||||
.then(() => knex.schema.createTable('movies_search', (table) => {
|
.then(() => knex.schema.createTable('movies_search', (table) => {
|
||||||
table.integer('movie_id', 16)
|
table.integer('movie_id', 16)
|
||||||
.references('id')
|
.references('id')
|
||||||
.inTable('movies')
|
.inTable('movies')
|
||||||
.onDelete('cascade');
|
.onDelete('cascade');
|
||||||
}))
|
}))
|
||||||
|
.then(() => knex.schema.createTable('series', (table) => {
|
||||||
|
table.increments('id', 16);
|
||||||
|
|
||||||
|
table.integer('entity_id', 12)
|
||||||
|
.references('id')
|
||||||
|
.inTable('entities')
|
||||||
|
.notNullable();
|
||||||
|
|
||||||
|
table.integer('studio_id', 12)
|
||||||
|
.references('id')
|
||||||
|
.inTable('entities');
|
||||||
|
|
||||||
|
table.text('entry_id');
|
||||||
|
table.unique(['entity_id', 'entry_id']);
|
||||||
|
|
||||||
|
table.text('url', 1000);
|
||||||
|
table.text('title');
|
||||||
|
table.text('slug');
|
||||||
|
|
||||||
|
table.timestamp('date');
|
||||||
|
table.index('date');
|
||||||
|
|
||||||
|
table.enum('date_precision', ['year', 'month', 'week', 'day', 'hour', 'minute', 'second'])
|
||||||
|
.defaultTo('day');
|
||||||
|
|
||||||
|
table.text('description');
|
||||||
|
|
||||||
|
table.boolean('deep');
|
||||||
|
table.text('deep_url', 1000);
|
||||||
|
|
||||||
|
table.text('comment');
|
||||||
|
|
||||||
|
table.integer('created_batch_id', 12)
|
||||||
|
.references('id')
|
||||||
|
.inTable('batches')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.integer('updated_batch_id', 12)
|
||||||
|
.references('id')
|
||||||
|
.inTable('batches')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.datetime('created_at')
|
||||||
|
.defaultTo(knex.fn.now());
|
||||||
|
}))
|
||||||
|
.then(() => knex.schema.createTable('series_scenes', (table) => {
|
||||||
|
table.integer('serie_id', 16)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('series')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.integer('scene_id', 16)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('releases')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.unique(['serie_id', 'scene_id']);
|
||||||
|
|
||||||
|
table.datetime('created_at')
|
||||||
|
.defaultTo(knex.fn.now());
|
||||||
|
}))
|
||||||
|
.then(() => knex.schema.createTable('series_trailers', (table) => {
|
||||||
|
table.integer('serie_id', 16)
|
||||||
|
.unique()
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('series')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.text('media_id', 21)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('media');
|
||||||
|
}))
|
||||||
|
.then(() => knex.schema.createTable('series_posters', (table) => {
|
||||||
|
table.integer('serie_id', 16)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('series')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.text('media_id', 21)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('media')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.unique('serie_id');
|
||||||
|
}))
|
||||||
|
.then(() => knex.schema.createTable('series_covers', (table) => {
|
||||||
|
table.integer('serie_id', 16)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('series')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.text('media_id', 21)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('media');
|
||||||
|
|
||||||
|
table.unique(['serie_id', 'media_id']);
|
||||||
|
}))
|
||||||
|
.then(() => knex.schema.createTable('series_photos', (table) => {
|
||||||
|
table.integer('serie_id', 16)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('series')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.text('media_id', 21)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('media');
|
||||||
|
|
||||||
|
table.unique(['serie_id', 'media_id']);
|
||||||
|
}))
|
||||||
|
.then(() => knex.schema.createTable('series_search', (table) => {
|
||||||
|
table.integer('serie_id', 16)
|
||||||
|
.references('id')
|
||||||
|
.inTable('series')
|
||||||
|
.onDelete('cascade');
|
||||||
|
}))
|
||||||
.then(() => knex.schema.createTable('chapters', (table) => {
|
.then(() => knex.schema.createTable('chapters', (table) => {
|
||||||
table.increments('id', 16);
|
table.increments('id', 16);
|
||||||
|
|
||||||
|
@ -1062,6 +1210,8 @@ exports.up = (knex) => Promise.resolve()
|
||||||
table.datetime('created_at')
|
table.datetime('created_at')
|
||||||
.notNullable()
|
.notNullable()
|
||||||
.defaultTo(knex.fn.now());
|
.defaultTo(knex.fn.now());
|
||||||
|
|
||||||
|
table.datetime('last_login');
|
||||||
}))
|
}))
|
||||||
.then(() => knex.schema.createTable('stashes', (table) => {
|
.then(() => knex.schema.createTable('stashes', (table) => {
|
||||||
table.increments('id');
|
table.increments('id');
|
||||||
|
@ -1088,7 +1238,12 @@ exports.up = (knex) => Promise.resolve()
|
||||||
table.datetime('created_at')
|
table.datetime('created_at')
|
||||||
.notNullable()
|
.notNullable()
|
||||||
.defaultTo(knex.fn.now());
|
.defaultTo(knex.fn.now());
|
||||||
|
|
||||||
|
table.unique(['user_id', 'slug']);
|
||||||
}))
|
}))
|
||||||
|
.then(() => knex.raw(`
|
||||||
|
CREATE UNIQUE INDEX unique_primary ON stashes (user_id, "primary") WHERE ("primary" = TRUE);
|
||||||
|
`))
|
||||||
.then(() => knex.schema.createTable('stashes_scenes', (table) => {
|
.then(() => knex.schema.createTable('stashes_scenes', (table) => {
|
||||||
table.integer('stash_id')
|
table.integer('stash_id')
|
||||||
.notNullable()
|
.notNullable()
|
||||||
|
@ -1152,6 +1307,27 @@ exports.up = (knex) => Promise.resolve()
|
||||||
.notNullable()
|
.notNullable()
|
||||||
.defaultTo(knex.fn.now());
|
.defaultTo(knex.fn.now());
|
||||||
}))
|
}))
|
||||||
|
.then(() => knex.schema.createTable('stashes_series', (table) => {
|
||||||
|
table.integer('stash_id')
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('stashes')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.integer('serie_id')
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('series')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.unique(['stash_id', 'serie_id']);
|
||||||
|
|
||||||
|
table.string('comment');
|
||||||
|
|
||||||
|
table.datetime('created_at')
|
||||||
|
.notNullable()
|
||||||
|
.defaultTo(knex.fn.now());
|
||||||
|
}))
|
||||||
.then(() => knex.schema.createTable('alerts', (table) => {
|
.then(() => knex.schema.createTable('alerts', (table) => {
|
||||||
table.increments('id');
|
table.increments('id');
|
||||||
|
|
||||||
|
@ -1329,6 +1505,9 @@ exports.up = (knex) => Promise.resolve()
|
||||||
.notNullable()
|
.notNullable()
|
||||||
.defaultTo(knex.fn.now());
|
.defaultTo(knex.fn.now());
|
||||||
}))
|
}))
|
||||||
|
.then(() => knex.raw(`
|
||||||
|
ALTER TABLE banners ADD COLUMN ratio numeric GENERATED ALWAYS AS (ROUND(width::decimal/ height::decimal, 2)) STORED;
|
||||||
|
`))
|
||||||
.then(() => knex.schema.createTable('banners_tags', (table) => {
|
.then(() => knex.schema.createTable('banners_tags', (table) => {
|
||||||
table.increments('id');
|
table.increments('id');
|
||||||
|
|
||||||
|
@ -1372,17 +1551,46 @@ exports.up = (knex) => Promise.resolve()
|
||||||
.notNullable()
|
.notNullable()
|
||||||
.defaultTo(knex.fn.now());
|
.defaultTo(knex.fn.now());
|
||||||
}))
|
}))
|
||||||
|
.then(() => knex.schema.createTable('random_campaign', (table) => {
|
||||||
|
table.integer('id')
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('campaigns');
|
||||||
|
|
||||||
|
table.text('banner_id')
|
||||||
|
.references('id')
|
||||||
|
.inTable('banners');
|
||||||
|
|
||||||
|
table.text('url');
|
||||||
|
|
||||||
|
table.integer('entity_id')
|
||||||
|
.references('id')
|
||||||
|
.inTable('entities');
|
||||||
|
|
||||||
|
table.string('affiliate_id')
|
||||||
|
.references('id')
|
||||||
|
.inTable('affiliates');
|
||||||
|
|
||||||
|
table.integer('parent_id')
|
||||||
|
.references('id')
|
||||||
|
.inTable('entities');
|
||||||
|
}))
|
||||||
// SEARCH AND SORT
|
// SEARCH AND SORT
|
||||||
.then(() => { // eslint-disable-line arrow-body-style
|
.then(() => { // eslint-disable-line arrow-body-style
|
||||||
// allow vim fold
|
// allow vim fold
|
||||||
return knex.raw(`
|
return knex.raw(`
|
||||||
ALTER TABLE releases_search ADD COLUMN document tsvector;
|
ALTER TABLE releases_search ADD COLUMN document tsvector;
|
||||||
ALTER TABLE movies_search ADD COLUMN document tsvector;
|
ALTER TABLE movies_search ADD COLUMN document tsvector;
|
||||||
|
ALTER TABLE series_search ADD COLUMN document tsvector;
|
||||||
|
|
||||||
/* allow scenes without dates to be mixed inbetween scenes with dates */
|
/* allow scenes without dates to be mixed inbetween scenes with dates */
|
||||||
ALTER TABLE releases
|
ALTER TABLE releases
|
||||||
ADD COLUMN effective_date timestamptz
|
ADD COLUMN effective_date timestamptz
|
||||||
GENERATED ALWAYS AS (COALESCE(date, created_at)) STORED;
|
GENERATED ALWAYS AS (COALESCE(date, created_at)) STORED;
|
||||||
|
|
||||||
|
ALTER TABLE movies
|
||||||
|
ADD COLUMN effective_date timestamptz
|
||||||
|
GENERATED ALWAYS AS (COALESCE(date, created_at)) STORED;
|
||||||
`);
|
`);
|
||||||
})
|
})
|
||||||
// INDEXES
|
// INDEXES
|
||||||
|
@ -1401,6 +1609,8 @@ exports.up = (knex) => Promise.resolve()
|
||||||
CREATE UNIQUE INDEX movies_search_unique ON movies_search (movie_id);
|
CREATE UNIQUE INDEX movies_search_unique ON movies_search (movie_id);
|
||||||
CREATE INDEX releases_search_index ON releases_search USING GIN (document);
|
CREATE INDEX releases_search_index ON releases_search USING GIN (document);
|
||||||
CREATE INDEX movies_search_index ON movies_search USING GIN (document);
|
CREATE INDEX movies_search_index ON movies_search USING GIN (document);
|
||||||
|
CREATE UNIQUE INDEX series_search_unique ON series_search (serie_id);
|
||||||
|
CREATE INDEX series_search_index ON series_search USING GIN (document);
|
||||||
`);
|
`);
|
||||||
})
|
})
|
||||||
// FUNCTIONS
|
// FUNCTIONS
|
||||||
|
@ -1421,15 +1631,13 @@ exports.up = (knex) => Promise.resolve()
|
||||||
CREATE TABLE movies_search_results (movie_id integer, rank real, FOREIGN KEY (movie_id) REFERENCES movies (id));
|
CREATE TABLE movies_search_results (movie_id integer, rank real, FOREIGN KEY (movie_id) REFERENCES movies (id));
|
||||||
|
|
||||||
CREATE FUNCTION search_releases(query text) RETURNS SETOF releases_search_results AS $$
|
CREATE FUNCTION search_releases(query text) RETURNS SETOF releases_search_results AS $$
|
||||||
SELECT releases.id, ranks.rank FROM (
|
SELECT results.release_id, ts_rank(results.document::tsvector, curate_search_query(query)) as rank
|
||||||
SELECT
|
FROM (
|
||||||
releases_search.release_id,
|
SELECT releases_search.release_id, document
|
||||||
ts_rank(releases_search.document, to_tsquery('english', array_to_string(array(SELECT * FROM regexp_matches(query, '[A-Za-zÀ-ÖØ-öø-ÿ0-9]+', 'g')), '|'))) AS rank
|
|
||||||
FROM releases_search
|
FROM releases_search
|
||||||
) ranks
|
WHERE document::tsvector @@ curate_search_query(query)
|
||||||
LEFT JOIN releases ON releases.id = ranks.release_id
|
) AS results
|
||||||
WHERE ranks.rank > 0
|
ORDER BY rank DESC;
|
||||||
ORDER BY ranks.rank DESC;
|
|
||||||
$$ LANGUAGE SQL STABLE;
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
CREATE FUNCTION search_movies(query text) RETURNS SETOF movies_search_results AS $$
|
CREATE FUNCTION search_movies(query text) RETURNS SETOF movies_search_results AS $$
|
||||||
|
@ -1513,6 +1721,52 @@ exports.up = (knex) => Promise.resolve()
|
||||||
ORDER BY actors.name;
|
ORDER BY actors.name;
|
||||||
$$ LANGUAGE SQL STABLE;
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION entities_scenes(entity entities) RETURNS SETOF releases AS $$
|
||||||
|
WITH RECURSIVE children AS (
|
||||||
|
SELECT entities.id
|
||||||
|
FROM entities
|
||||||
|
WHERE entities.id = entity.id
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
SELECT entities.id
|
||||||
|
FROM entities
|
||||||
|
INNER JOIN children ON children.id = entities.parent_id
|
||||||
|
)
|
||||||
|
|
||||||
|
SELECT releases FROM releases
|
||||||
|
INNER JOIN children ON children.id = releases.entity_id
|
||||||
|
|
||||||
|
UNION
|
||||||
|
|
||||||
|
SELECT releases FROM releases
|
||||||
|
INNER JOIN children ON children.id = releases.studio_id;
|
||||||
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION entities_scene_total(entity entities) RETURNS bigint AS $$
|
||||||
|
SELECT COUNT(id)
|
||||||
|
FROM releases
|
||||||
|
WHERE releases.entity_id = entity.id;
|
||||||
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION entities_scene_tags(entity entities, selectable_tags text[]) RETURNS SETOF tags AS $$
|
||||||
|
SELECT tags.*
|
||||||
|
FROM releases
|
||||||
|
LEFT JOIN
|
||||||
|
releases_tags ON releases_tags.release_id = releases.id
|
||||||
|
LEFT JOIN
|
||||||
|
tags ON tags.id = releases_tags.tag_id
|
||||||
|
WHERE
|
||||||
|
releases.entity_id = entity.id
|
||||||
|
AND
|
||||||
|
CASE WHEN array_length(selectable_tags, 1) IS NOT NULL
|
||||||
|
THEN tags.slug = ANY(selectable_tags)
|
||||||
|
ELSE true
|
||||||
|
END
|
||||||
|
GROUP BY tags.id
|
||||||
|
ORDER BY tags.name;
|
||||||
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
/* GraphQL/Postgraphile 'every' applies to the data, will only include scenes for which every assigned tag is selected,
|
/* GraphQL/Postgraphile 'every' applies to the data, will only include scenes for which every assigned tag is selected,
|
||||||
instead of what we want; scenes with every selected tag, but possibly also some others */
|
instead of what we want; scenes with every selected tag, but possibly also some others */
|
||||||
CREATE FUNCTION actors_scenes(actor actors, selected_tags text[], mode text DEFAULT 'all') RETURNS SETOF releases AS $$
|
CREATE FUNCTION actors_scenes(actor actors, selected_tags text[], mode text DEFAULT 'all') RETURNS SETOF releases AS $$
|
||||||
|
@ -1589,7 +1843,7 @@ exports.up = (knex) => Promise.resolve()
|
||||||
ORDER BY tags.priority DESC
|
ORDER BY tags.priority DESC
|
||||||
$$ LANGUAGE SQL STABLE;
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
CREATE FUNCTION movies_photos(movie movies) RETURNS SETOF media AS $$
|
CREATE FUNCTION movies_scenes_photos(movie movies) RETURNS SETOF media AS $$
|
||||||
SELECT media.*
|
SELECT media.*
|
||||||
FROM movies_scenes
|
FROM movies_scenes
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
|
@ -1607,15 +1861,111 @@ exports.up = (knex) => Promise.resolve()
|
||||||
SELECT EXISTS(SELECT true WHERE (SELECT id FROM batches ORDER BY created_at DESC LIMIT 1) = release.created_batch_id);
|
SELECT EXISTS(SELECT true WHERE (SELECT id FROM batches ORDER BY created_at DESC LIMIT 1) = release.created_batch_id);
|
||||||
$$ LANGUAGE sql STABLE;
|
$$ LANGUAGE sql STABLE;
|
||||||
|
|
||||||
CREATE FUNCTION banners_ratio(banner banners) RETURNS numeric AS $$
|
CREATE FUNCTION series_actors(serie series) RETURNS SETOF actors AS $$
|
||||||
SELECT ROUND(banner.width::decimal / banner.height::decimal, 2);
|
SELECT actors.*
|
||||||
|
FROM series_scenes
|
||||||
|
LEFT JOIN
|
||||||
|
releases ON releases.id = series_scenes.scene_id
|
||||||
|
LEFT JOIN
|
||||||
|
releases_actors ON releases_actors.release_id = releases.id
|
||||||
|
LEFT JOIN
|
||||||
|
actors ON actors.id = releases_actors.actor_id
|
||||||
|
WHERE series_scenes.serie_id = serie.id
|
||||||
|
AND actors.id IS NOT NULL
|
||||||
|
GROUP BY actors.id
|
||||||
|
ORDER BY actors.name, actors.gender
|
||||||
$$ LANGUAGE SQL STABLE;
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
CREATE FUNCTION get_random_campaign() RETURNS SETOF campaigns AS $$
|
CREATE FUNCTION series_tags(serie series) RETURNS SETOF tags AS $$
|
||||||
SELECT * FROM campaigns
|
SELECT tags.*
|
||||||
ORDER BY random()
|
FROM series_scenes
|
||||||
|
LEFT JOIN
|
||||||
|
releases ON releases.id = series_scenes.scene_id
|
||||||
|
LEFT JOIN
|
||||||
|
releases_tags ON releases_tags.release_id = releases.id
|
||||||
|
LEFT JOIN
|
||||||
|
tags ON tags.id = releases_tags.tag_id
|
||||||
|
WHERE series_scenes.serie_id = serie.id
|
||||||
|
AND tags.id IS NOT NULL
|
||||||
|
GROUP BY tags.id
|
||||||
|
ORDER BY tags.priority DESC
|
||||||
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION series_scenes_photos(serie series) RETURNS SETOF media AS $$
|
||||||
|
SELECT media.*
|
||||||
|
FROM series_scenes
|
||||||
|
LEFT JOIN
|
||||||
|
releases ON releases.id = series_scenes.scene_id
|
||||||
|
INNER JOIN
|
||||||
|
releases_photos ON releases_photos.release_id = releases.id
|
||||||
|
LEFT JOIN
|
||||||
|
media ON media.id = releases_photos.media_id
|
||||||
|
WHERE series_scenes.serie_id = serie.id
|
||||||
|
GROUP BY media.id
|
||||||
|
ORDER BY media.index ASC
|
||||||
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION get_random_campaign(min_ratio decimal default 0, max_ratio decimal default 1000.0) RETURNS random_campaign AS $$
|
||||||
|
SELECT * FROM (
|
||||||
|
SELECT DISTINCT ON (CASE WHEN parent_id IS NOT NULL THEN parent_id ELSE entity_id END)
|
||||||
|
id, banner_id, url, entity_id, affiliate_id, parent_id
|
||||||
|
FROM (
|
||||||
|
SELECT
|
||||||
|
campaigns.*, entities.parent_id as parent_id
|
||||||
|
FROM campaigns
|
||||||
|
LEFT JOIN entities ON entities.id = campaigns.entity_id
|
||||||
|
LEFT JOIN banners ON banners.id = campaigns.banner_id
|
||||||
|
WHERE banner_id IS NOT NULL
|
||||||
|
AND ratio >= min_ratio
|
||||||
|
AND ratio <= max_ratio
|
||||||
|
ORDER BY RANDOM()
|
||||||
|
) random_campaigns
|
||||||
|
) random_banners
|
||||||
|
ORDER BY RANDOM()
|
||||||
LIMIT 1;
|
LIMIT 1;
|
||||||
$$ LANGUAGE sql STABLE;
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
`);
|
||||||
|
})
|
||||||
|
// VIEWS AND COMMENTS
|
||||||
|
.then(() => { // eslint-disable-line arrow-body-style
|
||||||
|
// allow vim fold
|
||||||
|
return knex.raw(`
|
||||||
|
CREATE MATERIALIZED VIEW releases_not_showcased AS (
|
||||||
|
SELECT releases.id AS release_id FROM releases
|
||||||
|
LEFT JOIN entities AS channels ON channels.id = releases.entity_id
|
||||||
|
LEFT JOIN entities AS studios ON studios.id = releases.studio_id
|
||||||
|
LEFT JOIN entities AS networks ON networks.id = channels.parent_id
|
||||||
|
WHERE (studios.showcased = false)
|
||||||
|
OR (channels.showcased = false AND studios.showcased IS NOT true)
|
||||||
|
OR (networks.showcased = false AND channels.showcased IS NOT true AND studios.showcased IS NOT true)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX ON releases_not_showcased (release_id);
|
||||||
|
COMMENT ON MATERIALIZED VIEW releases_not_showcased IS E'@foreignKey (release_id) references releases (id)';
|
||||||
|
|
||||||
|
COMMENT ON COLUMN users.password IS E'@omit';
|
||||||
|
COMMENT ON COLUMN users.email IS E'@omit';
|
||||||
|
COMMENT ON COLUMN users.email_verified IS E'@omit';
|
||||||
|
COMMENT ON COLUMN users.abilities IS E'@omit';
|
||||||
|
|
||||||
|
COMMENT ON COLUMN actors.height IS E'@omit read,update,create,delete,all,many';
|
||||||
|
COMMENT ON COLUMN actors.weight IS E'@omit read,update,create,delete,all,many';
|
||||||
|
COMMENT ON COLUMN actors.penis_length IS E'@omit read,update,create,delete,all,many';
|
||||||
|
COMMENT ON COLUMN actors.penis_girth IS E'@omit read,update,create,delete,all,many';
|
||||||
|
|
||||||
|
COMMENT ON FUNCTION entities_scenes IS E'@sortable';
|
||||||
|
|
||||||
|
COMMENT ON FUNCTION actors_tags IS E'@sortable';
|
||||||
|
COMMENT ON FUNCTION actors_channels IS E'@sortable';
|
||||||
|
COMMENT ON FUNCTION actors_actors IS E'@sortable';
|
||||||
|
COMMENT ON FUNCTION actors_scenes IS E'@sortable';
|
||||||
|
COMMENT ON FUNCTION tags_scenes IS E'@sortable';
|
||||||
|
|
||||||
|
COMMENT ON FUNCTION search_releases IS E'@sortable';
|
||||||
|
COMMENT ON FUNCTION search_entities IS E'@sortable';
|
||||||
|
COMMENT ON FUNCTION search_actors IS E'@sortable';
|
||||||
|
COMMENT ON FUNCTION search_movies IS E'@sortable';
|
||||||
|
COMMENT ON FUNCTION search_tags IS E'@sortable';
|
||||||
`);
|
`);
|
||||||
})
|
})
|
||||||
// POLICIES
|
// POLICIES
|
||||||
|
@ -1632,6 +1982,7 @@ exports.up = (knex) => Promise.resolve()
|
||||||
ALTER TABLE stashes_scenes ENABLE ROW LEVEL SECURITY;
|
ALTER TABLE stashes_scenes ENABLE ROW LEVEL SECURITY;
|
||||||
ALTER TABLE stashes_movies ENABLE ROW LEVEL SECURITY;
|
ALTER TABLE stashes_movies ENABLE ROW LEVEL SECURITY;
|
||||||
ALTER TABLE stashes_actors ENABLE ROW LEVEL SECURITY;
|
ALTER TABLE stashes_actors ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE stashes_series ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
CREATE POLICY stashes_policy_select ON stashes FOR SELECT USING (stashes.public OR stashes.user_id = current_user_id());
|
CREATE POLICY stashes_policy_select ON stashes FOR SELECT USING (stashes.public OR stashes.user_id = current_user_id());
|
||||||
CREATE POLICY stashes_policy_update ON stashes FOR UPDATE USING (stashes.public OR stashes.user_id = current_user_id());
|
CREATE POLICY stashes_policy_update ON stashes FOR UPDATE USING (stashes.public OR stashes.user_id = current_user_id());
|
||||||
|
@ -1662,6 +2013,14 @@ exports.up = (knex) => Promise.resolve()
|
||||||
AND (stashes.user_id = current_user_id() OR stashes.public)
|
AND (stashes.user_id = current_user_id() OR stashes.public)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
CREATE POLICY stashes_policy ON stashes_series
|
||||||
|
USING (EXISTS (
|
||||||
|
SELECT *
|
||||||
|
FROM stashes
|
||||||
|
WHERE stashes.id = stashes_series.stash_id
|
||||||
|
AND (stashes.user_id = current_user_id() OR stashes.public)
|
||||||
|
));
|
||||||
|
|
||||||
ALTER TABLE alerts ENABLE ROW LEVEL SECURITY;
|
ALTER TABLE alerts ENABLE ROW LEVEL SECURITY;
|
||||||
ALTER TABLE alerts_tags ENABLE ROW LEVEL SECURITY;
|
ALTER TABLE alerts_tags ENABLE ROW LEVEL SECURITY;
|
||||||
ALTER TABLE alerts_scenes ENABLE ROW LEVEL SECURITY;
|
ALTER TABLE alerts_scenes ENABLE ROW LEVEL SECURITY;
|
||||||
|
@ -1729,33 +2088,6 @@ exports.up = (knex) => Promise.resolve()
|
||||||
`, {
|
`, {
|
||||||
visitor: knex.raw(config.database.query.user),
|
visitor: knex.raw(config.database.query.user),
|
||||||
});
|
});
|
||||||
})
|
|
||||||
// VIEWS AND COMMENTS
|
|
||||||
.then(() => { // eslint-disable-line arrow-body-style
|
|
||||||
// allow vim fold
|
|
||||||
return knex.raw(`
|
|
||||||
COMMENT ON COLUMN users.password IS E'@omit';
|
|
||||||
COMMENT ON COLUMN users.email IS E'@omit';
|
|
||||||
COMMENT ON COLUMN users.email_verified IS E'@omit';
|
|
||||||
COMMENT ON COLUMN users.abilities IS E'@omit';
|
|
||||||
|
|
||||||
COMMENT ON COLUMN actors.height IS E'@omit read,update,create,delete,all,many';
|
|
||||||
COMMENT ON COLUMN actors.weight IS E'@omit read,update,create,delete,all,many';
|
|
||||||
COMMENT ON COLUMN actors.penis_length IS E'@omit read,update,create,delete,all,many';
|
|
||||||
COMMENT ON COLUMN actors.penis_girth IS E'@omit read,update,create,delete,all,many';
|
|
||||||
|
|
||||||
COMMENT ON FUNCTION actors_tags IS E'@sortable';
|
|
||||||
COMMENT ON FUNCTION actors_channels IS E'@sortable';
|
|
||||||
COMMENT ON FUNCTION actors_actors IS E'@sortable';
|
|
||||||
COMMENT ON FUNCTION actors_scenes IS E'@sortable';
|
|
||||||
COMMENT ON FUNCTION tags_scenes IS E'@sortable';
|
|
||||||
|
|
||||||
COMMENT ON FUNCTION search_releases IS E'@sortable';
|
|
||||||
COMMENT ON FUNCTION search_entities IS E'@sortable';
|
|
||||||
COMMENT ON FUNCTION search_actors IS E'@sortable';
|
|
||||||
COMMENT ON FUNCTION search_movies IS E'@sortable';
|
|
||||||
COMMENT ON FUNCTION search_tags IS E'@sortable';
|
|
||||||
`);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
exports.down = (knex) => { // eslint-disable-line arrow-body-style
|
exports.down = (knex) => { // eslint-disable-line arrow-body-style
|
||||||
|
@ -1776,8 +2108,17 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
|
||||||
DROP TABLE IF EXISTS movies_scenes CASCADE;
|
DROP TABLE IF EXISTS movies_scenes CASCADE;
|
||||||
DROP TABLE IF EXISTS movies_covers CASCADE;
|
DROP TABLE IF EXISTS movies_covers CASCADE;
|
||||||
DROP TABLE IF EXISTS movies_posters CASCADE;
|
DROP TABLE IF EXISTS movies_posters CASCADE;
|
||||||
|
DROP TABLE IF EXISTS movies_photos CASCADE;
|
||||||
DROP TABLE IF EXISTS movies_trailers CASCADE;
|
DROP TABLE IF EXISTS movies_trailers CASCADE;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS stashes_series CASCADE;
|
||||||
|
DROP TABLE IF EXISTS series_scenes CASCADE;
|
||||||
|
DROP TABLE IF EXISTS series_trailers CASCADE;
|
||||||
|
DROP TABLE IF EXISTS series_posters CASCADE;
|
||||||
|
DROP TABLE IF EXISTS series_covers CASCADE;
|
||||||
|
DROP TABLE IF EXISTS series_photos CASCADE;
|
||||||
|
DROP TABLE IF EXISTS series_search CASCADE;
|
||||||
|
|
||||||
DROP TABLE IF EXISTS clips_tags CASCADE;
|
DROP TABLE IF EXISTS clips_tags CASCADE;
|
||||||
DROP TABLE IF EXISTS clips_posters CASCADE;
|
DROP TABLE IF EXISTS clips_posters CASCADE;
|
||||||
DROP TABLE IF EXISTS clips_photos CASCADE;
|
DROP TABLE IF EXISTS clips_photos CASCADE;
|
||||||
|
@ -1789,6 +2130,7 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
|
||||||
DROP TABLE IF EXISTS banners_tags CASCADE;
|
DROP TABLE IF EXISTS banners_tags CASCADE;
|
||||||
DROP TABLE IF EXISTS banners CASCADE;
|
DROP TABLE IF EXISTS banners CASCADE;
|
||||||
DROP TABLE IF EXISTS campaigns CASCADE;
|
DROP TABLE IF EXISTS campaigns CASCADE;
|
||||||
|
DROP TABLE IF EXISTS random_campaign CASCADE;
|
||||||
DROP TABLE IF EXISTS affiliates CASCADE;
|
DROP TABLE IF EXISTS affiliates CASCADE;
|
||||||
|
|
||||||
DROP TABLE IF EXISTS batches CASCADE;
|
DROP TABLE IF EXISTS batches CASCADE;
|
||||||
|
@ -1812,6 +2154,7 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
|
||||||
DROP TABLE IF EXISTS movies CASCADE;
|
DROP TABLE IF EXISTS movies CASCADE;
|
||||||
DROP TABLE IF EXISTS clips CASCADE;
|
DROP TABLE IF EXISTS clips CASCADE;
|
||||||
DROP TABLE IF EXISTS chapters CASCADE;
|
DROP TABLE IF EXISTS chapters CASCADE;
|
||||||
|
DROP TABLE IF EXISTS series CASCADE;
|
||||||
DROP TABLE IF EXISTS releases CASCADE;
|
DROP TABLE IF EXISTS releases CASCADE;
|
||||||
DROP TABLE IF EXISTS actors CASCADE;
|
DROP TABLE IF EXISTS actors CASCADE;
|
||||||
DROP TABLE IF EXISTS tags CASCADE;
|
DROP TABLE IF EXISTS tags CASCADE;
|
||||||
|
@ -1853,6 +2196,9 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
|
||||||
DROP FUNCTION IF EXISTS get_random_sfw_media_id;
|
DROP FUNCTION IF EXISTS get_random_sfw_media_id;
|
||||||
|
|
||||||
DROP FUNCTION IF EXISTS releases_is_new;
|
DROP FUNCTION IF EXISTS releases_is_new;
|
||||||
|
DROP FUNCTION IF EXISTS entities_scenes;
|
||||||
|
DROP FUNCTION IF EXISTS entities_scene_total;
|
||||||
|
DROP FUNCTION IF EXISTS entities_scene_tags;
|
||||||
DROP FUNCTION IF EXISTS actors_tags;
|
DROP FUNCTION IF EXISTS actors_tags;
|
||||||
DROP FUNCTION IF EXISTS actors_channels;
|
DROP FUNCTION IF EXISTS actors_channels;
|
||||||
DROP FUNCTION IF EXISTS actors_actors;
|
DROP FUNCTION IF EXISTS actors_actors;
|
||||||
|
@ -1860,7 +2206,11 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
|
||||||
|
|
||||||
DROP FUNCTION IF EXISTS movies_actors;
|
DROP FUNCTION IF EXISTS movies_actors;
|
||||||
DROP FUNCTION IF EXISTS movies_tags;
|
DROP FUNCTION IF EXISTS movies_tags;
|
||||||
DROP FUNCTION IF EXISTS movies_photos;
|
DROP FUNCTION IF EXISTS movies_scenes_photos;
|
||||||
|
|
||||||
|
DROP FUNCTION IF EXISTS series_actors;
|
||||||
|
DROP FUNCTION IF EXISTS series_tags;
|
||||||
|
DROP FUNCTION IF EXISTS series_scenes_photos;
|
||||||
|
|
||||||
DROP POLICY IF EXISTS stashes_policy ON stashes;
|
DROP POLICY IF EXISTS stashes_policy ON stashes;
|
||||||
DROP POLICY IF EXISTS stashes_policy ON stashes_scenes;
|
DROP POLICY IF EXISTS stashes_policy ON stashes_scenes;
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
exports.up = async (knex) => knex.raw(`
|
|
||||||
CREATE FUNCTION entities_scenes(entity entities) RETURNS SETOF releases AS $$
|
|
||||||
WITH RECURSIVE children AS (
|
|
||||||
SELECT entities.id
|
|
||||||
FROM entities
|
|
||||||
WHERE entities.id = entity.id
|
|
||||||
|
|
||||||
UNION ALL
|
|
||||||
|
|
||||||
SELECT entities.id
|
|
||||||
FROM entities
|
|
||||||
INNER JOIN children ON children.id = entities.parent_id
|
|
||||||
)
|
|
||||||
|
|
||||||
SELECT releases FROM releases
|
|
||||||
INNER JOIN children ON children.id = releases.entity_id
|
|
||||||
|
|
||||||
UNION
|
|
||||||
|
|
||||||
SELECT releases FROM releases
|
|
||||||
INNER JOIN children ON children.id = releases.studio_id;
|
|
||||||
$$ LANGUAGE SQL STABLE;
|
|
||||||
|
|
||||||
COMMENT ON FUNCTION entities_scenes IS E'@sortable';
|
|
||||||
`);
|
|
||||||
|
|
||||||
exports.down = async (knex) => knex.raw(`
|
|
||||||
DROP FUNCTION IF EXISTS entities_scenes;
|
|
||||||
`);
|
|
|
@ -1,15 +0,0 @@
|
||||||
exports.up = async (knex) => Promise.resolve()
|
|
||||||
.then(() => knex.schema.alterTable('releases_tags', (table) => {
|
|
||||||
table.index('release_id');
|
|
||||||
}))
|
|
||||||
.then(() => knex.schema.alterTable('movies_scenes', (table) => {
|
|
||||||
table.index('scene_id');
|
|
||||||
}));
|
|
||||||
|
|
||||||
exports.down = async (knex) => Promise.resolve()
|
|
||||||
.then(() => knex.schema.alterTable('releases_tags', (table) => {
|
|
||||||
table.dropIndex('release_id');
|
|
||||||
}))
|
|
||||||
.then(() => knex.schema.alterTable('movies_scenes', (table) => {
|
|
||||||
table.dropIndex('scene_id');
|
|
||||||
}));
|
|
|
@ -1,11 +0,0 @@
|
||||||
exports.up = async (knex) => knex.raw(`
|
|
||||||
CREATE OR REPLACE FUNCTION entities_scene_total(entity entities) RETURNS bigint AS $$
|
|
||||||
SELECT COUNT(id)
|
|
||||||
FROM releases
|
|
||||||
WHERE releases.entity_id = entity.id;
|
|
||||||
$$ LANGUAGE SQL STABLE;
|
|
||||||
`);
|
|
||||||
|
|
||||||
exports.down = async (knex) => knex.raw(`
|
|
||||||
DROP FUNCTION IF EXISTS entities_scene_total;
|
|
||||||
`);
|
|
|
@ -1,23 +0,0 @@
|
||||||
exports.up = async (knex) => knex.raw(`
|
|
||||||
CREATE FUNCTION entities_scene_tags(entity entities, selectable_tags text[]) RETURNS SETOF tags AS $$
|
|
||||||
SELECT tags.*
|
|
||||||
FROM releases
|
|
||||||
LEFT JOIN
|
|
||||||
releases_tags ON releases_tags.release_id = releases.id
|
|
||||||
LEFT JOIN
|
|
||||||
tags ON tags.id = releases_tags.tag_id
|
|
||||||
WHERE
|
|
||||||
releases.entity_id = entity.id
|
|
||||||
AND
|
|
||||||
CASE WHEN array_length(selectable_tags, 1) IS NOT NULL
|
|
||||||
THEN tags.slug = ANY(selectable_tags)
|
|
||||||
ELSE true
|
|
||||||
END
|
|
||||||
GROUP BY tags.id
|
|
||||||
ORDER BY tags.name;
|
|
||||||
$$ LANGUAGE SQL STABLE;
|
|
||||||
`);
|
|
||||||
|
|
||||||
exports.down = async (knex) => knex.raw(`
|
|
||||||
DROP FUNCTION IF EXISTS entities_scene_tags;
|
|
||||||
`);
|
|
|
@ -1,215 +0,0 @@
|
||||||
const config = require('config');
|
|
||||||
|
|
||||||
exports.up = async (knex) => Promise.resolve()
|
|
||||||
.then(() => knex.schema.createTable('series', (table) => {
|
|
||||||
table.increments('id', 16);
|
|
||||||
|
|
||||||
table.integer('entity_id', 12)
|
|
||||||
.references('id')
|
|
||||||
.inTable('entities')
|
|
||||||
.notNullable();
|
|
||||||
|
|
||||||
table.integer('studio_id', 12)
|
|
||||||
.references('id')
|
|
||||||
.inTable('entities');
|
|
||||||
|
|
||||||
table.text('entry_id');
|
|
||||||
table.unique(['entity_id', 'entry_id']);
|
|
||||||
|
|
||||||
table.text('url', 1000);
|
|
||||||
table.text('title');
|
|
||||||
table.text('slug');
|
|
||||||
|
|
||||||
table.timestamp('date');
|
|
||||||
table.index('date');
|
|
||||||
|
|
||||||
table.enum('date_precision', ['year', 'month', 'week', 'day', 'hour', 'minute', 'second'])
|
|
||||||
.defaultTo('day');
|
|
||||||
|
|
||||||
table.text('description');
|
|
||||||
|
|
||||||
table.boolean('deep');
|
|
||||||
table.text('deep_url', 1000);
|
|
||||||
|
|
||||||
table.text('comment');
|
|
||||||
|
|
||||||
table.integer('created_batch_id', 12)
|
|
||||||
.references('id')
|
|
||||||
.inTable('batches')
|
|
||||||
.onDelete('cascade');
|
|
||||||
|
|
||||||
table.integer('updated_batch_id', 12)
|
|
||||||
.references('id')
|
|
||||||
.inTable('batches')
|
|
||||||
.onDelete('cascade');
|
|
||||||
|
|
||||||
table.datetime('created_at')
|
|
||||||
.defaultTo(knex.fn.now());
|
|
||||||
}))
|
|
||||||
.then(() => knex.schema.createTable('series_scenes', (table) => {
|
|
||||||
table.integer('serie_id', 16)
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('series')
|
|
||||||
.onDelete('cascade');
|
|
||||||
|
|
||||||
table.integer('scene_id', 16)
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('releases')
|
|
||||||
.onDelete('cascade');
|
|
||||||
|
|
||||||
table.unique(['serie_id', 'scene_id']);
|
|
||||||
|
|
||||||
table.datetime('created_at')
|
|
||||||
.defaultTo(knex.fn.now());
|
|
||||||
}))
|
|
||||||
.then(() => knex.schema.createTable('series_trailers', (table) => {
|
|
||||||
table.integer('serie_id', 16)
|
|
||||||
.unique()
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('series')
|
|
||||||
.onDelete('cascade');
|
|
||||||
|
|
||||||
table.text('media_id', 21)
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('media');
|
|
||||||
}))
|
|
||||||
.then(() => knex.schema.createTable('series_posters', (table) => {
|
|
||||||
table.integer('serie_id', 16)
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('series')
|
|
||||||
.onDelete('cascade');
|
|
||||||
|
|
||||||
table.text('media_id', 21)
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('media')
|
|
||||||
.onDelete('cascade');
|
|
||||||
|
|
||||||
table.unique('serie_id');
|
|
||||||
}))
|
|
||||||
.then(() => knex.schema.createTable('series_covers', (table) => {
|
|
||||||
table.integer('serie_id', 16)
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('series')
|
|
||||||
.onDelete('cascade');
|
|
||||||
|
|
||||||
table.text('media_id', 21)
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('media');
|
|
||||||
|
|
||||||
table.unique(['serie_id', 'media_id']);
|
|
||||||
}))
|
|
||||||
.then(() => knex.schema.createTable('series_search', (table) => {
|
|
||||||
table.integer('serie_id', 16)
|
|
||||||
.references('id')
|
|
||||||
.inTable('series')
|
|
||||||
.onDelete('cascade');
|
|
||||||
}))
|
|
||||||
.then(() => knex.schema.createTable('stashes_series', (table) => {
|
|
||||||
table.integer('stash_id')
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('stashes')
|
|
||||||
.onDelete('cascade');
|
|
||||||
|
|
||||||
table.integer('serie_id')
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('series')
|
|
||||||
.onDelete('cascade');
|
|
||||||
|
|
||||||
table.unique(['stash_id', 'serie_id']);
|
|
||||||
|
|
||||||
table.string('comment');
|
|
||||||
|
|
||||||
table.datetime('created_at')
|
|
||||||
.notNullable()
|
|
||||||
.defaultTo(knex.fn.now());
|
|
||||||
}))
|
|
||||||
.then(() => knex.raw(`
|
|
||||||
ALTER TABLE series_search ADD COLUMN document tsvector;
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX series_search_unique ON series_search (serie_id);
|
|
||||||
CREATE INDEX series_search_index ON series_search USING GIN (document);
|
|
||||||
|
|
||||||
CREATE FUNCTION series_actors(serie series) RETURNS SETOF actors AS $$
|
|
||||||
SELECT actors.*
|
|
||||||
FROM series_scenes
|
|
||||||
LEFT JOIN
|
|
||||||
releases ON releases.id = series_scenes.scene_id
|
|
||||||
LEFT JOIN
|
|
||||||
releases_actors ON releases_actors.release_id = releases.id
|
|
||||||
LEFT JOIN
|
|
||||||
actors ON actors.id = releases_actors.actor_id
|
|
||||||
WHERE series_scenes.serie_id = serie.id
|
|
||||||
AND actors.id IS NOT NULL
|
|
||||||
GROUP BY actors.id
|
|
||||||
ORDER BY actors.name, actors.gender
|
|
||||||
$$ LANGUAGE SQL STABLE;
|
|
||||||
|
|
||||||
CREATE FUNCTION series_tags(serie series) RETURNS SETOF tags AS $$
|
|
||||||
SELECT tags.*
|
|
||||||
FROM series_scenes
|
|
||||||
LEFT JOIN
|
|
||||||
releases ON releases.id = series_scenes.scene_id
|
|
||||||
LEFT JOIN
|
|
||||||
releases_tags ON releases_tags.release_id = releases.id
|
|
||||||
LEFT JOIN
|
|
||||||
tags ON tags.id = releases_tags.tag_id
|
|
||||||
WHERE series_scenes.serie_id = serie.id
|
|
||||||
AND tags.id IS NOT NULL
|
|
||||||
GROUP BY tags.id
|
|
||||||
ORDER BY tags.priority DESC
|
|
||||||
$$ LANGUAGE SQL STABLE;
|
|
||||||
|
|
||||||
CREATE FUNCTION series_photos(serie series) RETURNS SETOF media AS $$
|
|
||||||
SELECT media.*
|
|
||||||
FROM series_scenes
|
|
||||||
LEFT JOIN
|
|
||||||
releases ON releases.id = series_scenes.scene_id
|
|
||||||
INNER JOIN
|
|
||||||
releases_photos ON releases_photos.release_id = releases.id
|
|
||||||
LEFT JOIN
|
|
||||||
media ON media.id = releases_photos.media_id
|
|
||||||
WHERE series_scenes.serie_id = serie.id
|
|
||||||
GROUP BY media.id
|
|
||||||
ORDER BY media.index ASC
|
|
||||||
$$ LANGUAGE SQL STABLE;
|
|
||||||
|
|
||||||
GRANT ALL ON ALL TABLES IN SCHEMA public TO :visitor;
|
|
||||||
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO :visitor;
|
|
||||||
|
|
||||||
ALTER TABLE stashes_series ENABLE ROW LEVEL SECURITY;
|
|
||||||
|
|
||||||
CREATE POLICY stashes_policy ON stashes_series
|
|
||||||
USING (EXISTS (
|
|
||||||
SELECT *
|
|
||||||
FROM stashes
|
|
||||||
WHERE stashes.id = stashes_series.stash_id
|
|
||||||
AND (stashes.user_id = current_user_id() OR stashes.public)
|
|
||||||
));
|
|
||||||
`, {
|
|
||||||
visitor: knex.raw(config.database.query.user),
|
|
||||||
}));
|
|
||||||
|
|
||||||
exports.down = async (knex) => Promise.resolve()
|
|
||||||
.then(() => knex.raw(`
|
|
||||||
DROP FUNCTION IF EXISTS series_actors;
|
|
||||||
DROP FUNCTION IF EXISTS series_tags;
|
|
||||||
DROP FUNCTION IF EXISTS series_photos;
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS stashes_series CASCADE;
|
|
||||||
DROP TABLE IF EXISTS series_scenes CASCADE;
|
|
||||||
DROP TABLE IF EXISTS series_trailers CASCADE;
|
|
||||||
DROP TABLE IF EXISTS series_posters CASCADE;
|
|
||||||
DROP TABLE IF EXISTS series_covers CASCADE;
|
|
||||||
DROP TABLE IF EXISTS series_search CASCADE;
|
|
||||||
DROP TABLE IF EXISTS series CASCADE;
|
|
||||||
`));
|
|
|
@ -1,49 +0,0 @@
|
||||||
const config = require('config');
|
|
||||||
|
|
||||||
exports.up = async (knex) => Promise.resolve()
|
|
||||||
.then(() => knex.raw(`
|
|
||||||
ALTER FUNCTION movies_photos(movie movies) RENAME TO movies_scenes_photos;
|
|
||||||
ALTER FUNCTION series_photos(serie series) RENAME TO series_scenes_photos;
|
|
||||||
`))
|
|
||||||
.then(() => knex.schema.createTable('movies_photos', (table) => {
|
|
||||||
table.integer('movie_id', 16)
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('movies')
|
|
||||||
.onDelete('cascade');
|
|
||||||
|
|
||||||
table.text('media_id', 21)
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('media');
|
|
||||||
|
|
||||||
table.unique(['movie_id', 'media_id']);
|
|
||||||
}))
|
|
||||||
.then(() => knex.schema.createTable('series_photos', (table) => {
|
|
||||||
table.integer('serie_id', 16)
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('series')
|
|
||||||
.onDelete('cascade');
|
|
||||||
|
|
||||||
table.text('media_id', 21)
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('media');
|
|
||||||
|
|
||||||
table.unique(['serie_id', 'media_id']);
|
|
||||||
}))
|
|
||||||
.then(() => knex.raw(`
|
|
||||||
GRANT ALL ON ALL TABLES IN SCHEMA public TO :visitor;
|
|
||||||
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO :visitor;
|
|
||||||
`, {
|
|
||||||
visitor: knex.raw(config.database.query.user),
|
|
||||||
}));
|
|
||||||
|
|
||||||
exports.down = async (knex) => knex.raw(`
|
|
||||||
DROP TABLE IF EXISTS movies_photos CASCADE;
|
|
||||||
DROP TABLE IF EXISTS series_photos CASCADE;
|
|
||||||
|
|
||||||
ALTER FUNCTION movies_scenes_photos(movie movies) RENAME TO movies_photos;
|
|
||||||
ALTER FUNCTION series_scenes_photos(serie series) RENAME TO series_photos;
|
|
||||||
`);
|
|
|
@ -1,7 +0,0 @@
|
||||||
exports.up = async (knex) => knex.schema.alterTable('releases', (table) => {
|
|
||||||
table.specificType('qualities', 'text[]');
|
|
||||||
});
|
|
||||||
|
|
||||||
exports.down = async (knex) => knex.schema.alterTable('releases', (table) => {
|
|
||||||
table.dropColumn('qualities');
|
|
||||||
});
|
|
|
@ -1,7 +0,0 @@
|
||||||
exports.up = async (knex) => knex.schema.alterTable('users', (table) => {
|
|
||||||
table.datetime('last_login');
|
|
||||||
});
|
|
||||||
|
|
||||||
exports.down = async (knex) => knex.schema.alterTable('users', (table) => {
|
|
||||||
table.dropColumn('last_login');
|
|
||||||
});
|
|
|
@ -1,58 +0,0 @@
|
||||||
exports.up = async (knex) => Promise.resolve()
|
|
||||||
.then(() => knex.schema.createTable('random_campaign', (table) => {
|
|
||||||
table.text('banner_id')
|
|
||||||
.references('id')
|
|
||||||
.inTable('banners');
|
|
||||||
|
|
||||||
table.text('url');
|
|
||||||
|
|
||||||
table.integer('entity_id')
|
|
||||||
.references('id')
|
|
||||||
.inTable('entities');
|
|
||||||
|
|
||||||
table.string('affiliate_id')
|
|
||||||
.references('id')
|
|
||||||
.inTable('affiliates');
|
|
||||||
|
|
||||||
table.integer('parent_id')
|
|
||||||
.references('id')
|
|
||||||
.inTable('entities');
|
|
||||||
}))
|
|
||||||
.then(() => knex.raw(`
|
|
||||||
ALTER TABLE banners ADD COLUMN ratio numeric GENERATED ALWAYS AS (ROUND(width::decimal/ height::decimal, 2)) STORED;
|
|
||||||
`))
|
|
||||||
.then(() => knex.raw(`
|
|
||||||
DROP FUNCTION IF EXISTS get_random_campaign;
|
|
||||||
DROP FUNCTION IF EXISTS banners_ratio;
|
|
||||||
|
|
||||||
CREATE FUNCTION get_random_campaign(min_ratio decimal default 0, max_ratio decimal default 1000.0) RETURNS random_campaign AS $$
|
|
||||||
SELECT * FROM (
|
|
||||||
SELECT DISTINCT ON (CASE WHEN parent_id IS NOT NULL THEN parent_id ELSE entity_id END)
|
|
||||||
banner_id, url, entity_id, affiliate_id, parent_id
|
|
||||||
FROM (
|
|
||||||
SELECT
|
|
||||||
campaigns.*, entities.parent_id as parent_id
|
|
||||||
FROM campaigns
|
|
||||||
LEFT JOIN entities ON entities.id = campaigns.entity_id
|
|
||||||
LEFT JOIN banners ON banners.id = campaigns.banner_id
|
|
||||||
WHERE banner_id IS NOT NULL
|
|
||||||
AND ratio >= min_ratio
|
|
||||||
AND ratio <= max_ratio
|
|
||||||
ORDER BY RANDOM()
|
|
||||||
) random_campaigns
|
|
||||||
) random_banners
|
|
||||||
ORDER BY RANDOM()
|
|
||||||
LIMIT 1;
|
|
||||||
$$ LANGUAGE SQL STABLE;
|
|
||||||
`));
|
|
||||||
|
|
||||||
exports.down = async (knex) => knex.raw(`
|
|
||||||
DROP FUNCTION IF EXISTS get_random_campaign;
|
|
||||||
DROP TABLE IF EXISTS random_campaign;
|
|
||||||
|
|
||||||
ALTER TABLE banners DROP COLUMN ratio;
|
|
||||||
|
|
||||||
CREATE FUNCTION banners_ratio(banner banners) RETURNS numeric AS $$
|
|
||||||
SELECT ROUND(banner.width::decimal / banner.height::decimal, 2);
|
|
||||||
$$ LANGUAGE SQL STABLE;
|
|
||||||
`);
|
|
|
@ -1,8 +0,0 @@
|
||||||
exports.up = async (knex) => knex.schema.alterTable('entities', (table) => {
|
|
||||||
table.boolean('showcased')
|
|
||||||
.defaultTo(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
exports.down = async (knex) => knex.schema.alterTable('entities', (table) => {
|
|
||||||
table.dropColumn('showcased');
|
|
||||||
});
|
|
|
@ -1,20 +0,0 @@
|
||||||
const config = require('config');
|
|
||||||
|
|
||||||
exports.up = async (knex) => knex.raw(`
|
|
||||||
CREATE VIEW releases_not_showcased AS (
|
|
||||||
SELECT releases.id AS release_id FROM releases
|
|
||||||
LEFT JOIN entities ON entities.id = releases.entity_id
|
|
||||||
LEFT JOIN entities AS studios ON studios.id = releases.studio_id
|
|
||||||
WHERE entities.showcased = false
|
|
||||||
OR studios.showcased = false
|
|
||||||
);
|
|
||||||
|
|
||||||
COMMENT ON VIEW releases_not_showcased IS E'@foreignKey (release_id) references releases (id)';
|
|
||||||
GRANT SELECT ON releases_not_showcased TO :visitor;
|
|
||||||
`, {
|
|
||||||
visitor: knex.raw(config.database.query.user),
|
|
||||||
});
|
|
||||||
|
|
||||||
exports.down = async (knex) => knex.raw(`
|
|
||||||
DROP VIEW IF EXISTS releases_not_showcased;
|
|
||||||
`);
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
exports.up = async (knex) => {
|
||||||
|
await knex.schema.alterTable('random_campaign', (table) => {
|
||||||
|
table.integer('id')
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('campaigns');
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.raw(`
|
||||||
|
CREATE OR REPLACE FUNCTION get_random_campaign(min_ratio decimal default 0, max_ratio decimal default 1000.0) RETURNS random_campaign AS $$
|
||||||
|
SELECT * FROM (
|
||||||
|
SELECT DISTINCT ON (CASE WHEN parent_id IS NOT NULL THEN parent_id ELSE entity_id END)
|
||||||
|
banner_id, url, entity_id, affiliate_id, parent_id, id
|
||||||
|
FROM (
|
||||||
|
SELECT
|
||||||
|
campaigns.*, entities.parent_id as parent_id
|
||||||
|
FROM campaigns
|
||||||
|
LEFT JOIN entities ON entities.id = campaigns.entity_id
|
||||||
|
LEFT JOIN banners ON banners.id = campaigns.banner_id
|
||||||
|
WHERE banner_id IS NOT NULL
|
||||||
|
AND ratio >= min_ratio
|
||||||
|
AND ratio <= max_ratio
|
||||||
|
ORDER BY RANDOM()
|
||||||
|
) random_campaigns
|
||||||
|
) random_banners
|
||||||
|
ORDER BY RANDOM()
|
||||||
|
LIMIT 1;
|
||||||
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
`);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = async (knex) => {
|
||||||
|
await knex.schema.alterTable('random_campaign', (table) => {
|
||||||
|
table.dropColumn('campaign_id');
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.raw(`
|
||||||
|
CREATE OR REPLACE FUNCTION get_random_campaign(min_ratio decimal default 0, max_ratio decimal default 1000.0) RETURNS random_campaign AS $$
|
||||||
|
SELECT * FROM (
|
||||||
|
SELECT DISTINCT ON (CASE WHEN parent_id IS NOT NULL THEN parent_id ELSE entity_id END)
|
||||||
|
banner_id, url, entity_id, affiliate_id, parent_id
|
||||||
|
FROM (
|
||||||
|
SELECT
|
||||||
|
campaigns.*, entities.parent_id as parent_id
|
||||||
|
FROM campaigns
|
||||||
|
LEFT JOIN entities ON entities.id = campaigns.entity_id
|
||||||
|
LEFT JOIN banners ON banners.id = campaigns.banner_id
|
||||||
|
WHERE banner_id IS NOT NULL
|
||||||
|
AND ratio >= min_ratio
|
||||||
|
AND ratio <= max_ratio
|
||||||
|
ORDER BY RANDOM()
|
||||||
|
) random_campaigns
|
||||||
|
) random_banners
|
||||||
|
ORDER BY RANDOM()
|
||||||
|
LIMIT 1;
|
||||||
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
`);
|
||||||
|
};
|
|
@ -0,0 +1,47 @@
|
||||||
|
const config = require('config');
|
||||||
|
|
||||||
|
exports.up = async (knex) => {
|
||||||
|
await knex.raw(`
|
||||||
|
CREATE MATERIALIZED VIEW releases_summaries AS (
|
||||||
|
SELECT
|
||||||
|
releases.id as release_id,
|
||||||
|
channels.slug as channel_slug,
|
||||||
|
channels.type as channel_type,
|
||||||
|
networks.slug as network_slug,
|
||||||
|
networks.type as network_type,
|
||||||
|
parent_networks.slug as parent_network_slug,
|
||||||
|
parent_networks.type as parent_network_type,
|
||||||
|
studios.showcased IS NOT false
|
||||||
|
AND (channels.showcased IS NOT false OR COALESCE(studios.showcased, false) = true)
|
||||||
|
AND (networks.showcased IS NOT false OR COALESCE(channels.showcased, false) = true OR COALESCE(studios.showcased, false) = true)
|
||||||
|
AS showcased,
|
||||||
|
batches.showcased AS batch_showcased,
|
||||||
|
releases.effective_date,
|
||||||
|
releases.created_at,
|
||||||
|
array_agg(tags.slug ORDER BY tags.priority DESC) FILTER (WHERE tags.slug IS NOT NULL) AS tags
|
||||||
|
FROM releases
|
||||||
|
LEFT JOIN releases_tags ON releases_tags.release_id = releases.id
|
||||||
|
LEFT JOIN tags ON tags.id = releases_tags.tag_id
|
||||||
|
LEFT JOIN entities AS channels ON channels.id = releases.entity_id
|
||||||
|
LEFT JOIN entities AS studios ON studios.id = releases.studio_id
|
||||||
|
LEFT JOIN entities AS networks ON networks.id = channels.parent_id
|
||||||
|
LEFT JOIN entities AS parent_networks ON parent_networks.id = networks.parent_id
|
||||||
|
LEFT JOIN batches ON batches.id = releases.updated_batch_id
|
||||||
|
GROUP BY releases.id, studios.showcased, batches.showcased,
|
||||||
|
channels.showcased, channels.slug, channels.type,
|
||||||
|
networks.showcased, networks.slug, networks.type,
|
||||||
|
parent_networks.slug, parent_networks.type
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON MATERIALIZED VIEW releases_summaries IS E'@foreignKey (release_id) references releases (id)';
|
||||||
|
GRANT ALL ON releases_summaries TO :visitor;
|
||||||
|
`, {
|
||||||
|
visitor: knex.raw(config.database.query.user),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = async (knex) => {
|
||||||
|
await knex.raw(`
|
||||||
|
DROP MATERIALIZED VIEW IF EXISTS releases_summaries;
|
||||||
|
`);
|
||||||
|
};
|
|
@ -0,0 +1,27 @@
|
||||||
|
exports.up = async (knex) => {
|
||||||
|
await knex.schema.alterTable('actors_social', (table) => {
|
||||||
|
table.integer('profile_id')
|
||||||
|
.references('id')
|
||||||
|
.inTable('actors_profiles');
|
||||||
|
|
||||||
|
table.dropUnique(['url', 'actor_id']);
|
||||||
|
table.unique(['url', 'actor_id', 'profile_id']);
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.raw(`
|
||||||
|
CREATE UNIQUE INDEX actors_social_url_actor_id_null_unique ON actors_social (url, actor_id) WHERE profile_id IS NULL;
|
||||||
|
`);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = async (knex) => {
|
||||||
|
await knex.raw(`
|
||||||
|
DROP INDEX actors_social_url_actor_id_null_unique;
|
||||||
|
`);
|
||||||
|
|
||||||
|
await knex.schema.alterTable('actors_social', (table) => {
|
||||||
|
table.dropUnique(['url', 'actor_id', 'profile_id']);
|
||||||
|
table.unique(['url', 'actor_id']);
|
||||||
|
|
||||||
|
table.dropColumn('profile_id');
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,24 @@
|
||||||
|
const config = require('config');
|
||||||
|
|
||||||
|
exports.up = async (knex) => {
|
||||||
|
await knex.schema.createTable('releases_caps', (table) => {
|
||||||
|
table.integer('release_id')
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('releases')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.text('media_id')
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('media');
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.raw('GRANT ALL ON releases_caps TO :visitor;', {
|
||||||
|
visitor: knex.raw(config.database.query.user),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = async (knex) => {
|
||||||
|
await knex.schema.dropTable('releases_caps');
|
||||||
|
};
|
|
@ -0,0 +1,27 @@
|
||||||
|
exports.up = async (knex) => {
|
||||||
|
await knex.schema.alterTable('actors_profiles', (table) => {
|
||||||
|
table.string('hair_type');
|
||||||
|
table.decimal('shoe_size');
|
||||||
|
table.string('blood_type');
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.alterTable('actors', (table) => {
|
||||||
|
table.string('hair_type');
|
||||||
|
table.decimal('shoe_size');
|
||||||
|
table.string('blood_type');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = async (knex) => {
|
||||||
|
await knex.schema.alterTable('actors_profiles', (table) => {
|
||||||
|
table.dropColumn('hair_type');
|
||||||
|
table.dropColumn('shoe_size');
|
||||||
|
table.dropColumn('blood_type');
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.alterTable('actors', (table) => {
|
||||||
|
table.dropColumn('hair_type');
|
||||||
|
table.dropColumn('shoe_size');
|
||||||
|
table.dropColumn('blood_type');
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,13 @@
|
||||||
|
exports.up = async (knex) => {
|
||||||
|
await knex.schema.alterTable('batches', (table) => {
|
||||||
|
table.boolean('showcased')
|
||||||
|
.notNullable()
|
||||||
|
.defaultTo(true);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = async (knex) => {
|
||||||
|
await knex.schema.alterTable('batches', (table) => {
|
||||||
|
table.dropColumn('showcased');
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,27 @@
|
||||||
|
exports.up = async (knex) => {
|
||||||
|
await knex.schema.alterTable('releases', (table) => {
|
||||||
|
table.specificType('alt_titles', 'text ARRAY');
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.alterTable('movies', (table) => {
|
||||||
|
table.specificType('alt_titles', 'text ARRAY');
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.alterTable('series', (table) => {
|
||||||
|
table.specificType('alt_titles', 'text ARRAY');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = async (knex) => {
|
||||||
|
await knex.schema.alterTable('releases', (table) => {
|
||||||
|
table.dropColumn('alt_titles');
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.alterTable('movies', (table) => {
|
||||||
|
table.dropColumn('alt_titles');
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.alterTable('series', (table) => {
|
||||||
|
table.dropColumn('alt_titles');
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,36 @@
|
||||||
|
exports.up = async (knex) => {
|
||||||
|
await knex.schema.createTable('movies_teasers', (table) => {
|
||||||
|
table.integer('movie_id', 16)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('movies')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.text('media_id', 21)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('media');
|
||||||
|
|
||||||
|
table.unique('movie_id');
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.createTable('series_teasers', (table) => {
|
||||||
|
table.integer('serie_id', 16)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('series')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.text('media_id', 21)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('media');
|
||||||
|
|
||||||
|
table.unique('serie_id');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = async (knex) => {
|
||||||
|
await knex.schema.dropTable('movies_teasers');
|
||||||
|
await knex.schema.dropTable('series_teasers');
|
||||||
|
};
|
|
@ -0,0 +1,19 @@
|
||||||
|
exports.up = async (knex) => {
|
||||||
|
await knex.schema.alterTable('releases', (table) => {
|
||||||
|
table.integer('photo_count');
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.alterTable('movies', (table) => {
|
||||||
|
table.integer('photo_count');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = async (knex) => {
|
||||||
|
await knex.schema.alterTable('releases', (table) => {
|
||||||
|
table.dropColumn('photo_count');
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.alterTable('movies', (table) => {
|
||||||
|
table.dropColumn('photo_count');
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,42 @@
|
||||||
|
const config = require('config');
|
||||||
|
|
||||||
|
exports.up = async (knex) => {
|
||||||
|
await knex.schema.alterTable('alerts', (table) => {
|
||||||
|
table.boolean('all')
|
||||||
|
.defaultTo(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.alterTable('alerts_entities', (table) => {
|
||||||
|
table.dropUnique('alert_id');
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.createTable('alerts_matches', (table) => {
|
||||||
|
table.increments('id');
|
||||||
|
|
||||||
|
table.integer('alert_id')
|
||||||
|
.references('id')
|
||||||
|
.inTable('alerts')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.string('property');
|
||||||
|
table.string('expression');
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.raw(`
|
||||||
|
GRANT SELECT ON alerts_matches TO :visitor;
|
||||||
|
`, {
|
||||||
|
visitor: knex.raw(config.database.query.user),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = async (knex) => {
|
||||||
|
await knex.schema.alterTable('alerts', (table) => {
|
||||||
|
table.dropColumn('all');
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.alterTable('alerts_entities', (table) => {
|
||||||
|
table.unique('alert_id');
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.dropTable('alerts_matches');
|
||||||
|
};
|
|
@ -1,25 +0,0 @@
|
||||||
exports.up = async (knex) => knex.raw(`
|
|
||||||
CREATE MATERIALIZED VIEW entities_stats
|
|
||||||
AS
|
|
||||||
WITH RECURSIVE relations AS (
|
|
||||||
SELECT entities.id, entities.parent_id, count(releases.id) AS releases_count, count(releases.id) AS total_count
|
|
||||||
FROM entities
|
|
||||||
LEFT JOIN releases ON releases.entity_id = entities.id
|
|
||||||
GROUP BY entities.id
|
|
||||||
|
|
||||||
UNION ALL
|
|
||||||
|
|
||||||
SELECT entities.id AS entity_id, count(releases.id) AS releases_count, count(releases.id) + relations.total_count AS total_count
|
|
||||||
FROM entities
|
|
||||||
INNER JOIN relations ON relations.id = entities.parent_id
|
|
||||||
LEFT JOIN releases ON releases.entity_id = entities.id
|
|
||||||
GROUP BY entities.id
|
|
||||||
)
|
|
||||||
|
|
||||||
SELECT relations.id AS entity_id, relations.releases_count
|
|
||||||
FROM relations;
|
|
||||||
`);
|
|
||||||
|
|
||||||
exports.down = async (knex) => knex.raw(`
|
|
||||||
DROP MATERIALIZED VIEW entities_stats;
|
|
||||||
`);
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
exports.up = async (knex) => {
|
||||||
|
await knex.schema.alterTable('banners', (table) => {
|
||||||
|
table.text('html');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = async (knex) => {
|
||||||
|
await knex.schema.alterTable('banners', (table) => {
|
||||||
|
table.dropColumn('html');
|
||||||
|
});
|
||||||
|
};
|
16
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "traxxx",
|
"name": "traxxx",
|
||||||
"version": "1.225.10",
|
"version": "1.233.0",
|
||||||
"description": "All the latest porn releases in one place",
|
"description": "All the latest porn releases in one place",
|
||||||
"main": "src/app.js",
|
"main": "src/app.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -91,9 +91,11 @@
|
||||||
"convert": "^4.2.4",
|
"convert": "^4.2.4",
|
||||||
"cookie": "^0.4.0",
|
"cookie": "^0.4.0",
|
||||||
"csv-stringify": "^5.3.6",
|
"csv-stringify": "^5.3.6",
|
||||||
|
"date-fns": "^2.30.0",
|
||||||
"dayjs": "^1.8.21",
|
"dayjs": "^1.8.21",
|
||||||
"dompurify": "^2.0.11",
|
"dompurify": "^2.0.11",
|
||||||
"ejs": "^3.0.1",
|
"ejs": "^3.0.1",
|
||||||
|
"escape-string-regexp": "^4.0.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"express-promise-router": "^4.1.0",
|
"express-promise-router": "^4.1.0",
|
||||||
"express-react-views": "^0.11.0",
|
"express-react-views": "^0.11.0",
|
||||||
|
@ -113,21 +115,23 @@
|
||||||
"knex": "^0.95.12",
|
"knex": "^0.95.12",
|
||||||
"knex-migrate": "^1.7.4",
|
"knex-migrate": "^1.7.4",
|
||||||
"longjohn": "^0.2.12",
|
"longjohn": "^0.2.12",
|
||||||
|
"merge-anything": "^5.1.7",
|
||||||
"mime": "^2.4.4",
|
"mime": "^2.4.4",
|
||||||
"mitt": "^3.0.0",
|
"mitt": "^3.0.0",
|
||||||
|
"moment": "^2.24.0",
|
||||||
"nanoid": "^3.1.30",
|
"nanoid": "^3.1.30",
|
||||||
"node-fetch": "^2.6.7",
|
"node-fetch": "^2.6.7",
|
||||||
"object-merge-advanced": "^12.1.0",
|
"object-merge-advanced": "^12.1.0",
|
||||||
"object.omit": "^3.0.0",
|
"object.omit": "^3.0.0",
|
||||||
"opn": "^6.0.0",
|
|
||||||
"pg": "^8.5.1",
|
"pg": "^8.5.1",
|
||||||
"postgraphile": "^4.13.0",
|
"postgraphile": "^4.13.0",
|
||||||
"postgraphile-plugin-connection-filter": "^2.2.2",
|
"postgraphile-plugin-connection-filter": "^2.2.2",
|
||||||
"promise-task-queue": "^1.2.0",
|
"promise-task-queue": "^1.2.0",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"puppeteer": "^18.2.0",
|
"puppeteer": "^20.5.0",
|
||||||
"puppeteer-extra": "^3.3.4",
|
"puppeteer-extra": "^3.3.6",
|
||||||
"puppeteer-extra-plugin-stealth": "^2.11.1",
|
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
||||||
|
"redis": "^4.6.7",
|
||||||
"sharp": "^0.29.2",
|
"sharp": "^0.29.2",
|
||||||
"showdown": "^1.9.1",
|
"showdown": "^1.9.1",
|
||||||
"source-map-support": "^0.5.16",
|
"source-map-support": "^0.5.16",
|
||||||
|
@ -137,7 +141,7 @@
|
||||||
"tunnel": "0.0.6",
|
"tunnel": "0.0.6",
|
||||||
"ua-parser-js": "^1.0.32",
|
"ua-parser-js": "^1.0.32",
|
||||||
"undici": "^4.13.0",
|
"undici": "^4.13.0",
|
||||||
"unprint": "^0.8.2",
|
"unprint": "^0.10.11",
|
||||||
"url-pattern": "^1.0.3",
|
"url-pattern": "^1.0.3",
|
||||||
"v-tooltip": "^2.0.3",
|
"v-tooltip": "^2.0.3",
|
||||||
"video.js": "^7.11.4",
|
"video.js": "^7.11.4",
|
||||||
|
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 67 KiB |
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 67 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 74 KiB |
After Width: | Height: | Size: 85 KiB |
After Width: | Height: | Size: 190 KiB |
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 92 KiB |
After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |