104 Commits

Author SHA1 Message Date
DebaucheryLibrarian
33a327a04b Merge branch 'master' into experimental 2022-03-30 23:00:29 +02:00
DebaucheryLibrarian
a46061e247 1.212.9 2022-03-30 16:11:09 +02:00
DebaucheryLibrarian
94e07ff23d Added Bang! Podcast channel. 2022-03-30 16:11:07 +02:00
DebaucheryLibrarian
4811befcf6 1.212.8 2022-03-30 15:45:51 +02:00
DebaucheryLibrarian
c455f02c66 Updated Men URLs. 2022-03-30 15:45:41 +02:00
DebaucheryLibrarian
efc5620a28 1.212.7 2022-03-30 01:17:56 +02:00
DebaucheryLibrarian
61123fdb6a Added Accept-Language header to MindGeek requests, seems to help with acquiring sessions. 2022-03-30 01:17:54 +02:00
DebaucheryLibrarian
3ec6911d46 1.212.6 2022-03-29 23:24:58 +02:00
DebaucheryLibrarian
2021093645 Marked Dane Jones and Lesbea as native sites. 2022-03-29 23:24:55 +02:00
DebaucheryLibrarian
1c72dc202f 1.212.5 2022-03-28 23:44:44 +02:00
DebaucheryLibrarian
1ef946fa77 Marked Mile High sites as native. 2022-03-28 23:44:42 +02:00
DebaucheryLibrarian
3b6bbc39ff 1.212.4 2022-03-28 20:05:28 +02:00
DebaucheryLibrarian
481c9feada Fixed missing scenes photos breaking album. 2022-03-28 20:05:25 +02:00
DebaucheryLibrarian
953b3e9568 1.212.3 2022-03-28 00:36:05 +02:00
DebaucheryLibrarian
bdd2e68f49 Fixed centering logic in banner. 2022-03-28 00:36:04 +02:00
DebaucheryLibrarian
e4cc349302 1.212.2 2022-03-28 00:32:00 +02:00
DebaucheryLibrarian
6547b93e55 Fixed broken scene photo length check in release banner. 2022-03-28 00:31:59 +02:00
DebaucheryLibrarian
bb9649d23b 1.212.1 2022-03-28 00:22:59 +02:00
DebaucheryLibrarian
9e2eaef9d1 Added dedicated serie photos table, renamed serie scene photo function. Fixed covers and scene photos in banner and album. 2022-03-28 00:22:57 +02:00
DebaucheryLibrarian
1c3ee75d3b 1.212.0 2022-03-27 23:42:06 +02:00
DebaucheryLibrarian
15c9af8057 Added dedicated movie photo table, renamed scene photo function. 2022-03-27 23:42:03 +02:00
DebaucheryLibrarian
295573c1ef 1.211.2 2022-03-27 00:27:29 +01:00
DebaucheryLibrarian
e93e8ace5c Added deep scene force parameter to MindGeek scraper. 2022-03-27 00:27:26 +01:00
DebaucheryLibrarian
43af7ba777 1.211.1 2022-03-26 17:56:24 +01:00
DebaucheryLibrarian
0dad5b0d68 Added series removal utils. 2022-03-26 17:56:22 +01:00
DebaucheryLibrarian
ae9b793318 1.211.0 2022-03-26 17:40:23 +01:00
DebaucheryLibrarian
fd8170f223 Added series. 2022-03-26 17:40:20 +01:00
DebaucheryLibrarian
661b8b716b 1.210.1 2022-03-09 23:26:50 +01:00
DebaucheryLibrarian
5ff076cac3 Added DP Star Sex Challenges to Digital Playground. 2022-03-09 23:26:48 +01:00
DebaucheryLibrarian
41c100ac4e 1.210.0 2022-03-04 23:32:28 +01:00
DebaucheryLibrarian
c6e977f842 Added movie support to MindGeek scraper. 2022-03-04 23:32:09 +01:00
DebaucheryLibrarian
50b7f521b5 1.209.5 2022-02-27 21:15:07 +01:00
DebaucheryLibrarian
f0d0ee3acc Removed redundant scenes path from Digital Playground main channel. 2022-02-27 21:15:05 +01:00
DebaucheryLibrarian
7b3bdadd44 1.209.4 2022-02-25 22:13:43 +01:00
DebaucheryLibrarian
5deba6b90f Passing resolved parameters into before-scrape methods. 2022-02-25 22:13:41 +01:00
DebaucheryLibrarian
a5afffc968 1.209.3 2022-02-25 00:00:21 +01:00
DebaucheryLibrarian
a239a5c593 Fixed entity scenes function for studios. Added triple anal tag alias. 2022-02-25 00:00:18 +01:00
DebaucheryLibrarian
e56e7333e3 1.209.2 2022-02-24 23:33:26 +01:00
DebaucheryLibrarian
d55e3c37cd Updated media seeds from LegalPorno to AnalVids. 2022-02-24 23:33:19 +01:00
DebaucheryLibrarian
97b78ea016 1.209.1 2022-02-24 23:30:49 +01:00
DebaucheryLibrarian
3e290b74dc Updated affiliates from LegalPorno to AnalVids. 2022-02-24 23:30:47 +01:00
DebaucheryLibrarian
65141207ae 1.209.0 2022-02-24 23:28:27 +01:00
DebaucheryLibrarian
17dfeac1af Renamed LegalPorno to AnalVids, added missing studios. 2022-02-24 23:28:24 +01:00
DebaucheryLibrarian
4a9c428d69 1.208.1 2022-02-24 22:43:01 +01:00
DebaucheryLibrarian
333f252099 Added BJ Raw to Radical. 2022-02-24 22:42:59 +01:00
DebaucheryLibrarian
38232f258a 1.208.0 2022-02-24 22:33:03 +01:00
DebaucheryLibrarian
582269cfaa Added Radical metadata layout scraper and the Got Filled and Inserted channels. 2022-02-24 22:33:00 +01:00
DebaucheryLibrarian
0b646429fd 1.207.5 2022-02-20 23:40:23 +01:00
DebaucheryLibrarian
fecef6c1cf Added missing MOFOS serie channels. 2022-02-20 23:40:20 +01:00
DebaucheryLibrarian
73e5404c44 1.207.4 2022-02-20 16:52:32 +01:00
DebaucheryLibrarian
690d2bb3ed Added MOFOS to default rate limit config, added light logos. 2022-02-20 16:52:29 +01:00
DebaucheryLibrarian
1dd935e1e9 1.207.3 2022-02-14 01:27:58 +01:00
DebaucheryLibrarian
8386230f33 Added missing Team Skeet sites. 2022-02-14 01:27:56 +01:00
DebaucheryLibrarian
5a68b06137 1.207.2 2022-02-13 01:34:41 +01:00
DebaucheryLibrarian
5918364cf5 Preventing unnecessary deep scrapes in Team Skeet scraper. 2022-02-13 01:34:39 +01:00
DebaucheryLibrarian
df4d860d35 1.207.1 2022-02-12 17:19:49 +01:00
DebaucheryLibrarian
d9f0db6e3c Fixed reading screen ID from non-existent item in Bang scraper. 2022-02-12 17:19:47 +01:00
DebaucheryLibrarian
7372b560b2 1.207.0 2022-02-12 17:16:26 +01:00
DebaucheryLibrarian
9d638c685c 1.206.12 2022-02-12 17:16:23 +01:00
DebaucheryLibrarian
5da1acc38d Added media resize. 2022-02-12 17:16:20 +01:00
DebaucheryLibrarian
c7c62e220d Removed console log from Bang scraper. 2022-02-12 16:22:41 +01:00
DebaucheryLibrarian
9edd652a2c 1.206.11 2022-02-12 16:22:03 +01:00
DebaucheryLibrarian
cde760c1ea Preventing Bang! scraper from redundant deep scraping when base release is available. 2022-02-12 16:22:00 +01:00
DebaucheryLibrarian
19c7f958e1 1.206.10 2022-02-12 03:26:43 +01:00
DebaucheryLibrarian
c7710afdbf Fixed Bang! scraped failing when scene has no photos. 2022-02-12 03:26:40 +01:00
DebaucheryLibrarian
ba18a3dadb 1.206.9 2022-02-11 22:14:46 +01:00
DebaucheryLibrarian
496c29e569 Configured Reality Kings to fetch session from RK scene overview. 2022-02-11 22:14:44 +01:00
DebaucheryLibrarian
98a72a4929 1.206.8 2022-02-09 00:19:56 +01:00
DebaucheryLibrarian
c4f0b48932 Fixed cover dimensions on movie tile to show lazy image, showing missing cover icon. 2022-02-09 00:19:54 +01:00
DebaucheryLibrarian
93abbab873 1.206.7 2022-02-08 23:59:48 +01:00
DebaucheryLibrarian
f7bbab08bd Added Porn Pros main channel and working URLs for Flexible Positions, Public Violations and Squirt Disgrace. Added bisexual tag photos. 2022-02-08 23:59:44 +01:00
DebaucheryLibrarian
1e4ddf2bbf 1.206.6 2022-02-08 00:19:36 +01:00
DebaucheryLibrarian
9b7879bff7 Removed channel ID from Gamma update query to prevent duplicate scenes. Added sexuality tags with covers to overview. 2022-02-08 00:19:34 +01:00
DebaucheryLibrarian
03d96d4dec 1.206.5 2022-02-07 22:29:11 +01:00
DebaucheryLibrarian
1dfa034332 Handling 'page not found' with 200 OK in Gamma scraper. 2022-02-07 22:29:09 +01:00
DebaucheryLibrarian
196a1d33e3 1.206.4 2022-02-07 22:16:46 +01:00
DebaucheryLibrarian
3d349c413f Fixed Adult Time scraper. 2022-02-07 22:16:43 +01:00
DebaucheryLibrarian
bd5babd37e 1.206.3 2022-02-07 21:58:31 +01:00
DebaucheryLibrarian
fffe9080f3 Added stand-alone Spizoo sites. 2022-02-07 21:58:27 +01:00
DebaucheryLibrarian
5630b16e99 1.206.2 2022-02-07 01:57:14 +01:00
DebaucheryLibrarian
4bcf7ef45b Storing associations if existing media are found. 2022-02-07 01:57:09 +01:00
DebaucheryLibrarian
9abc1d48ac 1.206.1 2022-02-05 17:14:37 +01:00
DebaucheryLibrarian
ceaf5a3217 Not storing shallow movie data when argument is disabled. 2022-02-05 17:14:34 +01:00
DebaucheryLibrarian
36d84fb98b 1.206.0 2022-02-03 00:04:51 +01:00
DebaucheryLibrarian
3d80e9d77b Added movie URL file support. Fixed Gamma movie API scraper movie URL. 2022-02-03 00:04:48 +01:00
DebaucheryLibrarian
1dc38ffacc 1.205.20 2022-02-02 23:53:44 +01:00
DebaucheryLibrarian
ee210b5c68 Replaced Gender X with Gender X Films, fixed Zero Tolerance movie URLs. 2022-02-02 23:53:42 +01:00
DebaucheryLibrarian
3aae3cd3a1 1.205.19 2022-02-02 23:51:20 +01:00
DebaucheryLibrarian
f1a7f2f905 Added Gamma movie API scraper. 2022-02-02 23:51:15 +01:00
DebaucheryLibrarian
0b7e0319f1 1.205.18 2022-02-02 22:25:06 +01:00
DebaucheryLibrarian
10b8abf706 Updated Zero Tolerance channel to Zero Tolerance Films. 2022-02-02 22:25:04 +01:00
DebaucheryLibrarian
b163223ff1 1.205.17 2022-02-01 21:18:58 +01:00
DebaucheryLibrarian
b9f3eb85f9 Ignoring Gamma master categories in update scrape as these seem to contain irrelevant tags. 2022-02-01 21:18:54 +01:00
DebaucheryLibrarian
285a65f018 1.205.16 2022-01-30 22:15:09 +01:00
DebaucheryLibrarian
815d56d334 Preventing media associations when use-reference is unavailable. 2022-01-30 22:15:07 +01:00
DebaucheryLibrarian
27a9a233e2 1.205.15 2022-01-30 17:05:20 +01:00
DebaucheryLibrarian
22864105ac Writing media associations to disk for debugging. 2022-01-30 17:05:18 +01:00
DebaucheryLibrarian
7cf47cbf8d 1.205.14 2022-01-30 00:18:17 +01:00
DebaucheryLibrarian
502c588958 Updated Bang Bros product ID for Haze Her, removed wrong ones. 2022-01-30 00:18:15 +01:00
DebaucheryLibrarian
81792a7c2f 1.205.13 2022-01-30 00:08:41 +01:00
DebaucheryLibrarian
784d326584 Updated Bang Bros product ID for College Rules. 2022-01-30 00:08:39 +01:00
DebaucheryLibrarian
83dc7aa578 1.205.12 2022-01-29 23:50:01 +01:00
DebaucheryLibrarian
5a3b27fbf7 Fixed movie cover index for overview in GraphQL query. 2022-01-29 23:49:59 +01:00
DebaucheryLibrarian
ccb99e278c Added periodic memory logger. 2021-11-20 23:59:15 +01:00
609 changed files with 5605 additions and 1219 deletions

View File

@@ -6,7 +6,7 @@
<div class="actor-header"> <div class="actor-header">
<h2 class="header-name"> <h2 class="header-name">
<span v-if="actor.entity">{{ actor.name }} ({{ actor.entity.name }})</span> <span v-if="actor.entity">{{ actor.name }} ({{ actor.entity.name }})</span>
<span v-else="">{{ actor.name }}</span> <span v-else>{{ actor.name }}</span>
<Gender <Gender
:gender="actor.gender" :gender="actor.gender"

View File

@@ -46,7 +46,7 @@
/> />
</li> </li>
<template v-slot:tooltip> <template #tooltip>
<Search <Search
content="actors" content="actors"
@select="actor => addActor(actor)" @select="actor => addActor(actor)"
@@ -88,7 +88,7 @@
/> />
</li> </li>
<template v-slot:tooltip> <template #tooltip>
<Search <Search
content="tags" content="tags"
:defaults="['anal', 'blowbang', 'mfm', 'dp', 'gangbang', 'airtight']" :defaults="['anal', 'blowbang', 'mfm', 'dp', 'gangbang', 'airtight']"
@@ -126,7 +126,7 @@
/> />
</div> </div>
<template v-slot:tooltip> <template #tooltip>
<Search <Search
label="Search channels" label="Search channels"
content="entities" content="entities"
@@ -181,7 +181,7 @@
/> />
</li> </li>
<template v-slot:tooltip> <template #tooltip>
<Search <Search
content="stashes" content="stashes"
@select="stash => addStash(stash)" @select="stash => addStash(stash)"
@@ -194,6 +194,7 @@
<div class="dialog-actions right"> <div class="dialog-actions right">
<button <button
:disabled="actors.length === 0 && tags.length === 0 && !entity"
type="submit" type="submit"
class="button button-primary" class="button button-primary"
>Add alert</button> >Add alert</button>
@@ -210,19 +211,19 @@ import Search from './search.vue';
async function addAlert() { async function addAlert() {
await this.$store.dispatch('addAlert', { await this.$store.dispatch('addAlert', {
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, entity: this.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);
} }
function addActor(actor) { function addActor(actor) {
if (!this.actors.some(selectedActor => selectedActor.id === actor.id)) { if (!this.actors.some((selectedActor) => selectedActor.id === actor.id)) {
this.actors = this.actors.concat(actor); this.actors = this.actors.concat(actor);
} }
@@ -235,7 +236,7 @@ function addEntity(entity) {
} }
function addTag(tag) { function addTag(tag) {
if (!this.tags.some(selectedTag => selectedTag.id === tag.id)) { if (!this.tags.some((selectedTag) => selectedTag.id === tag.id)) {
this.tags = this.tags.concat(tag); this.tags = this.tags.concat(tag);
} }
@@ -243,7 +244,7 @@ function addTag(tag) {
} }
function removeActor(actor) { 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() {
@@ -251,11 +252,11 @@ function removeEntity() {
} }
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 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);
} }
@@ -263,7 +264,7 @@ function addStash(stash) {
} }
function removeStash(stash) { function removeStash(stash) {
this.stashes = this.stashes.filter(listedStash => listedStash.id !== stash.id); this.stashes = this.stashes.filter((listedStash) => listedStash.id !== stash.id);
} }
export default { export default {
@@ -273,6 +274,7 @@ export default {
Entity, Entity,
Search, Search,
}, },
emits: ['close'],
data() { data() {
return { return {
actors: [], actors: [],
@@ -284,7 +286,6 @@ export default {
availableStashes: this.$store.state.auth.user.stashes, availableStashes: this.$store.state.auth.user.stashes,
}; };
}, },
emits: ['close'],
methods: { methods: {
addActor, addActor,
addAlert, addAlert,

View File

@@ -62,7 +62,7 @@ async function setConsent(consent, includeQueer) {
} }
if (includeQueer) { if (includeQueer) {
this.$store.dispatch('setTagFilter', this.$store.state.ui.tagFilter.filter(tag => !['gay', 'bisexual', 'transsexual'].includes(tag))); this.$store.dispatch('setTagFilter', this.$store.state.ui.tagFilter.filter((tag) => !['gay', 'bisexual', 'transsexual'].includes(tag)));
return; return;
} }

View File

@@ -108,6 +108,7 @@
:fetch-releases="fetchEntity" :fetch-releases="fetchEntity"
:items-total="totalCount" :items-total="totalCount"
:items-per-page="limit" :items-per-page="limit"
:available-tags="entity.tags"
/> />
<div class="releases"> <div class="releases">

View File

@@ -102,6 +102,8 @@ export default {
} }
.name { .name {
display: flex;
align-items: center;
color: var(--text-light); color: var(--text-light);
font-size: 1.25rem; font-size: 1.25rem;
font-weight: bold; font-weight: bold;

View File

@@ -18,19 +18,16 @@
<div class="filters"> <div class="filters">
<ActorFilter <ActorFilter
class="filters-filter" class="filters-filter"
:filter="filter"
:available-actors="availableActors" :available-actors="availableActors"
/> />
<ChannelFilter <ChannelFilter
class="filters-filter" class="filters-filter"
:filter="filter"
:available-channels="availableChannels" :available-channels="availableChannels"
/> />
<TagFilter <TagFilter
class="filters-filter" class="filters-filter"
:filter="filter"
:available-tags="availableTags" :available-tags="availableTags"
/> />
</div> </div>
@@ -44,10 +41,6 @@ import ActorFilter from './actor-filter.vue';
import ChannelFilter from './channel-filter.vue'; import ChannelFilter from './channel-filter.vue';
import TagFilter from './tag-filter.vue'; import TagFilter from './tag-filter.vue';
function filter(state) {
return state.ui.filter;
}
function range() { function range() {
return this.$route.params.range; return this.$route.params.range;
} }
@@ -114,7 +107,6 @@ export default {
}, },
computed: { computed: {
...mapState({ ...mapState({
filter,
range, range,
batch, batch,
}), }),
@@ -129,6 +121,43 @@ export default {
<style lang="scss"> <style lang="scss">
@import 'breakpoints'; @import 'breakpoints';
.filter {
color: var(--shadow);
display: inline-flex;
align-items: center;
.filter-applied {
flex-grow: 1;
padding: .75rem .5rem;
font-size: 1rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: right;
&.empty {
color: var(--shadow);
}
}
.icon {
fill: var(--shadow);
margin: -.1rem 0 0 0;
}
&:hover {
cursor: pointer;
.applied {
color: var(--shadow-strong);
}
.icon {
fill: var(--shadow-strong);
}
}
}
.filter-mode { .filter-mode {
width: 100%; width: 100%;
color: var(--shadow); color: var(--shadow);
@@ -274,43 +303,6 @@ export default {
} }
} }
::v-deep(.filter) {
color: var(--shadow);
display: inline-flex;
align-items: center;
.filter-applied {
flex-grow: 1;
padding: .75rem .5rem;
font-size: 1rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: right;
&.empty {
color: var(--shadow);
}
}
.icon {
fill: var(--shadow);
margin: -.1rem 0 0 0;
}
&:hover {
cursor: pointer;
.applied {
color: var(--shadow-strong);
}
.icon {
fill: var(--shadow-strong);
}
}
}
.filters { .filters {
flex-shrink: 0; flex-shrink: 0;
} }

View File

@@ -14,7 +14,7 @@
>Tags</div> >Tags</div>
</div> </div>
<template v-slot:tooltip> <template #tooltip>
<div <div
class="filter-options" class="filter-options"
@click.stop @click.stop
@@ -70,7 +70,7 @@
<script> <script>
function getNewRange(tag) { function getNewRange(tag) {
if (this.selectedTags.includes(tag)) { if (this.selectedTags.includes(tag)) {
return { tags: this.selectedTags.filter(selectedTag => selectedTag !== tag).join(',') || undefined }; return { tags: this.selectedTags.filter((selectedTag) => selectedTag !== tag).join(',') || undefined };
} }
return { tags: this.selectedTags.concat(tag).join(',') }; return { tags: this.selectedTags.concat(tag).join(',') };
@@ -82,10 +82,6 @@ function selectedTags() {
export default { export default {
props: { props: {
filter: {
type: Array,
default: () => [],
},
compact: { compact: {
type: Boolean, type: Boolean,
default: false, default: false,

View File

@@ -28,7 +28,7 @@ export default {
}; };
}, },
beforeMount() { beforeMount() {
this.svg = require(`../../img/icons/${this.icon}.svg`).default; this.svg = require(`../../img/icons/${this.icon}.svg`).default; // eslint-disable-line global-require, import/no-dynamic-require
}, },
}; };
</script> </script>

View File

@@ -11,16 +11,31 @@
class="empty" class="empty"
>No results for "{{ $route.query.query }}"</span> >No results for "{{ $route.query.query }}"</span>
<div <template v-else>
v-else <h2 class="heading">Popular</h2>
class="entity-tiles"
> <div
<Entity class="entity-tiles"
v-for="entity in entities" >
:key="entity.parent ? `entity-tile-${entity.parent.slug}-${entity.slug}` : `entity-tile-${entity.slug}`" <Entity
:entity="entity" v-for="entity in popularEntities"
/> :key="entity.parent ? `entity-tile-${entity.parent.slug}-${entity.slug}` : `entity-tile-${entity.slug}`"
</div> :entity="entity"
/>
</div>
<h2 class="heading">All networks</h2>
<div
class="entity-tiles"
>
<Entity
v-for="entity in entities"
:key="entity.parent ? `entity-tile-${entity.parent.slug}-${entity.slug}` : `entity-tile-${entity.slug}`"
:entity="entity"
/>
</div>
</template>
</div> </div>
<Footer /> <Footer />
@@ -58,6 +73,45 @@ async function searchEntities() {
this.done = true; this.done = true;
} }
function popularEntities() {
const entitiesBySlug = Object.fromEntries(this.entities.map((entity) => [entity.slug, entity]));
return [
'21sextury',
'amateurallure',
'analvids',
'bamvisions',
'bang',
'bangbros',
'blowpass',
'brazzers',
'burningangel',
'digitalplayground',
'dogfartnetwork',
'dorcel',
'elegantangel',
'evilangel',
'fakehub',
'girlsway',
'hookuphotshot',
'hussiepass',
'insex',
'julesjordan',
'kellymadison',
'kink',
'mofos',
'naughtyamerica',
'newsensations',
'pervcity',
'pornpros',
'private',
'realitykings',
'twistys',
'vixen',
'xempire',
].map((slug) => entitiesBySlug[slug]).filter(Boolean);
}
async function mounted() { async function mounted() {
this.pageTitle = 'Channels'; this.pageTitle = 'Channels';
@@ -82,6 +136,7 @@ export default {
}, },
computed: { computed: {
channelCount, channelCount,
popularEntities,
}, },
watch: { watch: {
$route: fetchEntities, $route: fetchEntities,
@@ -130,6 +185,10 @@ export default {
font-weight: bold; font-weight: bold;
} }
.heading {
margin: 1rem 0 0 0;
}
@media(max-width: $breakpoint2) { @media(max-width: $breakpoint2) {
.entity-tiles { .entity-tiles {
grid-gap: .5rem; grid-gap: .5rem;

View File

@@ -2,7 +2,7 @@
<div class="media-container"> <div class="media-container">
<div <div
class="media" class="media"
:class="{ center: release.photos.length < 2, preview: !me }" :class="{ center: (release.photos?.length || 0) + (release.scenesPhotos?.length || 0) < 2, preview: !me }"
> >
<div <div
v-if="release.trailer || release.teaser" v-if="release.trailer || release.teaser"
@@ -71,24 +71,28 @@
</span> </span>
</div> </div>
<template v-if="release.covers && release.covers.length > 0"> <template v-if="release.covers?.length > 0">
<a <div
v-for="cover in release.covers" v-for="cover in release.covers"
:key="`cover-${cover.id}`" :key="`cover-${cover.id}`"
:href="getPath(cover)" class="item-container"
target="_blank"
rel="noopener noreferrer"
> >
<img <a
:src="getPath(cover, 'thumbnail')" :href="getPath(cover)"
:style="{ 'background-image': getBgPath(cover, 'lazy') }" target="_blank"
:width="cover.thumbnailWidth" rel="noopener noreferrer"
:height="cover.thumbnailHeight"
class="item cover"
loading="lazy"
@load="$emit('load', $event)"
> >
</a> <img
:src="getPath(cover, 'thumbnail')"
:style="{ 'background-image': getBgPath(cover, 'lazy') }"
:width="cover.thumbnailWidth"
:height="cover.thumbnailHeight"
class="item cover"
loading="lazy"
@load="$emit('load', $event)"
>
</a>
</div>
</template> </template>
<div <div
@@ -164,8 +168,8 @@ function poster() {
function photos() { 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(uniqueClipPosters); const photosWithClipPosters = (this.release.photos || []).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')) {
// poster will be on trailer video // poster will be on trailer video

View File

@@ -14,6 +14,12 @@
loading="lazy" loading="lazy"
> >
<div
v-else
:title="movie.title"
class="unavailable"
><Icon icon="blocked" /></div>
<Icon <Icon
v-show="(!stash || stash.primary) && favorited" v-show="(!stash || stash.primary) && favorited"
icon="heart7" icon="heart7"
@@ -161,7 +167,6 @@ export default {
} }
.movie { .movie {
height: 16rem;
display: flex; display: flex;
} }
@@ -171,18 +176,30 @@ export default {
} }
.cover { .cover {
height: 100%; height: 16rem;
width: 11.25rem;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
position: relative; position: relative;
box-shadow: 0 0 3px var(--darken-weak); box-shadow: 0 0 3px var(--darken-weak);
background-color: var(--shadow-hint);
img { img {
height: 100%; height: 100%;
max-width: 12rem; width: 100%;
background-position: center; background-position: center;
background-size: cover; background-size: cover;
object-fit: cover; object-fit: cover;
object-position: center; object-position: center;
} }
.unavailable .icon {
width: 2rem;
height: 2rem;
fill: var(--shadow-hint);
}
} }
.info { .info {
@@ -254,10 +271,10 @@ export default {
.stash { .stash {
width: 1.5rem; width: 1.5rem;
height: 1.5rem; height: 1.5rem;
padding: .25rem .5rem .5rem .25rem; padding: .25rem .5rem .5rem .5rem;
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; left: 0;
fill: var(--lighten-weak); fill: var(--lighten-weak);
filter: drop-shadow(0 0 2px var(--darken)); filter: drop-shadow(0 0 2px var(--darken));
@@ -273,8 +290,9 @@ export default {
} }
@media(max-width: $breakpoint-kilo) { @media(max-width: $breakpoint-kilo) {
.movie { .cover {
height: 12rem; height: 12rem;
width: 8.25rem;
} }
/* ensure no half actor names show */ /* ensure no half actor names show */

View File

@@ -21,14 +21,14 @@
<Details :release="release" /> <Details :release="release" />
<button <button
v-if="release.photos.length > 0" v-if="release.photos?.length > 0 || release.scenesPhotos?.length > 0"
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"
:items="[release.poster, ...release.photos]" :items="[release.poster, ...(release.photos || []), ...(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 })"
@@ -79,22 +79,23 @@
/> />
<div <div
v-if="release.movies && release.movies.length > 0" v-if="release.movies?.length > 0 || release.series?.length > 0"
class="row" class="row"
> >
<span class="row-label">Part of</span> <span class="row-label">Part of</span>
<div class="movies"> <div class="movies">
<router-link <router-link
v-for="movie in release.movies" v-for="movie in [...release.movies, ...release.series]"
:key="`movie-${movie.id}`" :key="`movie-${movie.id}`"
:to="{ name: 'movie', params: { releaseId: movie.id, releaseSlug: movie.slug } }" :to="{ name: movie.type || 'movie', params: { releaseId: movie.id, releaseSlug: movie.slug } }"
class="movie" class="movie"
> >
<span class="movie-title">{{ movie.title }}</span> <span class="movie-title">{{ movie.title }}</span>
<img <img
v-if="movie.covers.length > 0" v-if="movie.covers.length > 0 || movie.poster"
:src="getPath(movie.covers[0], 'thumbnail')" :src="getPath(movie.covers[0] || movie.poster, 'thumbnail')"
class="movie-cover" class="movie-cover"
> >
</router-link> </router-link>
@@ -243,6 +244,10 @@ async function fetchRelease(scroll = true) {
this.release = await this.$store.dispatch('fetchMovieById', this.$route.params.releaseId); this.release = await this.$store.dispatch('fetchMovieById', this.$route.params.releaseId);
} }
if (this.$route.name === 'serie') {
this.release = await this.$store.dispatch('fetchSerieById', this.$route.params.releaseId);
}
if (scroll && this.$refs.content) { if (scroll && this.$refs.content) {
this.$refs.content.scrollTop = 0; this.$refs.content.scrollTop = 0;
} }
@@ -282,7 +287,7 @@ function pageTitle() {
} }
function showAlbum() { function showAlbum() {
return this.release.photos?.length > 0 && this.$route.hash === '#album'; return (this.release.photos?.length > 0 || this.release.scenesPhotos?.length > 0) && this.$route.hash === '#album';
} }
export default { export default {

View File

@@ -45,6 +45,8 @@ const tagSlugsByCategory = {
'teen', 'teen',
'milf', 'milf',
'blowjob', 'blowjob',
'gay',
'transsexual',
'dp', 'dp',
'gangbang', 'gangbang',
'facial', 'facial',
@@ -64,6 +66,11 @@ const tagSlugsByCategory = {
'tattoos', 'tattoos',
'piercings', 'piercings',
], ],
sexuality: [
'gay',
'bisexual',
'transsexual',
],
oral: [ oral: [
'blowjob', 'blowjob',
'pussy-eating', 'pussy-eating',
@@ -80,7 +87,6 @@ const tagSlugsByCategory = {
'titty-fucking', 'titty-fucking',
'fisting', 'fisting',
'anal-fisting', 'anal-fisting',
'fisting-dp',
], ],
group: [ group: [
'mfm', 'mfm',
@@ -101,16 +107,6 @@ const tagSlugsByCategory = {
'bukkake', 'bukkake',
'fake-cum', 'fake-cum',
], ],
toys: [
'toys',
'toy-anal',
'toy-dp',
'double-dildo',
'double-dildo-blowjob',
'double-dildo-kiss',
'double-dildo-anal',
'double-dildo-dp',
],
roleplay: [ roleplay: [
'family', 'family',
'parody', 'parody',
@@ -119,6 +115,15 @@ const tagSlugsByCategory = {
'maid', 'maid',
'nun', 'nun',
], ],
extreme: [
'dp',
'airtight',
'dap',
'dvp',
'triple-penetration',
'tap',
'tvp',
],
fetish: [ fetish: [
'bdsm', 'bdsm',
'femdom', 'femdom',
@@ -127,15 +132,15 @@ const tagSlugsByCategory = {
'latex', 'latex',
'blindfold', 'blindfold',
], ],
extreme: [ toys: [
'dp', 'toys',
'airtight', 'toy-anal',
'dap', 'toy-dp',
'dvp', 'double-dildo',
'da-tp', 'double-dildo-blowjob',
'dv-tp', 'double-dildo-kiss',
'tap', 'double-dildo-anal',
'tvp', 'double-dildo-dp',
], ],
misc: [ misc: [
'gaping', 'gaping',
@@ -162,10 +167,18 @@ async function fetchTags() {
const tagsBySlug = tags.reduce((acc, tag) => ({ ...acc, [tag.slug]: tag }), {}); const tagsBySlug = tags.reduce((acc, tag) => ({ ...acc, [tag.slug]: tag }), {});
this.categories = Object.entries(tagSlugsByCategory).reduce((acc, [category, tagSlugs]) => ({ this.categories = Object.entries(tagSlugsByCategory).reduce((acc, [category, tagSlugs]) => {
...acc, const categoryTags = tagSlugs.map((tagSlug) => tagsBySlug[tagSlug]).filter(Boolean);
[category]: tagSlugs.map(tagSlug => tagsBySlug[tagSlug]),
}), {}); if (categoryTags.length === 0) {
return acc;
}
return {
...acc,
[category]: categoryTags,
};
}, {});
} }
async function searchTags() { async function searchTags() {
@@ -202,6 +215,7 @@ export default {
}, },
watch: { watch: {
$route: fetchTags, $route: fetchTags,
'$store.state.ui.tagFilter': fetchTags,
}, },
mounted, mounted,
methods: { methods: {

View File

@@ -44,6 +44,11 @@
&:hover { &:hover {
background: var(--primary-strong); background: var(--primary-strong);
} }
&:disabled {
background: var(--shadow-weak);
cursor: default;
}
} }
.button-secondary { .button-secondary {

View File

@@ -65,19 +65,23 @@ function curateActor(actor, release) {
return curatedActor; return curatedActor;
} }
function curateRelease(release) { function curateRelease(release, type = 'scene') {
const curatedRelease = { const curatedRelease = {
...release, ...release,
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) : [],
}; };
if (release.scenes) curatedRelease.scenes = release.scenes.filter(Boolean).map(({ scene }) => curateRelease(scene)); curatedRelease.scenes = release.scenes?.filter(Boolean).map(({ scene }) => curateRelease(scene, 'scene')) || [];
if (release.movies) curatedRelease.movies = release.movies.filter(Boolean).map(({ movie }) => curateRelease(movie)); curatedRelease.movies = release.movies?.filter(Boolean).map(({ movie }) => curateRelease(movie, 'movie')) || [];
if (release.chapters) curatedRelease.chapters = release.chapters.filter(Boolean).map((chapter) => curateRelease(chapter)); curatedRelease.series = release.series?.filter(Boolean).map(({ serie }) => curateRelease(serie, 'serie')) || [];
if (release.photos) curatedRelease.photos = release.photos.filter(Boolean).map((photo) => photo.media || photo); curatedRelease.chapters = release.chapters?.filter(Boolean).map((chapter) => curateRelease(chapter)) || [];
if (release.covers) curatedRelease.covers = release.covers.filter(Boolean).map(({ media }) => media); curatedRelease.photos = release.photos?.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) || [];
if (release.trailer) curatedRelease.trailer = release.trailer.media; if (release.trailer) curatedRelease.trailer = release.trailer.media;
if (release.teaser) curatedRelease.teaser = release.teaser.media; if (release.teaser) curatedRelease.teaser = release.teaser.media;
if (release.actors) curatedRelease.actors = release.actors.filter(Boolean).map((actor) => curateActor(actor.actor || actor, curatedRelease)); if (release.actors) curatedRelease.actors = release.actors.filter(Boolean).map((actor) => curateActor(actor.actor || actor, curatedRelease));
@@ -105,6 +109,7 @@ function curateEntity(entity, parent, releases) {
}; };
if (entity.tags) curatedEntity.tags = entity.tags.map(({ tag }) => tag); if (entity.tags) curatedEntity.tags = entity.tags.map(({ tag }) => tag);
if (entity.sceneTags) curatedEntity.sceneTags = entity.sceneTags;
if (entity.children) { if (entity.children) {
if (entity.children.nodes) { if (entity.children.nodes) {

View File

@@ -41,6 +41,11 @@ function initEntitiesActions(store, router) {
slug slug
} }
} }
sceneTags {
id
name
slug
}
children: childEntitiesConnection( children: childEntitiesConnection(
orderBy: [PRIORITY_DESC, NAME_ASC], orderBy: [PRIORITY_DESC, NAME_ASC],
filter: { filter: {
@@ -87,7 +92,7 @@ function initEntitiesActions(store, router) {
or: [ or: [
{ {
date: { date: {
isNull: ${entityType === 'channel'} isNull: ${entityType !== 'network'}
} }
} }
{ {

View File

@@ -125,7 +125,7 @@ const movieFields = `
type type
} }
} }
covers: moviesCovers { covers: moviesCovers(orderBy: MEDIA_BY_MEDIA_ID__INDEX_ASC) {
media { media {
id id
path path
@@ -442,6 +442,29 @@ const releasesFragment = `
} }
`; `;
const mediaFields = `
id
index
path
thumbnail
lazy
isS3
comment
sfw: sfwMedia {
id
thumbnail
lazy
path
comment
}
`;
const mediaFragment = `
media {
${mediaFields}
}
`;
const releaseFragment = ` const releaseFragment = `
release(id: $releaseId) { release(id: $releaseId) {
id id
@@ -536,6 +559,19 @@ const releaseFragment = `
} }
} }
} }
series: seriesScenesBySceneId {
serie {
id
title
slug
covers: seriesCoversBySerieId(orderBy: MEDIA_BY_MEDIA_ID__INDEX_ASC) {
${mediaFragment}
}
poster: seriesPosterBySerieId {
${mediaFragment}
}
}
}
isFavorited isFavorited
isStashed(includeFavorites: false) isStashed(includeFavorites: false)
stashes: stashesScenesBySceneId( stashes: stashesScenesBySceneId(
@@ -624,6 +660,8 @@ export {
actorFields, actorFields,
actorStashesFields, actorStashesFields,
campaignsFragment, campaignsFragment,
mediaFields,
mediaFragment,
movieFields, movieFields,
releaseActorsFragment, releaseActorsFragment,
releaseFields, releaseFields,

View File

@@ -4,6 +4,8 @@ import {
releaseFragment, releaseFragment,
releaseFields, releaseFields,
movieFields, movieFields,
mediaFragment,
mediaFields,
} from '../fragments'; } from '../fragments';
import { curateRelease } from '../curate'; import { curateRelease } from '../curate';
import getDateRange from '../get-date-range'; import getDateRange from '../get-date-range';
@@ -110,6 +112,8 @@ function initReleasesActions(store, router) {
$query: String! $query: String!
$limit:Int = 20 $limit:Int = 20
$offset:Int = 0 $offset:Int = 0
$hasAuth: Boolean!
$userId: Int
) { ) {
connection: searchMoviesConnection( connection: searchMoviesConnection(
query: $query query: $query
@@ -126,6 +130,8 @@ function initReleasesActions(store, router) {
} }
} }
`, { `, {
hasAuth: !!store.state.auth.user,
userId: store.state.auth.user?.id,
query, query,
limit, limit,
offset: Math.max(0, (pageNumber - 1)) * limit, offset: Math.max(0, (pageNumber - 1)) * limit,
@@ -137,16 +143,16 @@ function initReleasesActions(store, router) {
}; };
} }
async function fetchMovieById({ _commit }, movieId) { async function fetchCollectionById({ _commit }, movieId, type = 'movie') {
// const release = await get(`/releases/${releaseId}`); // const release = await get(`/releases/${releaseId}`);
const { movie } = await graphql(` const result = await graphql(`
query Movie( query Movie(
$movieId: Int! $movieId: Int!
$hasAuth: Boolean! $hasAuth: Boolean!
$userId: Int $userId: Int
) { ) {
movie(id: $movieId) { ${type}(id: $movieId) {
id id
title title
description description
@@ -178,7 +184,7 @@ function initReleasesActions(store, router) {
isS3 isS3
} }
} }
poster: moviesPoster { poster: ${type === 'series' ? 'seriesPosterBySerieId' : 'moviesPoster'} {
media { media {
id id
path path
@@ -191,7 +197,7 @@ function initReleasesActions(store, router) {
isS3 isS3
} }
} }
covers: moviesCovers(orderBy: MEDIA_BY_MEDIA_ID__INDEX_ASC) { covers: ${type === 'series' ? 'seriesCoversBySerieId' : 'moviesCovers'}(orderBy: MEDIA_BY_MEDIA_ID__INDEX_ASC) {
media { media {
id id
path path
@@ -204,14 +210,14 @@ function initReleasesActions(store, router) {
isS3 isS3
} }
} }
trailer: moviesTrailer { trailer: ${type === 'series' ? 'seriesTrailerBySerieId' : 'moviesTrailer'} {
media { media {
id id
path path
isS3 isS3
} }
} }
scenes: moviesScenes { scenes: ${type === 'series' ? 'seriesScenesBySerieId' : 'moviesScenes'} {
scene { scene {
${releaseFields} ${releaseFields}
} }
@@ -221,25 +227,11 @@ function initReleasesActions(store, router) {
slug slug
name name
} }
photos { photos: ${type === 'series' ? 'seriesPhotosBySerieId' : 'moviesPhotos'} {
id ${mediaFragment}
index }
path scenesPhotos {
thumbnail ${mediaFields}
lazy
width
height
thumbnailWidth
thumbnailHeight
isS3
comment
sfw: sfwMedia {
id
thumbnail
lazy
path
comment
}
} }
entity { entity {
id id
@@ -255,7 +247,7 @@ function initReleasesActions(store, router) {
hasLogo hasLogo
} }
} }
stashes: stashesMovies( stashes: ${type === 'series' ? 'stashesSeriesBySerieId' : 'stashesMovies'}(
filter: { filter: {
stash: { stash: {
userId: { userId: {
@@ -279,12 +271,20 @@ function initReleasesActions(store, router) {
userId: store.state.auth.user?.id, userId: store.state.auth.user?.id,
}); });
if (!movie) { if (!result[type]) {
router.replace('/not-found'); router.replace('/not-found');
return null; return null;
} }
return curateRelease(movie); return curateRelease(result[type]);
}
async function fetchMovieById(context, movieId) {
return fetchCollectionById(context, movieId, 'movie');
}
async function fetchSerieById(context, serieId) {
return fetchCollectionById(context, serieId, 'series');
} }
return { return {
@@ -292,6 +292,7 @@ function initReleasesActions(store, router) {
fetchReleaseById, fetchReleaseById,
fetchMovies, fetchMovies,
fetchMovieById, fetchMovieById,
fetchSerieById,
searchMovies, searchMovies,
}; };
} }

View File

@@ -71,6 +71,11 @@ const routes = [
component: Release, component: Release,
name: 'movie', name: 'movie',
}, },
{
path: '/serie/:releaseId/:releaseSlug?',
component: Release,
name: 'serie',
},
{ {
path: '/actor/:actorId/:actorSlug', path: '/actor/:actorId/:actorSlug',
name: 'actor', name: 'actor',

View File

@@ -210,9 +210,15 @@ function initTagsActions(store, _router) {
query Tags( query Tags(
$slugs: [String!] = [], $slugs: [String!] = [],
$limit: Int = 100 $limit: Int = 100
$exclude: [String!]
) { ) {
tags( tags(
filter: { slug: { in: $slugs } }, filter: {
slug: {
in: $slugs
notIn: $exclude
}
},
first: $limit first: $limit
) { ) {
id id
@@ -257,6 +263,7 @@ function initTagsActions(store, _router) {
`, { `, {
slugs, slugs,
limit, limit,
exclude: store.state.ui.tagFilter,
}); });
return tags.map((tag) => curateTag(tag, store.state.ui.sfw)); return tags.map((tag) => curateTag(tag, store.state.ui.sfw));

View File

@@ -66,10 +66,7 @@ module.exports = {
// pornpros // pornpros
'milfhumiliation', 'milfhumiliation',
'humiliated', 'humiliated',
'flexiblepositions',
'publicviolations',
'amateurviolations', 'amateurviolations',
'squirtdisgrace',
'cumdisgrace', 'cumdisgrace',
'webcamhackers', 'webcamhackers',
'collegeteens', 'collegeteens',
@@ -301,10 +298,6 @@ module.exports = {
interval: 1000, interval: 1000,
concurrency: 1, concurrency: 1,
}, },
'www.realitykings.com': {
interval: 1000,
concurrency: 1,
},
'westcoastproductions.com': { 'westcoastproductions.com': {
interval: 100, interval: 100,
concurrency: 1, concurrency: 1,
@@ -323,6 +316,8 @@ module.exports = {
}, },
media: { media: {
path: './media', path: './media',
maxSize: 1000,
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,

View File

@@ -13,7 +13,12 @@ exports.up = async (knex) => knex.raw(`
) )
SELECT releases FROM releases SELECT releases FROM releases
INNER JOIN children ON children.id = releases.entity_id; 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; $$ LANGUAGE SQL STABLE;
COMMENT ON FUNCTION entities_scenes IS E'@sortable'; COMMENT ON FUNCTION entities_scenes IS E'@sortable';

View File

@@ -1,5 +1,5 @@
exports.up = async (knex) => knex.raw(` exports.up = async (knex) => knex.raw(`
CREATE OR REPLACE FUNCTION entities_scene_total(entity entities) RETURNS integer AS $$ CREATE OR REPLACE FUNCTION entities_scene_total(entity entities) RETURNS bigint AS $$
SELECT COUNT(id) SELECT COUNT(id)
FROM releases FROM releases
WHERE releases.entity_id = entity.id; WHERE releases.entity_id = entity.id;

View File

@@ -0,0 +1,23 @@
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;
`);

View File

@@ -0,0 +1,215 @@
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;
`));

View File

@@ -0,0 +1,49 @@
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;
`);

58
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "traxxx", "name": "traxxx",
"version": "1.205.11", "version": "1.212.9",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "traxxx", "name": "traxxx",
"version": "1.205.11", "version": "1.212.9",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@casl/ability": "^5.2.2", "@casl/ability": "^5.2.2",
@@ -57,6 +57,7 @@
"mitt": "^3.0.0", "mitt": "^3.0.0",
"moment": "^2.24.0", "moment": "^2.24.0",
"nanoid": "^3.1.30", "nanoid": "^3.1.30",
"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", "opn": "^6.0.0",
@@ -72,6 +73,7 @@
"tippy.js": "^6.3.1", "tippy.js": "^6.3.1",
"tough-cookie": "^4.0.0", "tough-cookie": "^4.0.0",
"tunnel": "0.0.6", "tunnel": "0.0.6",
"undici": "^4.13.0",
"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",
@@ -11611,14 +11613,41 @@
"integrity": "sha512-eazsqzwG2lskuzBqCGPi7Ac2UgOoMz8JVOXVhTvvPDYhthvNpefx8jWD8Np7Gv+2Sz0FlPWZk0nJV0z598Wn8Q==" "integrity": "sha512-eazsqzwG2lskuzBqCGPi7Ac2UgOoMz8JVOXVhTvvPDYhthvNpefx8jWD8Np7Gv+2Sz0FlPWZk0nJV0z598Wn8Q=="
}, },
"node_modules/node-fetch": { "node_modules/node-fetch": {
"version": "2.6.5", "version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"dependencies": { "dependencies": {
"whatwg-url": "^5.0.0" "whatwg-url": "^5.0.0"
}, },
"engines": { "engines": {
"node": "4.x || >=6.0.0" "node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/node-fetch/node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
},
"node_modules/node-fetch/node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
},
"node_modules/node-fetch/node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
} }
}, },
"node_modules/node-fetch/node_modules/tr46": { "node_modules/node-fetch/node_modules/tr46": {
@@ -16346,6 +16375,14 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/undici": {
"version": "4.13.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-4.13.0.tgz",
"integrity": "sha512-8lk8S/f2V0VUNGf2scU2b+KI2JSzEQLdCyRNRF3XmHu+5jectlSDaPSBCXAHFaUlt1rzngzOBVDgJS9/Gue/KA==",
"engines": {
"node": ">=12.18"
}
},
"node_modules/unicode-canonical-property-names-ecmascript": { "node_modules/unicode-canonical-property-names-ecmascript": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
@@ -26407,9 +26444,9 @@
"integrity": "sha512-eazsqzwG2lskuzBqCGPi7Ac2UgOoMz8JVOXVhTvvPDYhthvNpefx8jWD8Np7Gv+2Sz0FlPWZk0nJV0z598Wn8Q==" "integrity": "sha512-eazsqzwG2lskuzBqCGPi7Ac2UgOoMz8JVOXVhTvvPDYhthvNpefx8jWD8Np7Gv+2Sz0FlPWZk0nJV0z598Wn8Q=="
}, },
"node-fetch": { "node-fetch": {
"version": "2.6.5", "version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"requires": { "requires": {
"whatwg-url": "^5.0.0" "whatwg-url": "^5.0.0"
}, },
@@ -30068,6 +30105,11 @@
"resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
"integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=" "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo="
}, },
"undici": {
"version": "4.13.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-4.13.0.tgz",
"integrity": "sha512-8lk8S/f2V0VUNGf2scU2b+KI2JSzEQLdCyRNRF3XmHu+5jectlSDaPSBCXAHFaUlt1rzngzOBVDgJS9/Gue/KA=="
},
"unicode-canonical-property-names-ecmascript": { "unicode-canonical-property-names-ecmascript": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",

View File

@@ -1,6 +1,6 @@
{ {
"name": "traxxx", "name": "traxxx",
"version": "1.205.11", "version": "1.212.9",
"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": {
@@ -116,6 +116,7 @@
"mitt": "^3.0.0", "mitt": "^3.0.0",
"moment": "^2.24.0", "moment": "^2.24.0",
"nanoid": "^3.1.30", "nanoid": "^3.1.30",
"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", "opn": "^6.0.0",
@@ -131,6 +132,7 @@
"tippy.js": "^6.3.1", "tippy.js": "^6.3.1",
"tough-cookie": "^4.0.0", "tough-cookie": "^4.0.0",
"tunnel": "0.0.6", "tunnel": "0.0.6",
"undici": "^4.13.0",
"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",

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Some files were not shown because too many files have changed in this diff Show More