Merge branch 'experimental'
51
README.md
|
@ -18,12 +18,44 @@ Do not modify `config/default.js`, but instead create a copy at `config/local.js
|
|||
|
||||
You can also use `npm run flush` to run both steps at once, and wipe the database completely later.
|
||||
|
||||
#### Networks and channels
|
||||
To scrape the networks and channels available in the database, you can configure `include` and `exclude` lists. To include all available channels and only use the `exclude` list, leave the `include` parameter unconfigured. The `exclude` lists will exclude channels and child networks from networks on the `include` lists, but not vice versa. That is, if the `include` list includes a network and the `exclude` list excludes one of that network's channels, the channel will not be scraped. However, if the `include` list includes a channel, and the `exclude` list includes its parent network, the channel will be scraped.
|
||||
|
||||
This configuration will scrape Evil Angel and all XEmpire channels, except for LesbianX.
|
||||
```
|
||||
include: {
|
||||
networks: [
|
||||
'xempire',
|
||||
],
|
||||
channels: [
|
||||
'evilangel',
|
||||
],
|
||||
},
|
||||
exclude: {
|
||||
channels: [
|
||||
'lesbianx',
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
This configuration will scrape all channels, except for BAM Visions, and except all channels part of the Vixen network.
|
||||
```
|
||||
exclude: {
|
||||
channels: [
|
||||
'bamvisions',
|
||||
],
|
||||
networks: [
|
||||
'vixen'
|
||||
],
|
||||
},
|
||||
```
|
||||
|
||||
### Building
|
||||
To build traxxx, run the following command:
|
||||
|
||||
`npm run build`
|
||||
|
||||
To generate the thumbnails for logos and tag photos, run:
|
||||
To generate thumbnails for logos and tag photos, install ImageMagick and run:
|
||||
|
||||
`npm run logos-thumbs`
|
||||
|
||||
|
@ -33,13 +65,24 @@ To generate the thumbnails for logos and tag photos, run:
|
|||
`./traxxx --option value` or `npm start -- --option value`
|
||||
|
||||
* `--server`: Run the web server
|
||||
* `--all`: Fetch updates from the channels and networks in the configuration file.
|
||||
* `--channel [slug] [slug]`: Fetch updates from specific channels. The slug is the channel's name in lowercase and without cases or special characters. For example, Teens Like It Big is teenslikeitbig.
|
||||
* `--network [slug] [slug]`: Fetch updates from all sites of a specific network. The network slug is composed similarly to the channel slug.
|
||||
|
||||
#### Channels
|
||||
* `--channels [slug] [slug]`: Fetch updates from specific channels. The slug is the channel's name in lowercase and without cases or special characters. For example, Teens Like It Big is teenslikeitbig. Overrides configured included networks and channels.
|
||||
* `--networks [slug] [slug]`: Fetch updates from all sites of a specific network. The network slug is composed similarly to the channel slug. Overrides configured included networks and channels.
|
||||
* `--exclude-channels [slug] [slug]`: Scrape every configured, specified or available channel, except for specified. Overrides configured excluded channels.
|
||||
* `--exclude-networks [slug] [slug]`: Scrape every configured, specified or available network, except for specified. Overrides configured excluded networks.
|
||||
* `--after "[time]"`: Do not fetch scenes older than this period or date. Example values are: `"1 month"`, `"3 years"`, `"2019-01-01"`.
|
||||
* `--scene [URL]`: Try to retrieve scene details from its official channel or network URL.
|
||||
* `--deep`: Follow each release link found running `--channel` or `--network` and scrape it for more details. Enabled by default ; use `--no-deep` to only save information found on the overview pages.
|
||||
|
||||
#### Actors
|
||||
* `--actors "[name]" "[name]"`: Fetch actor profiles. When no names are specified, actors without existing profiles are scraped
|
||||
* `--actors-file [filepath]`: Fetch all scenes for the actors specified in a file using a newline delimiter.
|
||||
* `--actors-sources [slug] [slug]`: Scrapers to use for actor profiles. Defaults to config.
|
||||
* `--actors-update [time]`: Update actors that don't have any profiles newer than period ("1 month") or date (2020-08-01). Using this argument without a value will default to 1900-01-01, practically updating all actors.
|
||||
* `--actors-scenes`: Fetch all scenes for scraped actors. Use with caution, as an actor may have many scenes.
|
||||
* `--scene-actors`: Fetch profiles for actors associated with scraped scenes. Use with caution, as scenes may have many actors, each with many profiles.
|
||||
|
||||
#### Developers
|
||||
* `--no-save`: Do not store retrieved information in local database, forcing re-fetch.
|
||||
* `--level`: Change log level to `silly`, `verbose`, `info`, `warn` or `error`.
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
/>
|
||||
</div>
|
||||
|
||||
<div class="actor-inner">
|
||||
<div class="content-inner actor-inner">
|
||||
<div
|
||||
class="profile"
|
||||
:class="{ expanded: bioExpanded, 'with-avatar': !!actor.avatar }"
|
||||
|
@ -296,6 +296,7 @@
|
|||
:items-total="totalCount"
|
||||
:items-per-page="limit"
|
||||
:available-tags="actor.tags"
|
||||
:available-channels="actor.channels"
|
||||
/>
|
||||
|
||||
<Releases :releases="releases" />
|
||||
|
@ -306,13 +307,15 @@
|
|||
class="pagination-top"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Pagination from '../pagination/pagination.vue';
|
||||
import FilterBar from '../header/filter-bar.vue';
|
||||
import FilterBar from '../filters/filter-bar.vue';
|
||||
import Releases from '../releases/releases.vue';
|
||||
import Photos from './photos.vue';
|
||||
import Expand from '../expand/expand.vue';
|
||||
|
@ -402,7 +405,7 @@ export default {
|
|||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: var(--highlight-extreme);
|
||||
color: var(--lighten-extreme);
|
||||
background: var(--profile);
|
||||
padding: .75rem 1rem;
|
||||
}
|
||||
|
@ -429,7 +432,7 @@ export default {
|
|||
|
||||
.profile {
|
||||
background: var(--profile);
|
||||
color: var(--highlight-extreme);
|
||||
color: var(--lighten-extreme);
|
||||
width: 100%;
|
||||
max-height: 18rem;
|
||||
display: flex;
|
||||
|
@ -486,7 +489,7 @@ export default {
|
|||
overflow: hidden;
|
||||
|
||||
&:not(:last-of-type) {
|
||||
border-bottom: solid 1px var(--highlight-hint);
|
||||
border-bottom: solid 1px var(--lighten-hint);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -497,14 +500,14 @@ export default {
|
|||
}
|
||||
|
||||
.bio-label {
|
||||
color: var(--highlight);
|
||||
color: var(--lighten);
|
||||
margin: 0 1rem 0 0;
|
||||
flex-shrink: 0;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
|
||||
.icon {
|
||||
fill: var(--highlight);
|
||||
fill: var(--lighten);
|
||||
margin: -.25rem .5rem 0 0;
|
||||
}
|
||||
}
|
||||
|
@ -538,7 +541,7 @@ export default {
|
|||
.age {
|
||||
font-weight: bold;
|
||||
padding: 0 0 0 .5rem;
|
||||
border-left: solid 1px var(--highlight-weak);
|
||||
border-left: solid 1px var(--lighten-weak);
|
||||
margin: 0 0 0 .5rem;
|
||||
}
|
||||
|
||||
|
@ -554,7 +557,7 @@ export default {
|
|||
.height-imperial,
|
||||
.weight-imperial {
|
||||
padding: 0 0 0 .5rem;
|
||||
border-left: solid 1px var(--highlight-weak);
|
||||
border-left: solid 1px var(--lighten-weak);
|
||||
margin: 0 0 0 .5rem;
|
||||
}
|
||||
|
||||
|
@ -571,7 +574,7 @@ export default {
|
|||
}
|
||||
|
||||
.scraped {
|
||||
color: var(--highlight-weak);
|
||||
color: var(--lighten-weak);
|
||||
font-size: .8rem;
|
||||
}
|
||||
|
||||
|
@ -713,8 +716,13 @@ export default {
|
|||
margin: 1rem 0 0 0;
|
||||
}
|
||||
|
||||
.actor-header {
|
||||
padding: .5rem 1rem;
|
||||
}
|
||||
|
||||
.header-name {
|
||||
flex-grow: 1;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -2,6 +2,13 @@
|
|||
<div class="actors">
|
||||
<nav class="filter">
|
||||
<ul class="genders nolist">
|
||||
<li class="gender">
|
||||
<router-link
|
||||
:to="{ name: 'actors', params: { gender: 'all', letter, pageNumber: 1 } }"
|
||||
:class="{ selected: gender === 'all' }"
|
||||
class="gender-link all"
|
||||
>all</router-link>
|
||||
</li>
|
||||
<li class="gender">
|
||||
<router-link
|
||||
:to="{ name: 'actors', params: { gender: 'female', letter, pageNumber: 1 } }"
|
||||
|
@ -33,13 +40,6 @@
|
|||
replace
|
||||
><Icon icon="question5" /></router-link>
|
||||
</li>
|
||||
<li class="gender">
|
||||
<router-link
|
||||
:to="{ name: 'actors', params: { gender: 'all', letter, pageNumber: 1 } }"
|
||||
:class="{ selected: gender === 'all' }"
|
||||
class="gender-link all"
|
||||
>all</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="letters nolist">
|
||||
|
@ -80,6 +80,8 @@
|
|||
:items-per-page="limit"
|
||||
class="pagination-top"
|
||||
/>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -107,7 +109,7 @@ function letter() {
|
|||
}
|
||||
|
||||
function gender() {
|
||||
return this.$route.params.gender || 'female';
|
||||
return this.$route.params.gender || 'all';
|
||||
}
|
||||
|
||||
async function route() {
|
||||
|
|
|
@ -3,10 +3,12 @@
|
|||
class="container"
|
||||
:class="theme"
|
||||
>
|
||||
<transition name="slide">
|
||||
<Sidebar
|
||||
v-if="showSidebar"
|
||||
:toggle-sidebar="toggleSidebar"
|
||||
@toggle="(state) => showSidebar = state"
|
||||
/>
|
||||
</transition>
|
||||
|
||||
<Header :toggle-sidebar="toggleSidebar" />
|
||||
|
||||
|
@ -36,7 +38,6 @@ function toggleSidebar(state) {
|
|||
function mounted() {
|
||||
document.addEventListener('click', () => {
|
||||
EventBus.$emit('blur');
|
||||
this.showSidebar = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -88,4 +89,34 @@ export default {
|
|||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.slide-enter-active,
|
||||
.slide-leave-active {
|
||||
&.sidebar-container {
|
||||
transition: background .2s ease-in-out;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
transition: transform .2s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
.slide-enter,
|
||||
.slide-leave-to {
|
||||
&.sidebar-container {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
transform: translate(-100%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.column {
|
||||
width: 1200px;
|
||||
max-width: 100%;
|
||||
padding: 0 1rem;
|
||||
margin: 0 auto;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -35,7 +35,10 @@
|
|||
class="name"
|
||||
>{{ entity.name }}</h2>
|
||||
|
||||
<Icon icon="share2" />
|
||||
<Icon
|
||||
v-if="entity.url"
|
||||
icon="share2"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<ul
|
||||
|
@ -103,6 +106,8 @@
|
|||
class="pagination-top"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -110,7 +115,7 @@
|
|||
<script>
|
||||
import Vue from 'vue';
|
||||
|
||||
import FilterBar from '../header/filter-bar.vue';
|
||||
import FilterBar from '../filters/filter-bar.vue';
|
||||
import Pagination from '../pagination/pagination.vue';
|
||||
import Releases from '../releases/releases.vue';
|
||||
import Children from './children.vue';
|
||||
|
@ -156,7 +161,7 @@ export default {
|
|||
return {
|
||||
entity: null,
|
||||
totalCount: null,
|
||||
limit: 20,
|
||||
limit: Number(this.$route.query.limit) || 20,
|
||||
expanded: false,
|
||||
};
|
||||
},
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
<template>
|
||||
<v-popover class="filter-container">
|
||||
<div class="filter">
|
||||
<Icon icon="antenna" />
|
||||
|
||||
<div
|
||||
v-if="selectedChannels.length > 0"
|
||||
class="filter-applied"
|
||||
>{{ selectedChannels.length }} {{ selectedChannels.length > 1 ? 'channels' : 'channel' }}</div>
|
||||
|
||||
<div
|
||||
v-else
|
||||
class="filter-applied empty"
|
||||
>Channels</div>
|
||||
</div>
|
||||
|
||||
<div slot="popover">
|
||||
<router-link
|
||||
class="filter-clear"
|
||||
:to="{ query: { ...$route.query, channels: undefined } }"
|
||||
:class="{ active: selectedChannels.length > 0 }"
|
||||
>clear all<Icon icon="cross2" /></router-link>
|
||||
|
||||
<ul class="filter-items nolist">
|
||||
<li
|
||||
v-for="channel in channelsPerNetwork"
|
||||
:key="`channel-${channel.id}`"
|
||||
class="filter-item"
|
||||
:class="{ [channel.type]: true, independent: channel.independent }"
|
||||
>
|
||||
<router-link
|
||||
:to="{ query: { ...$route.query, channels: channel.slug, mode }, params: { pageNumber: 1 } }"
|
||||
class="filter-name"
|
||||
>
|
||||
<img
|
||||
v-if="channel.independent || !channel.parent"
|
||||
:src="`/img/logos/${channel.slug}/favicon.png`"
|
||||
class="favicon"
|
||||
>
|
||||
|
||||
{{ channel.name }}
|
||||
</router-link>
|
||||
|
||||
<router-link
|
||||
:to="{ query: { ...$route.query, ...getNewRange(channel.slug), mode }, params: { pageNumber: 1 } }"
|
||||
class="filter-include"
|
||||
:class="{ selected: selectedChannels.includes(channel.slug) }"
|
||||
>
|
||||
<Icon
|
||||
icon="checkmark"
|
||||
class="filter-add"
|
||||
/>
|
||||
|
||||
<Icon
|
||||
icon="cross2"
|
||||
class="filter-remove"
|
||||
/>
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</v-popover>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
function getNewRange(channel) {
|
||||
if (this.selectedChannels.includes(channel)) {
|
||||
return { channels: this.selectedChannels.filter(selectedTag => selectedTag !== channel).join(',') || undefined };
|
||||
}
|
||||
|
||||
return { channels: this.selectedChannels.concat(channel).join(',') };
|
||||
}
|
||||
|
||||
function selectedChannels() {
|
||||
return this.$route.query.channels ? this.$route.query.channels.split(',') : [];
|
||||
}
|
||||
|
||||
function channelsPerNetwork() {
|
||||
const networks = this.availableChannels.reduce((acc, channel) => {
|
||||
if (channel.independent || !channel.parent) {
|
||||
acc[channel.slug] = { ...channel, children: [] };
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (!acc[channel.parent.slug]) {
|
||||
acc[channel.parent.slug] = { ...channel.parent, children: [] };
|
||||
}
|
||||
|
||||
acc[channel.parent.slug].children.push(channel);
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return Object.values(networks).reduce((acc, network) => [...acc, network, ...(network.children || [])], []);
|
||||
}
|
||||
|
||||
export default {
|
||||
props: {
|
||||
filter: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
compact: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
availableChannels: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
mode: this.$route.query.mode || 'all',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
channelsPerNetwork,
|
||||
selectedChannels,
|
||||
},
|
||||
methods: {
|
||||
getNewRange,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.favicon {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
padding: 0 .75rem 0 0;
|
||||
filter: drop-shadow(0 0 1px var(--darken));
|
||||
}
|
||||
|
||||
.network .filter-name,
|
||||
.independent .filter-name {
|
||||
font-weight: bold;
|
||||
padding: .5rem;
|
||||
}
|
||||
|
||||
.channel:not(.independent) .filter-name {
|
||||
padding: .5rem .5rem .5rem 2.25rem;
|
||||
}
|
||||
</style>
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="filter-bar noselect">
|
||||
<span class="sort">
|
||||
<div class="sort">
|
||||
<router-link
|
||||
:to="{ params: { range: 'latest', pageNumber: 1 } }"
|
||||
:class="{ active: $route.name === 'latest' || range === 'latest' }"
|
||||
|
@ -18,20 +18,28 @@
|
|||
:class="{ active: $route.name === 'new' || range === 'new' }"
|
||||
class="range-button"
|
||||
>New</router-link>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Filters
|
||||
class="filters"
|
||||
<div class="filters">
|
||||
<ChannelFilter
|
||||
class="filters-filter"
|
||||
:filter="filter"
|
||||
:available-channels="availableChannels"
|
||||
/>
|
||||
|
||||
<TagFilter
|
||||
class="filters-filter"
|
||||
:filter="filter"
|
||||
:available-tags="availableTags"
|
||||
@set-filter="setFilter"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
import Filters from './filters.vue';
|
||||
import ChannelFilter from './channel-filter.vue';
|
||||
import TagFilter from './tag-filter.vue';
|
||||
|
||||
function filter(state) {
|
||||
return state.ui.filter;
|
||||
|
@ -45,12 +53,6 @@ function batch(state) {
|
|||
return state.ui.batch;
|
||||
}
|
||||
|
||||
async function setFilter(newFilter) {
|
||||
this.$store.dispatch('setFilter', newFilter);
|
||||
|
||||
await this.fetchReleases();
|
||||
}
|
||||
|
||||
async function setRange(newRange) {
|
||||
this.$store.dispatch('setRange', newRange);
|
||||
|
||||
|
@ -65,7 +67,8 @@ async function setBatch(newBatch) {
|
|||
|
||||
export default {
|
||||
components: {
|
||||
Filters,
|
||||
ChannelFilter,
|
||||
TagFilter,
|
||||
},
|
||||
props: {
|
||||
fetchReleases: {
|
||||
|
@ -88,6 +91,10 @@ export default {
|
|||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
availableChannels: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
|
@ -97,13 +104,164 @@ export default {
|
|||
}),
|
||||
},
|
||||
methods: {
|
||||
setFilter,
|
||||
setRange,
|
||||
setBatch,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import 'breakpoints';
|
||||
|
||||
.filter {
|
||||
color: var(--shadow);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
fill: var(--shadow);
|
||||
margin: -.1rem 0 0 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
|
||||
.applied {
|
||||
color: var(--shadow-strong);
|
||||
}
|
||||
|
||||
.icon {
|
||||
fill: var(--shadow-strong);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
}
|
||||
|
||||
.filter-mode {
|
||||
width: 100%;
|
||||
background: none;
|
||||
padding: .75rem;
|
||||
margin: 0 0 .5rem 0;
|
||||
font-size: 1rem;
|
||||
border: none;
|
||||
border-bottom: solid 1px var(--shadow-hint);
|
||||
}
|
||||
|
||||
.filter-clear {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: .5rem 1rem;
|
||||
color: var(--darken-weak);
|
||||
text-decoration: none;
|
||||
cursor: default;
|
||||
|
||||
.icon {
|
||||
fill: var(--darken-hint);
|
||||
margin: 0 0 0 1rem;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: var(--darken);
|
||||
|
||||
.icon {
|
||||
fill: var(--darken-weak);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--text);
|
||||
background: var(--darken-hint);
|
||||
cursor: pointer;
|
||||
|
||||
.icon {
|
||||
fill: var(--alert);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-items .filter-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:hover {
|
||||
background: var(--darken-hint);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.selected .filter-include .filter-add {
|
||||
fill: var(--success);
|
||||
}
|
||||
}
|
||||
|
||||
.filter-include {
|
||||
.icon {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
padding: .5rem 1rem;
|
||||
fill: var(--darken-hint);
|
||||
}
|
||||
|
||||
.filter-remove {
|
||||
display: none;
|
||||
fill: var(--alert);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
|
||||
&.selected .filter-add {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.selected .filter-remove {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-name {
|
||||
min-width: 8rem;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
padding: .5rem .75rem .5rem 1rem;
|
||||
color: var(--text);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.filter-include:hover,
|
||||
.filter-name:hover {
|
||||
background: var(--darken-hint);
|
||||
}
|
||||
|
||||
@media(max-width: $breakpoint-micro) {
|
||||
.filter-applied {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.filters-filter:not(:last-child) .filter {
|
||||
padding: .5rem;
|
||||
}
|
||||
|
||||
.filters-filter:last-child .filter {
|
||||
padding: .5rem 0 .5rem .5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import 'theme';
|
||||
|
||||
|
@ -162,8 +320,12 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
.filters {
|
||||
.filters-filter {
|
||||
display: inline-block;
|
||||
flex-shrink: 0;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin: 0 1rem 0 0;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,111 @@
|
|||
<template>
|
||||
<v-popover class="filter-container">
|
||||
<div class="filter">
|
||||
<Icon icon="price-tag4" />
|
||||
|
||||
<div
|
||||
v-if="selectedTags.length > 0"
|
||||
class="filter-applied"
|
||||
>{{ selectedTags.length }} {{ selectedTags.length > 1 ? 'tags' : 'tag' }}</div>
|
||||
|
||||
<div
|
||||
v-else
|
||||
class="filter-applied empty"
|
||||
>Tags</div>
|
||||
</div>
|
||||
|
||||
<div slot="popover">
|
||||
<select
|
||||
v-model="mode"
|
||||
class="filter-mode"
|
||||
@change="$router.push({ query: { ...$route.query, mode }, params: { pageNumber: 1 } })"
|
||||
>
|
||||
<option value="all">match all selected</option>
|
||||
<option value="any">match any selected</option>
|
||||
</select>
|
||||
|
||||
<router-link
|
||||
class="filter-clear"
|
||||
:to="{ query: { ...$route.query, tags: undefined, mode: undefined } }"
|
||||
:class="{ active: selectedTags.length > 0 }"
|
||||
>clear all<Icon icon="cross2" /></router-link>
|
||||
|
||||
<ul class="filter-items nolist">
|
||||
<li
|
||||
v-for="tag in availableTags"
|
||||
:key="`tag-${tag.id}`"
|
||||
class="filter-item"
|
||||
:class="{ selected: selectedTags.includes(tag.slug) }"
|
||||
>
|
||||
<router-link
|
||||
:to="{ query: { ...$route.query, tags: tag.slug, mode }, params: { pageNumber: 1 } }"
|
||||
class="filter-name"
|
||||
>{{ tag.name }}</router-link>
|
||||
|
||||
<router-link
|
||||
:to="{ query: { ...$route.query, ...getNewRange(tag.slug), mode }, params: { pageNumber: 1 } }"
|
||||
class="filter-include"
|
||||
:class="{ selected: selectedTags.includes(tag.slug) }"
|
||||
>
|
||||
<Icon
|
||||
icon="checkmark"
|
||||
class="filter-add"
|
||||
/>
|
||||
|
||||
<Icon
|
||||
icon="cross2"
|
||||
class="filter-remove"
|
||||
/>
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</v-popover>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
function getNewRange(tag) {
|
||||
if (this.selectedTags.includes(tag)) {
|
||||
return { tags: this.selectedTags.filter(selectedTag => selectedTag !== tag).join(',') || undefined };
|
||||
}
|
||||
|
||||
return { tags: this.selectedTags.concat(tag).join(',') };
|
||||
}
|
||||
|
||||
function selectedTags() {
|
||||
return this.$route.query.tags ? this.$route.query.tags.split(',') : [];
|
||||
}
|
||||
|
||||
export default {
|
||||
props: {
|
||||
filter: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
compact: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
availableTags: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
mode: this.$route.query.mode || 'all',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
selectedTags,
|
||||
},
|
||||
methods: {
|
||||
getNewRange,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import 'theme';
|
||||
|
||||
</style>
|
|
@ -0,0 +1,34 @@
|
|||
<template>
|
||||
<footer class="footer">
|
||||
<span class="segment">© traxxx</span>
|
||||
|
||||
<router-link
|
||||
:to="{ name: 'stats' }"
|
||||
class="segment footer-link nolink"
|
||||
>stats</router-link>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.footer {
|
||||
margin: 2rem 0 0 0;
|
||||
background: var(--background);
|
||||
color: var(--shadow);
|
||||
box-shadow: inset 0 1px 3px var(--darken-hint);
|
||||
font-size: .8rem;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.segment {
|
||||
padding: .5rem;
|
||||
|
||||
&:not(:last-child) {
|
||||
border-right: solid 1px var(--shadow-hint);
|
||||
}
|
||||
}
|
||||
|
||||
.footer-link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
|
@ -1,200 +0,0 @@
|
|||
<template>
|
||||
<v-popover class="filters-container">
|
||||
<div class="filters">
|
||||
<div
|
||||
v-if="selectedTags.length > 0"
|
||||
class="applied"
|
||||
>{{ selectedTags.length }} selected</div>
|
||||
|
||||
<div
|
||||
v-else
|
||||
class="applied empty"
|
||||
>Filter by tags</div>
|
||||
|
||||
<Icon icon="filter" />
|
||||
</div>
|
||||
|
||||
<div slot="popover">
|
||||
<select
|
||||
v-model="mode"
|
||||
class="mode"
|
||||
@change="$router.push({ query: { ...$route.query, mode }, params: { pageNumber: 1 } })"
|
||||
>
|
||||
<option
|
||||
value="all"
|
||||
class="option"
|
||||
>match all selected</option>
|
||||
<option
|
||||
value="any"
|
||||
class="option"
|
||||
>match any selected</option>
|
||||
</select>
|
||||
|
||||
<ul class="tags nolist">
|
||||
<li
|
||||
v-for="tag in availableTags"
|
||||
:key="`tag-${tag.id}`"
|
||||
class="tag"
|
||||
:class="{ selected: selectedTags.includes(tag.slug) }"
|
||||
>
|
||||
<router-link :to="{ query: { ...getNewRange(tag.slug), mode }, params: { pageNumber: 1 } }">
|
||||
<Icon
|
||||
icon="checkmark"
|
||||
class="include"
|
||||
/>
|
||||
</router-link>
|
||||
|
||||
<router-link
|
||||
:to="{ query: { ...(selectedTags.length === 1 && selectedTags.includes(tag.slug) ? null : { tags: tag.slug }), mode }, params: { pageNumber: 1 } }"
|
||||
class="name"
|
||||
>{{ tag.name }}</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</v-popover>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
function getNewRange(tag) {
|
||||
if (this.selectedTags.includes(tag)) {
|
||||
if (this.selectedTags.length > 1) {
|
||||
return { tags: this.selectedTags.filter(selectedTag => selectedTag !== tag).join(',') };
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
return { tags: this.selectedTags.concat(tag).join(',') };
|
||||
}
|
||||
|
||||
function selectedTags() {
|
||||
return this.$route.query.tags ? this.$route.query.tags.split(',') : [];
|
||||
}
|
||||
|
||||
export default {
|
||||
props: {
|
||||
filter: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
compact: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
availableTags: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
mode: this.$route.query.mode || 'all',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
selectedTags,
|
||||
},
|
||||
methods: {
|
||||
getNewRange,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import 'theme';
|
||||
|
||||
.filters {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
fill: var(--shadow);
|
||||
margin: 0 .5rem 0 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
|
||||
.applied {
|
||||
color: var(--shadow-strong);
|
||||
}
|
||||
|
||||
.icon {
|
||||
fill: var(--shadow-strong);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
}
|
||||
|
||||
.mode {
|
||||
width: 100%;
|
||||
background: none;
|
||||
padding: .75rem;
|
||||
font-size: 1rem;
|
||||
border: none;
|
||||
border-bottom: solid 1px var(--shadow-hint);
|
||||
}
|
||||
|
||||
.tags {
|
||||
padding: .5rem 0;
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.include {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
padding: .5rem .75rem .5rem 1rem;
|
||||
fill: var(--darken-hint);
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
min-width: 8rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-grow: 1;
|
||||
padding: .5rem 1rem .5rem .75rem;
|
||||
color: var(--text);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--darken-hint);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.include:hover,
|
||||
.name:hover {
|
||||
background: var(--darken-hint);
|
||||
}
|
||||
|
||||
&.selected .include {
|
||||
fill: var(--success);
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-width: $breakpoint0) {
|
||||
.applied {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -2,15 +2,9 @@
|
|||
<header class="header">
|
||||
<div class="header-nav">
|
||||
<div
|
||||
class="sidebar-toggle"
|
||||
class="sidebar-toggle noselect"
|
||||
@click.stop="toggleSidebar"
|
||||
>
|
||||
<Icon icon="menu" />
|
||||
<div
|
||||
class="logo"
|
||||
v-html="logo"
|
||||
/>
|
||||
</div>
|
||||
><Icon icon="menu" /></div>
|
||||
|
||||
<router-link
|
||||
to="/"
|
||||
|
@ -52,6 +46,20 @@
|
|||
</router-link>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<router-link
|
||||
v-slot="{ href, isActive, navigate }"
|
||||
to="/movies"
|
||||
>
|
||||
<a
|
||||
class="nav-link"
|
||||
:href="href"
|
||||
:class="{ active: isActive }"
|
||||
@click="navigate"
|
||||
>Movies</a>
|
||||
</router-link>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<router-link
|
||||
v-slot="{ href, isActive, navigate }"
|
||||
|
@ -82,7 +90,7 @@
|
|||
<Icon
|
||||
v-show="sfw"
|
||||
v-tooltip="'Hit N to disable safe mode'"
|
||||
icon="evil2"
|
||||
icon="fire"
|
||||
class="toggle noselect"
|
||||
@click.native="setSfw(false)"
|
||||
/>
|
||||
|
@ -215,11 +223,6 @@ export default {
|
|||
align-items: center;
|
||||
height: 100%;
|
||||
|
||||
.logo {
|
||||
fill: var(--primary);
|
||||
padding: .5rem;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: inline-block;
|
||||
fill: var(--shadow-modest);
|
||||
|
@ -353,15 +356,18 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
@media(max-width: $breakpoint-micro) {
|
||||
.nav,
|
||||
.header-logo {
|
||||
@media(max-width: $breakpoint) {
|
||||
.nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar-toggle {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.header-logo {
|
||||
padding: 0 0 0 .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-width: $breakpoint-nano) {
|
||||
|
|
|
@ -20,12 +20,14 @@
|
|||
:items-per-page="limit"
|
||||
class="pagination-bottom"
|
||||
/>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FilterBar from '../header/filter-bar.vue';
|
||||
import FilterBar from '../filters/filter-bar.vue';
|
||||
import Releases from '../releases/releases.vue';
|
||||
import Pagination from '../pagination/pagination.vue';
|
||||
|
||||
|
|
|
@ -40,6 +40,8 @@
|
|||
:entity="entity"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -9,12 +9,12 @@
|
|||
>
|
||||
<router-link
|
||||
class="pagination-button cursor"
|
||||
:to="{ params: { pageNumber: 1 } }"
|
||||
:to="{ params: { pageNumber: 1 }, query: $route.query }"
|
||||
><Icon icon="first2" /></router-link>
|
||||
|
||||
<router-link
|
||||
class="pagination-button cursor"
|
||||
:to="{ params: { pageNumber: pageNumber - 1 } }"
|
||||
:to="{ params: { pageNumber: pageNumber - 1 }, query: $route.query }"
|
||||
><Icon icon="arrow-left" /></router-link>
|
||||
</span>
|
||||
|
||||
|
@ -30,14 +30,14 @@
|
|||
<router-link
|
||||
v-for="pageX in pageNumber - 1"
|
||||
:key="`page-${pageX}`"
|
||||
:to="{ params: { pageNumber: pageNumber - pageX } }"
|
||||
:to="{ params: { pageNumber: pageNumber - pageX }, query: $route.query }"
|
||||
class="pagination-button page"
|
||||
> {{ pageNumber - pageX }} </router-link>
|
||||
</span>
|
||||
|
||||
<router-link
|
||||
:key="`page-${pageNumber}`"
|
||||
:to="{ params: { pageNumber } }"
|
||||
:to="{ params: { pageNumber }, query: $route.query }"
|
||||
class="pagination-button page active"
|
||||
> {{ pageNumber }} </router-link>
|
||||
|
||||
|
@ -45,7 +45,7 @@
|
|||
<router-link
|
||||
v-for="pageX in (pageCount - pageNumber)"
|
||||
:key="`page-${pageX + pageNumber}`"
|
||||
:to="{ params: { pageNumber: pageX + pageNumber } }"
|
||||
:to="{ params: { pageNumber: pageX + pageNumber }, query: $route.query }"
|
||||
class="pagination-button page"
|
||||
> {{ pageX + pageNumber }} </router-link>
|
||||
</span>
|
||||
|
@ -56,12 +56,12 @@
|
|||
>
|
||||
<router-link
|
||||
class="pagination-button cursor"
|
||||
:to="{ params: { pageNumber: pageNumber + 1 } }"
|
||||
:to="{ params: { pageNumber: pageNumber + 1 }, query: $route.query }"
|
||||
><Icon icon="arrow-right" /></router-link>
|
||||
|
||||
<router-link
|
||||
class="pagination-button cursor"
|
||||
:to="{ params: { pageNumber: pageCount } }"
|
||||
:to="{ params: { pageNumber: pageCount }, query: $route.query }"
|
||||
><Icon icon="last2" /></router-link>
|
||||
</span>
|
||||
|
||||
|
|
|
@ -0,0 +1,223 @@
|
|||
<template>
|
||||
<div class="details">
|
||||
<div class="column">
|
||||
<div class="tidbits">
|
||||
<a
|
||||
v-if="release.date"
|
||||
:title="release.url && `View scene on ${release.entity.name}`"
|
||||
:href="release.url"
|
||||
:class="{ link: release.url }"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="tidbit date nolink"
|
||||
>
|
||||
<span class="date-compact">{{ formatDate(release.date, 'MMM D, YYYY', release.datePrecision) }}</span>
|
||||
<span class="date-full">{{ formatDate(release.date, 'MMMM D, YYYY', release.datePrecision) }}</span>
|
||||
|
||||
<Icon
|
||||
v-if="release.url"
|
||||
icon="share2"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="site">
|
||||
<template v-if="release.entity.parent && !release.entity.independent">
|
||||
<a
|
||||
v-if="release.entity.parent.hasLogo"
|
||||
:href="`/network/${release.entity.parent.slug}`"
|
||||
class="logo-link"
|
||||
>
|
||||
<img
|
||||
:src="`/img/logos/${release.entity.parent.slug}/thumbs/network.png`"
|
||||
:title="release.entity.parent.name"
|
||||
:alt="release.entity.parent.name"
|
||||
class="logo logo-parent"
|
||||
>
|
||||
</a>
|
||||
|
||||
<a
|
||||
v-else
|
||||
:href="`/network/${release.entity.parent.slug}`"
|
||||
class="logo-link logo-name"
|
||||
>{{ release.entity.parent.name }}</a>
|
||||
|
||||
<span class="chain">presents</span>
|
||||
|
||||
<a
|
||||
v-if="release.entity.hasLogo"
|
||||
:href="`/${release.entity.type}/${release.entity.slug}`"
|
||||
class="logo-link"
|
||||
>
|
||||
<img
|
||||
v-if="release.entity.type === 'network'"
|
||||
:src="`/img/logos/${release.entity.slug}/thumbs/network.png`"
|
||||
:title="release.entity.name"
|
||||
class="logo logo-site"
|
||||
>
|
||||
|
||||
<img
|
||||
v-else
|
||||
:src="`/img/logos/${release.entity.parent.slug}/thumbs/${release.entity.slug}.png`"
|
||||
:title="release.entity.name"
|
||||
class="logo logo-site"
|
||||
>
|
||||
</a>
|
||||
|
||||
<a
|
||||
v-else
|
||||
:href="`/${release.entity.type}/${release.entity.slug}`"
|
||||
class="logo-link logo-name"
|
||||
>{{ release.entity.name }}</a>
|
||||
</template>
|
||||
|
||||
<a
|
||||
v-else
|
||||
:href="`/${release.entity.type}/${release.entity.slug}`"
|
||||
>
|
||||
<img
|
||||
:src="`/img/logos/${release.entity.slug}/thumbs/network.png`"
|
||||
:title="release.entity.name"
|
||||
class="logo logo-site"
|
||||
>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
release: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import 'breakpoints';
|
||||
|
||||
.details {
|
||||
background: var(--profile);
|
||||
color: var(--text-light);
|
||||
box-shadow: 0 0 3px var(--shadow-weak);
|
||||
cursor: default;
|
||||
|
||||
.column {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.link {
|
||||
color: var(--text-light);
|
||||
|
||||
.icon {
|
||||
fill: var(--lighten);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--text-light);
|
||||
|
||||
.icon {
|
||||
fill: var(--text-light);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tidbits {
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tidbit {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
|
||||
&.date {
|
||||
flex-shrink: 0;
|
||||
font-weight: bold;
|
||||
|
||||
.icon {
|
||||
fill: var(--lighten);
|
||||
margin: -.2rem 0 0 .75rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.site {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: .25rem 0;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.logo-link {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.logo-site {
|
||||
height: 2.5rem;
|
||||
max-width: 15rem;
|
||||
margin: .25rem 0;
|
||||
object-fit: contain;
|
||||
object-position: 100% 50%;
|
||||
}
|
||||
|
||||
.logo-parent {
|
||||
height: 1.5rem;
|
||||
max-width: 10rem;
|
||||
object-fit: contain;
|
||||
object-position: 100% 50%;
|
||||
}
|
||||
|
||||
.logo-name {
|
||||
padding: .5rem 0;
|
||||
color: var(--text-light);
|
||||
font-size: 1.25rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.chain {
|
||||
color: var(--lighten);
|
||||
padding: 0 .5rem;
|
||||
font-weight: bold;
|
||||
font-size: .8rem;
|
||||
}
|
||||
|
||||
.date-compact {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media(max-width: $breakpoint-mega) {
|
||||
.logo-parent,
|
||||
.chain {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.logo-site {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-width: $breakpoint) {
|
||||
.date-full {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.date-compact {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -71,7 +71,7 @@
|
|||
|
||||
<div
|
||||
v-for="photo in photos"
|
||||
:key="`media-${photo.index}`"
|
||||
:key="`media-${photo.id}`"
|
||||
class="item-container"
|
||||
>
|
||||
<a
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
<template>
|
||||
<div class="tile">
|
||||
<div class="movie">
|
||||
<router-link
|
||||
:to="{ name: 'movie', params: { movieId: movie.id, movieSlug: movie.slug } }"
|
||||
class="cover"
|
||||
>
|
||||
<img
|
||||
v-if="movie.covers[0]"
|
||||
:src="`/media/${movie.covers[0].thumbnail}`"
|
||||
>
|
||||
</router-link>
|
||||
|
||||
<div class="info">
|
||||
<router-link
|
||||
:to="{ name: 'movie', params: { movieId: movie.id, movieSlug: movie.slug } }"
|
||||
class="title-link"
|
||||
>
|
||||
<h3 class="title">{{ movie.title }}</h3>
|
||||
</router-link>
|
||||
|
||||
<ul
|
||||
class="actors nolist"
|
||||
:title="movie.actors.map(actor => actor.name).join(', ')"
|
||||
>
|
||||
<li
|
||||
v-for="actor in movie.actors"
|
||||
:key="`tag-${movie.id}-${actor.id}`"
|
||||
class="actor"
|
||||
><router-link
|
||||
:to="`/actor/${actor.id}/${actor.slug}`"
|
||||
class="actor-link"
|
||||
>{{ actor.name }}</router-link></li>
|
||||
</ul>
|
||||
|
||||
<ul
|
||||
class="tags nolist"
|
||||
:title="movie.tags.map(tag => tag.name).join(', ')"
|
||||
>
|
||||
<li
|
||||
v-for="tag in movie.tags"
|
||||
:key="`tag-${movie.id}-${tag.id}`"
|
||||
class="tag"
|
||||
><router-link
|
||||
:to="`/tag/${tag.slug}`"
|
||||
class="tag-link"
|
||||
>{{ tag.name }}</router-link></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Details :release="movie" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Details from './tile-details.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Details,
|
||||
},
|
||||
props: {
|
||||
movie: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import 'breakpoints';
|
||||
|
||||
.tile {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--background);
|
||||
box-shadow: 0 0 3px var(--darken-weak);
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.movie {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.title-link {
|
||||
color: var(--text);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.cover {
|
||||
height: 16rem;
|
||||
box-shadow: 0 0 3px var(--darken-weak);
|
||||
|
||||
img {
|
||||
height: 100%;
|
||||
max-width: 12rem;
|
||||
object-fit: cover;
|
||||
object-position: center ;
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.title {
|
||||
box-sizing: border-box;
|
||||
padding: 1rem;
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.actors {
|
||||
padding: 0 1rem;
|
||||
margin: 0 0 1rem 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.actor:not(:last-child)::after {
|
||||
content: ',';
|
||||
margin: 0 .25rem 0 0;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.actor-link {
|
||||
font-size: 1rem;
|
||||
color: var(--link);
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
||||
|
||||
.tags {
|
||||
padding: .2rem 1rem 0 1rem;
|
||||
height: 1.75rem;
|
||||
line-height: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tag {
|
||||
margin: 0 0 .5rem 0;
|
||||
}
|
||||
|
||||
.tag-link {
|
||||
background: var(--background);
|
||||
font-size: .75rem;
|
||||
padding: .25rem .5rem;
|
||||
color: var(--shadow);
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
box-shadow: 0 0 3px var(--shadow-weak);
|
||||
|
||||
&:hover {
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-width: $breakpoint) {
|
||||
.cover {
|
||||
height: 12rem;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,107 @@
|
|||
<template>
|
||||
<div
|
||||
v-if="movie"
|
||||
class="movie"
|
||||
>
|
||||
<Media
|
||||
:release="movie"
|
||||
/>
|
||||
|
||||
<Details :release="movie" />
|
||||
|
||||
<div class="column">
|
||||
<h2 class="title">{{ movie.title }}</h2>
|
||||
|
||||
<p>{{ movie.description }}</p>
|
||||
|
||||
<div
|
||||
v-lazy-container="{ selector: '.lazy' }"
|
||||
class="actors"
|
||||
>
|
||||
<ActorTile
|
||||
v-for="actor in movie.actors"
|
||||
:key="`actor-${actor.id}`"
|
||||
:actor="actor"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Tags
|
||||
v-if="movie.tags && movie.tags.length > 0"
|
||||
:tags="movie.tags"
|
||||
/>
|
||||
|
||||
<Releases
|
||||
:releases="movie.scenes"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Media from './media.vue';
|
||||
import Details from './details.vue';
|
||||
import Tags from './tags.vue';
|
||||
import Releases from './releases.vue';
|
||||
import ActorTile from '../actors/tile.vue';
|
||||
|
||||
async function mounted() {
|
||||
this.movie = await this.$store.dispatch('fetchMovieById', this.$route.params.movieId);
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Details,
|
||||
Tags,
|
||||
Media,
|
||||
ActorTile,
|
||||
Releases,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
movie: null,
|
||||
};
|
||||
},
|
||||
mounted,
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.title {
|
||||
padding: .5rem 0;
|
||||
margin: 0 0 1rem 0;
|
||||
color: var(--shadow-strong);
|
||||
}
|
||||
|
||||
.covers {
|
||||
display: inline-block;
|
||||
margin: 0 0 1rem 0;
|
||||
}
|
||||
|
||||
.cover {
|
||||
height: 20rem;
|
||||
margin: 0 1rem 0 0;
|
||||
box-shadow: 0 0 3px var(--shadow-weak);
|
||||
}
|
||||
|
||||
.trailer {
|
||||
height: 20rem;
|
||||
}
|
||||
|
||||
.date {
|
||||
display: inline-block;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.content-inner {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.actors {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
|
||||
grid-gap: .5rem;
|
||||
flex-grow: 1;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 0 1rem 0;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,63 @@
|
|||
<template>
|
||||
<div class="movies">
|
||||
<div class="content-inner">
|
||||
<div class="tiles">
|
||||
<MovieTile
|
||||
v-for="movie in movies"
|
||||
:key="`movie-${movie.id}`"
|
||||
:movie="movie"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MovieTile from './movie-tile.vue';
|
||||
|
||||
async function mounted() {
|
||||
const { movies, totalCount } = await this.$store.dispatch('fetchMovies', {
|
||||
limit: 30,
|
||||
});
|
||||
|
||||
this.movies = movies;
|
||||
this.totalCount = totalCount;
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MovieTile,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
movies: [],
|
||||
totalCount: 0,
|
||||
};
|
||||
},
|
||||
mounted,
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import 'breakpoints';
|
||||
|
||||
.movies {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.tiles {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(25rem, 1fr));
|
||||
grid-gap: 1rem;
|
||||
}
|
||||
|
||||
@media(max-width: $breakpoint) {
|
||||
.tiles {
|
||||
grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -15,7 +15,7 @@
|
|||
v-for="(release, index) in releases"
|
||||
:key="`release-${release.id}`"
|
||||
>
|
||||
<ReleaseTile
|
||||
<SceneTile
|
||||
:release="release"
|
||||
:referer="referer"
|
||||
:index="index"
|
||||
|
@ -36,7 +36,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import ReleaseTile from './tile.vue';
|
||||
import SceneTile from './scene-tile.vue';
|
||||
|
||||
function range() {
|
||||
return this.$route.params.range;
|
||||
|
@ -48,7 +48,7 @@ function sfw() {
|
|||
|
||||
export default {
|
||||
components: {
|
||||
ReleaseTile,
|
||||
SceneTile,
|
||||
},
|
||||
props: {
|
||||
releases: {
|
||||
|
|
|
@ -145,7 +145,6 @@ export default {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
padding: 0 0 .5rem 0;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 0 3px var(--darken-weak);
|
||||
height: 100%;
|
||||
|
@ -212,7 +211,7 @@ export default {
|
|||
}
|
||||
|
||||
.title {
|
||||
margin: 0 .25rem .25rem 0;
|
||||
margin: 0;
|
||||
color: var(--text);
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
|
@ -226,12 +225,6 @@ export default {
|
|||
color: var(--shadow);
|
||||
}
|
||||
|
||||
.network {
|
||||
color: #555;
|
||||
margin: 0 .25rem 0 0;
|
||||
font-size: .8rem;
|
||||
}
|
||||
|
||||
.actors {
|
||||
word-wrap: break-word;
|
||||
overflow: hidden;
|
||||
|
@ -255,18 +248,21 @@ export default {
|
|||
}
|
||||
|
||||
.labels {
|
||||
padding: 0 .5rem 1rem .25rem;
|
||||
padding: .1rem .5rem 1.5rem .5rem;
|
||||
max-height: .5rem;
|
||||
overflow-y: hidden;
|
||||
font-size: 0;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
.shoot {
|
||||
display: inline;
|
||||
padding: .25rem .5rem .25rem .25rem;
|
||||
border-right: solid 1px var(--shadow-hint);
|
||||
color: var(--shadow-strong);
|
||||
font-size: 0.8rem;
|
||||
padding: .25rem .5rem;
|
||||
background: var(--primary);
|
||||
color: var(--text-light);
|
||||
font-size: 0.75rem;
|
||||
font-weight: bold;
|
||||
box-shadow: inset 0 0 3px var(--shadow-weak);
|
||||
}
|
||||
|
||||
.tags {
|
||||
|
@ -275,17 +271,19 @@ export default {
|
|||
}
|
||||
|
||||
.tag {
|
||||
margin: 0 0 .25rem 0;
|
||||
margin: 0 0 1rem 0;
|
||||
}
|
||||
|
||||
.tag-link {
|
||||
background: var(--background);
|
||||
color: var(--shadow);
|
||||
display: inline-block;
|
||||
padding: .25rem;
|
||||
padding: .25rem .5rem;
|
||||
font-size: .75rem;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
line-height: 1;
|
||||
box-shadow: 0 0 3px var(--shadow-weak);
|
||||
|
||||
&:hover {
|
||||
color: var(--primary);
|
|
@ -20,91 +20,7 @@
|
|||
/>
|
||||
</Scroll>
|
||||
|
||||
<div class="details">
|
||||
<div class="column">
|
||||
<div class="tidbits">
|
||||
<a
|
||||
v-if="release.date"
|
||||
:title="release.url && `View scene on ${release.entity.name}`"
|
||||
:href="release.url"
|
||||
:class="{ link: release.url }"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="tidbit date"
|
||||
>
|
||||
<span class="showable">{{ formatDate(release.date, 'MMM D, YYYY', release.datePrecision) }}</span>
|
||||
<span class="hideable">{{ formatDate(release.date, 'MMMM D, YYYY', release.datePrecision) }}</span>
|
||||
|
||||
<Icon
|
||||
v-if="release.url"
|
||||
icon="share2"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="site">
|
||||
<template v-if="release.entity.parent && !release.entity.independent">
|
||||
<a
|
||||
v-if="release.entity.parent.hasLogo"
|
||||
:href="`/network/${release.entity.parent.slug}`"
|
||||
class="logo-link"
|
||||
>
|
||||
<img
|
||||
:src="`/img/logos/${release.entity.parent.slug}/thumbs/network.png`"
|
||||
:title="release.entity.parent.name"
|
||||
:alt="release.entity.parent.name"
|
||||
class="logo logo-parent"
|
||||
>
|
||||
</a>
|
||||
|
||||
<a
|
||||
v-else
|
||||
:href="`/network/${release.entity.parent.slug}`"
|
||||
class="logo-link logo-name"
|
||||
>{{ release.entity.parent.name }}</a>
|
||||
|
||||
<span class="chain">presents</span>
|
||||
|
||||
<a
|
||||
v-if="release.entity.hasLogo"
|
||||
:href="`/${release.entity.type}/${release.entity.slug}`"
|
||||
class="logo-link"
|
||||
>
|
||||
<img
|
||||
v-if="release.entity.type === 'network'"
|
||||
:src="`/img/logos/${release.entity.slug}/thumbs/network.png`"
|
||||
:title="release.entity.name"
|
||||
class="logo logo-site"
|
||||
>
|
||||
|
||||
<img
|
||||
v-else
|
||||
:src="`/img/logos/${release.entity.parent.slug}/thumbs/${release.entity.slug}.png`"
|
||||
:title="release.entity.name"
|
||||
class="logo logo-site"
|
||||
>
|
||||
</a>
|
||||
|
||||
<a
|
||||
v-else
|
||||
:href="`/${release.entity.type}/${release.entity.slug}`"
|
||||
class="logo-link logo-name"
|
||||
>{{ release.entity.name }}</a>
|
||||
</template>
|
||||
|
||||
<a
|
||||
v-else
|
||||
:href="`/${release.entity.type}/${release.entity.slug}`"
|
||||
>
|
||||
<img
|
||||
:src="`/img/logos/${release.entity.slug}/thumbs/network.png`"
|
||||
:title="release.entity.name"
|
||||
class="logo logo-site"
|
||||
>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Details :release="release" />
|
||||
|
||||
<Expand
|
||||
v-if="release.photos.length > 0"
|
||||
|
@ -136,18 +52,7 @@
|
|||
v-if="release.tags.length > 0"
|
||||
class="row"
|
||||
>
|
||||
<ul class="tags nolist">
|
||||
<li
|
||||
v-for="tag in release.tags"
|
||||
:key="`tag-${tag.slug}`"
|
||||
class="tag"
|
||||
>
|
||||
<a
|
||||
:href="`/tag/${tag.slug}`"
|
||||
class="link"
|
||||
>{{ tag.name }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<Tags :tags="release.tags" />
|
||||
</div>
|
||||
|
||||
<div class="row associations">
|
||||
|
@ -162,24 +67,28 @@
|
|||
<Actor :actor="actor" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="release.movies && release.movies.length > 0"
|
||||
class="movies"
|
||||
>
|
||||
<Release :release="release.movies[0]" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="release.scenes && release.scenes.length > 0"
|
||||
class="scenes"
|
||||
>
|
||||
<h3>Scenes</h3>
|
||||
<Releases
|
||||
:releases="release.scenes"
|
||||
class="row"
|
||||
/>
|
||||
>
|
||||
<span class="row-label">Part of</span>
|
||||
|
||||
<div class="movies">
|
||||
<router-link
|
||||
v-for="movie in release.movies"
|
||||
:key="`movie-${movie.id}`"
|
||||
:to="{ name: 'movie', params: { movieId: movie.id, movieSlug: movie.slug } }"
|
||||
class="movie"
|
||||
>
|
||||
<span class="movie-title">{{ movie.title }}</span>
|
||||
<img
|
||||
:src="`/media/${movie.covers[0].thumbnail}`"
|
||||
class="movie-cover"
|
||||
>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
|
@ -259,9 +168,9 @@
|
|||
|
||||
<script>
|
||||
import Media from './media.vue';
|
||||
import Details from './details.vue';
|
||||
import Tags from './tags.vue';
|
||||
import Actor from '../actors/tile.vue';
|
||||
import Release from './tile.vue';
|
||||
import Releases from './releases.vue';
|
||||
import Scroll from '../scroll/scroll.vue';
|
||||
import Expand from '../expand/expand.vue';
|
||||
|
||||
|
@ -278,11 +187,11 @@ async function mounted() {
|
|||
export default {
|
||||
components: {
|
||||
Actor,
|
||||
Details,
|
||||
Media,
|
||||
Release,
|
||||
Releases,
|
||||
Scroll,
|
||||
Expand,
|
||||
Tags,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -298,112 +207,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import 'theme';
|
||||
.column {
|
||||
width: 1200px;
|
||||
max-width: 100%;
|
||||
padding: 0 1rem;
|
||||
margin: 0 auto;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.details {
|
||||
background: var(--profile);
|
||||
color: var(--text-light);
|
||||
box-shadow: 0 0 3px var(--shadow-weak);
|
||||
cursor: default;
|
||||
|
||||
.column {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.link {
|
||||
color: var(--text-light);
|
||||
|
||||
.icon {
|
||||
fill: var(--lighten);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--text-light);
|
||||
|
||||
.icon {
|
||||
fill: var(--text-light);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tidbits {
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tidbit {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
|
||||
&.date {
|
||||
flex-shrink: 0;
|
||||
padding: 0 2rem 0 0;
|
||||
font-weight: bold;
|
||||
|
||||
.icon {
|
||||
fill: var(--lighten);
|
||||
margin: -.2rem 0 0 .75rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.site {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: .25rem 0;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.logo-link {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.logo-site {
|
||||
height: 2.5rem;
|
||||
max-width: 15rem;
|
||||
margin: .25rem 0;
|
||||
object-fit: contain;
|
||||
object-position: 100% 50%;
|
||||
}
|
||||
|
||||
.logo-parent {
|
||||
height: 1.5rem;
|
||||
max-width: 10rem;
|
||||
object-fit: contain;
|
||||
object-position: 100% 50%;
|
||||
}
|
||||
|
||||
.logo-name {
|
||||
padding: .5rem 0;
|
||||
color: var(--text-light);
|
||||
font-size: 1.25rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.chain {
|
||||
color: var(--lighten);
|
||||
padding: 0 .5rem;
|
||||
font-weight: bold;
|
||||
font-size: .8rem;
|
||||
}
|
||||
|
||||
@import 'breakpoints';
|
||||
.expand-bottom {
|
||||
border-bottom: solid 1px var(--shadow-hint);
|
||||
}
|
||||
|
@ -487,6 +291,39 @@ export default {
|
|||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.movies {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
|
||||
grid-gap: .5rem;
|
||||
flex-grow: 1;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.movie {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--background);
|
||||
box-shadow: 0 0 3px var(--shadow-weak);
|
||||
color: var(--text);
|
||||
text-decoration: none;
|
||||
|
||||
&:hover .movie-title {
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
||||
|
||||
.movie-cover {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.movie-title {
|
||||
padding: .5rem;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.link {
|
||||
display: inline-flex;
|
||||
color: var(--link);
|
||||
|
@ -501,35 +338,10 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
.tag .link {
|
||||
background: var(--background);
|
||||
display: inline-block;
|
||||
padding: .5rem;
|
||||
margin: 0 .25rem .25rem 0;
|
||||
box-shadow: 0 0 2px var(--shadow-weak);
|
||||
text-decoration: none;
|
||||
text-transform: capitalize;
|
||||
|
||||
&:hover {
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
||||
|
||||
.showable {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media(max-width: $breakpoint3) {
|
||||
.logo-parent,
|
||||
.chain {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.logo-site {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-width: $breakpoint) {
|
||||
.hideable {
|
||||
display: none;
|
|
@ -0,0 +1,42 @@
|
|||
<template>
|
||||
<ul class="tags nolist">
|
||||
<li
|
||||
v-for="tag in tags"
|
||||
:key="`tag-${tag.slug}`"
|
||||
class="tag"
|
||||
>
|
||||
<a
|
||||
:href="`/tag/${tag.slug}`"
|
||||
class="link"
|
||||
>{{ tag.name }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
tags: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tag .link {
|
||||
color: var(--link);
|
||||
background: var(--background);
|
||||
display: inline-block;
|
||||
padding: .5rem;
|
||||
margin: 0 .25rem .25rem 0;
|
||||
box-shadow: 0 0 2px var(--shadow-weak);
|
||||
text-decoration: none;
|
||||
text-transform: capitalize;
|
||||
|
||||
&:hover {
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -36,8 +36,8 @@
|
|||
|
||||
<a
|
||||
v-if="release.date"
|
||||
v-tooltip.bottom="release.url && `View scene on ${release.entity.name}`"
|
||||
:title="release.url && `View scene on ${release.entity.name}`"
|
||||
v-tooltip.bottom="release.url && `View release on ${release.entity.name}`"
|
||||
:title="release.url && `View release on ${release.entity.name}`"
|
||||
:href="release.url"
|
||||
:class="{ upcoming: isAfter(release.date, new Date()) }"
|
||||
target="_blank"
|
||||
|
@ -73,6 +73,7 @@ export default {
|
|||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
white-space: nowrap;
|
||||
background: var(--profile);
|
||||
font-size: 0;
|
||||
font-weight: bold;
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
<template>
|
||||
<div class="sidebar-container">
|
||||
<div
|
||||
class="sidebar-container"
|
||||
@click="$emit('toggle', false)"
|
||||
>
|
||||
<div
|
||||
class="sidebar"
|
||||
@click.stop
|
||||
|
@ -8,14 +11,14 @@
|
|||
<div class="sidebar-header">
|
||||
<Icon
|
||||
icon="cross2"
|
||||
class="sidebar-close"
|
||||
@click.native="toggleSidebar(false)"
|
||||
class="sidebar-close noselect"
|
||||
@click.native="$emit('toggle', false)"
|
||||
/>
|
||||
|
||||
<router-link
|
||||
to="/updates"
|
||||
class="logo-link"
|
||||
@click.native="toggleSidebar(false)"
|
||||
@click.native="$emit('toggle', false)"
|
||||
>
|
||||
<h1 class="sidebar-logo">
|
||||
<div
|
||||
|
@ -32,7 +35,7 @@
|
|||
<router-link
|
||||
v-slot="{ href, isActive, navigate }"
|
||||
to="/updates"
|
||||
@click.native="toggleSidebar(false)"
|
||||
@click.native="$emit('toggle', false)"
|
||||
>
|
||||
<a
|
||||
class="nav-link"
|
||||
|
@ -47,7 +50,7 @@
|
|||
<router-link
|
||||
v-slot="{ href, isActive, navigate }"
|
||||
to="/actors"
|
||||
@click.native="toggleSidebar(false)"
|
||||
@click.native="$emit('toggle', false)"
|
||||
>
|
||||
<a
|
||||
class="nav-link"
|
||||
|
@ -62,7 +65,7 @@
|
|||
<router-link
|
||||
v-slot="{ href, isActive, navigate }"
|
||||
to="/networks"
|
||||
@click.native="toggleSidebar(false)"
|
||||
@click.native="$emit('toggle', false)"
|
||||
>
|
||||
<a
|
||||
class="nav-link"
|
||||
|
@ -73,11 +76,26 @@
|
|||
</router-link>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<router-link
|
||||
v-slot="{ href, isActive, navigate }"
|
||||
to="/movies"
|
||||
@click.native="$emit('toggle', false)"
|
||||
>
|
||||
<a
|
||||
class="nav-link"
|
||||
:href="href"
|
||||
:class="{ active: isActive }"
|
||||
@click="navigate"
|
||||
>Movies</a>
|
||||
</router-link>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<router-link
|
||||
v-slot="{ href, isActive, navigate }"
|
||||
to="/tags"
|
||||
@click.native="toggleSidebar(false)"
|
||||
@click.native="$emit('toggle', false)"
|
||||
>
|
||||
<a
|
||||
class="nav-link"
|
||||
|
@ -96,7 +114,7 @@
|
|||
v-show="sfw"
|
||||
class="toggle"
|
||||
@click="setSfw(false)"
|
||||
><Icon icon="evil2" />Disable safe mode</label>
|
||||
><Icon icon="fire" />Disable safe mode</label>
|
||||
|
||||
<label
|
||||
v-show="!sfw"
|
||||
|
@ -142,12 +160,6 @@ function setSfw(enabled) {
|
|||
}
|
||||
|
||||
export default {
|
||||
props: {
|
||||
toggleSidebar: {
|
||||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
logo,
|
||||
|
@ -172,7 +184,7 @@ export default {
|
|||
width: 100%;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
background: var(--darken-hint);
|
||||
background: var(--darken-weak);
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
<template>
|
||||
<div class="stats">
|
||||
<div class="content-inner">
|
||||
<h1 class="heading">Stats</h1>
|
||||
|
||||
<dl class="stat-table">
|
||||
<div class="stat-row">
|
||||
<dt class="stat-label">Networks</dt>
|
||||
<dd class="stat-value">{{ totalNetworks }}</dd>
|
||||
</div>
|
||||
|
||||
<div class="stat-row">
|
||||
<dt class="stat-label">Channels</dt>
|
||||
<dd class="stat-value">{{ totalChannels }}</dd>
|
||||
</div>
|
||||
|
||||
<div class="stat-row">
|
||||
<dt class="stat-label">Scenes</dt>
|
||||
<dd class="stat-value">{{ totalScenes }}</dd>
|
||||
</div>
|
||||
|
||||
<div class="stat-row">
|
||||
<dt class="stat-label">Movies</dt>
|
||||
<dd class="stat-value">{{ totalMovies }}</dd>
|
||||
</div>
|
||||
|
||||
<div class="stat-row">
|
||||
<dt class="stat-label">Actors</dt>
|
||||
<dd class="stat-value">{{ totalActors }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
async function mounted() {
|
||||
const stats = await this.$store.dispatch('fetchStats');
|
||||
|
||||
this.totalScenes = stats.totalScenes;
|
||||
this.totalMovies = stats.totalMovies;
|
||||
this.totalActors = stats.totalActors;
|
||||
this.totalNetworks = stats.totalNetworks;
|
||||
this.totalChannels = stats.totalChannels;
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
totalScenes: 0,
|
||||
totalMovies: 0,
|
||||
totalActors: 0,
|
||||
totalNetworks: 0,
|
||||
totalChannels: 0,
|
||||
};
|
||||
},
|
||||
mounted,
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.stats {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.content-inner {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.stat-row {
|
||||
width: 20rem;
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
padding: .5rem 0;
|
||||
justify-content: space-between;
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: solid 1px var(--shadow-hint);
|
||||
}
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
color: var(--shadow-strong);
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
|
@ -12,6 +12,7 @@
|
|||
:src="`/img/${poster.thumbnail}`"
|
||||
:alt="tag.poster.comment"
|
||||
class="poster"
|
||||
@load="$parent.$emit('load')"
|
||||
>
|
||||
</a>
|
||||
|
||||
|
@ -28,6 +29,7 @@
|
|||
:src="`/img/${photo.thumbnail}`"
|
||||
:alt="photo.comment"
|
||||
class="photo"
|
||||
@load="$parent.$emit('load')"
|
||||
>
|
||||
</a>
|
||||
</div>
|
||||
|
@ -67,7 +69,7 @@ export default {
|
|||
<style lang="scss" scoped>
|
||||
.photos {
|
||||
width: 100%;
|
||||
padding: 1rem 1rem 0 1rem;
|
||||
padding: .5rem 1rem 0 .5rem;
|
||||
box-sizing: border-box;
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
|
||||
<FilterBar :fetch-releases="fetchReleases" />
|
||||
<Releases :releases="tag.releases" />
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -41,7 +43,7 @@ import { Converter } from 'showdown';
|
|||
|
||||
import escapeHtml from '../../../src/utils/escape-html';
|
||||
|
||||
import FilterBar from '../header/filter-bar.vue';
|
||||
import FilterBar from '../filters/filter-bar.vue';
|
||||
import Photos from './photos.vue';
|
||||
import Releases from '../releases/releases.vue';
|
||||
import Scroll from '../scroll/scroll.vue';
|
||||
|
@ -123,7 +125,7 @@ export default {
|
|||
}
|
||||
|
||||
.title {
|
||||
padding: .75rem 1rem;
|
||||
padding: .5rem 1rem;
|
||||
margin: 0;
|
||||
flex-shrink: 0;
|
||||
text-transform: capitalize;
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -48,13 +50,11 @@ async function mounted() {
|
|||
'creampie',
|
||||
'squirting',
|
||||
],
|
||||
ethnicity: [
|
||||
appearance: [
|
||||
'asian',
|
||||
'ebony',
|
||||
'latina',
|
||||
'caucasian',
|
||||
],
|
||||
appearance: [
|
||||
'natural-boobs',
|
||||
'fake-boobs',
|
||||
'blonde',
|
||||
|
@ -73,6 +73,15 @@ async function mounted() {
|
|||
'ass-eating',
|
||||
'atm',
|
||||
],
|
||||
cumshot: [
|
||||
'facial',
|
||||
'bukkake',
|
||||
'creampie',
|
||||
'anal-creampie',
|
||||
'cum-in-mouth',
|
||||
'oral-creampie',
|
||||
'cum-on-butt',
|
||||
],
|
||||
extreme: [
|
||||
'airtight',
|
||||
'dap',
|
||||
|
@ -81,14 +90,6 @@ async function mounted() {
|
|||
'dv-tp',
|
||||
'tap',
|
||||
],
|
||||
cumshot: [
|
||||
'facial',
|
||||
'bukkake',
|
||||
'creampie',
|
||||
'anal-creampie',
|
||||
'cum-in-mouth',
|
||||
'cum-on-butt',
|
||||
],
|
||||
roleplay: [
|
||||
'family',
|
||||
'parody',
|
||||
|
@ -98,6 +99,7 @@ async function mounted() {
|
|||
],
|
||||
fetish: [
|
||||
'bdsm',
|
||||
'bondage',
|
||||
'femdom',
|
||||
],
|
||||
toys: [
|
||||
|
@ -146,7 +148,7 @@ export default {
|
|||
@import 'theme';
|
||||
|
||||
.tags {
|
||||
padding: 1rem;
|
||||
padding: 1rem 1rem 0 1rem;
|
||||
}
|
||||
|
||||
.tiles {
|
||||
|
@ -157,11 +159,6 @@ export default {
|
|||
}
|
||||
|
||||
.heading {
|
||||
display: inline-block;
|
||||
background: var(--primary);
|
||||
color: var(--text-light);
|
||||
padding: .5rem;
|
||||
font-size: 1rem;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
:to="{ name: 'tag', params: { tagSlug: tag.slug, range: 'latest' } }"
|
||||
:title="tag.name"
|
||||
>{{ tag.name }}</router-link>
|
||||
|
||||
</div>
|
||||
|
||||
<span
|
||||
|
@ -85,6 +86,11 @@ export default {
|
|||
box-sizing: border-box;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
font-size: 0;
|
||||
|
||||
&:hover .poster {
|
||||
box-shadow: 0 0 3px var(--darken);
|
||||
}
|
||||
}
|
||||
|
||||
.poster {
|
||||
|
@ -93,24 +99,17 @@ export default {
|
|||
}
|
||||
|
||||
.title {
|
||||
display: inline-block;
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
padding: .25rem .5rem .25rem 1rem;
|
||||
padding: .5rem;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
color: var(--shadow-strong);
|
||||
color: var(--text-light);
|
||||
background: var(--profile);
|
||||
text-decoration: none;
|
||||
font-size: .9rem;
|
||||
font-weight: bold;
|
||||
text-transform: capitalize;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.poster-link:hover + .title,
|
||||
.title:hover {
|
||||
color: var(--primary)
|
||||
}
|
||||
|
||||
.poster-link:hover .poster {
|
||||
box-shadow: 0 0 3px var(--darken);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -18,6 +18,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
.nolink {
|
||||
display: inline-block;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<title>antenna</title>
|
||||
<path d="M11.182 11.932c-0.192 0-0.384-0.073-0.53-0.22-0.293-0.293-0.293-0.768 0-1.061 1.462-1.462 1.462-3.841 0-5.303s-3.841-1.462-5.303 0c-0.708 0.708-1.098 1.65-1.098 2.652s0.39 1.943 1.098 2.652c0.293 0.293 0.293 0.768 0 1.061s-0.768 0.293-1.061 0c-0.992-0.992-1.538-2.31-1.538-3.712s0.546-2.721 1.538-3.712c2.047-2.047 5.378-2.047 7.425 0s2.047 5.378 0 7.425c-0.146 0.146-0.338 0.22-0.53 0.22v0z"></path>
|
||||
<path d="M13.127 13.877c-0.192 0-0.384-0.073-0.53-0.22-0.293-0.293-0.293-0.768 0-1.061 2.534-2.534 2.534-6.658 0-9.192s-6.658-2.534-9.192 0c-2.534 2.534-2.534 6.658 0 9.192 0.293 0.293 0.293 0.768 0 1.061s-0.768 0.293-1.061 0c-1.511-1.511-2.343-3.52-2.343-5.657s0.832-4.146 2.343-5.657c1.511-1.511 3.52-2.343 5.657-2.343s4.146 0.832 5.657 2.343c1.511 1.511 2.343 3.52 2.343 5.657s-0.832 4.146-2.343 5.657c-0.146 0.146-0.338 0.22-0.53 0.22v0z"></path>
|
||||
<path d="M9.5 15h-0.501l0.001-5.268c0.598-0.346 1-0.992 1-1.732 0-1.105-0.895-2-2-2s-2 0.895-2 2c0 0.74 0.402 1.386 1 1.732l-0.001 5.268h-0.499c-0.276 0-0.5 0.224-0.5 0.5s0.224 0.5 0.5 0.5h3c0.276 0 0.5-0.224 0.5-0.5s-0.224-0.5-0.5-0.5z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
|
@ -0,0 +1,5 @@
|
|||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<title>antenna2</title>
|
||||
<path d="M5 16h6l-2-2v-7.268c0.598-0.346 1-0.992 1-1.732 0-1.105-0.895-2-2-2s-2 0.895-2 2c0 0.74 0.402 1.386 1 1.732v7.268l-2 2zM11.122 2.5c0.549 0.685 0.878 1.554 0.878 2.5s-0.329 1.815-0.878 2.5l0.781 0.625c0.686-0.856 1.097-1.942 1.097-3.125s-0.411-2.269-1.097-3.125l-0.781 0.625zM4.097 1.875c-0.686 0.856-1.097 1.942-1.097 3.125s0.411 2.269 1.097 3.125l0.781-0.625c-0.549-0.685-0.878-1.554-0.878-2.5s0.329-1.815 0.878-2.5l-0.781-0.625zM1.755 0c-1.098 1.37-1.755 3.108-1.755 5s0.657 3.63 1.755 5l0.781-0.625c-0.961-1.198-1.536-2.719-1.536-4.375s0.575-3.176 1.536-4.375l-0.781-0.625zM14.245 0l-0.781 0.625c0.961 1.198 1.536 2.719 1.536 4.375s-0.575 3.176-1.536 4.375l0.781 0.625c1.098-1.37 1.755-3.108 1.755-5s-0.657-3.63-1.755-5z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 904 B |
|
@ -0,0 +1,5 @@
|
|||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<title>camera</title>
|
||||
<path d="M4.75 9.5c0 1.795 1.455 3.25 3.25 3.25s3.25-1.455 3.25-3.25-1.455-3.25-3.25-3.25-3.25 1.455-3.25 3.25zM15 4h-3.5c-0.25-1-0.5-2-1.5-2h-4c-1 0-1.25 1-1.5 2h-3.5c-0.55 0-1 0.45-1 1v9c0 0.55 0.45 1 1 1h14c0.55 0 1-0.45 1-1v-9c0-0.55-0.45-1-1-1zM8 13.938c-2.451 0-4.438-1.987-4.438-4.438s1.987-4.438 4.438-4.438c2.451 0 4.438 1.987 4.438 4.438s-1.987 4.438-4.438 4.438zM15 7h-2v-1h2v1z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 559 B |
|
@ -0,0 +1,5 @@
|
|||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<title>display</title>
|
||||
<path d="M0 1v10h16v-10h-16zM15 10h-14v-8h14v8zM10.5 12h-5l-0.5 2-1 1h8l-1-1z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 248 B |
|
@ -0,0 +1,5 @@
|
|||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<title>display4</title>
|
||||
<path d="M16 13v-12h-16v12h7v1h-3v1h8v-1h-3v-1h7zM2 3h12v8h-12v-8z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 238 B |
|
@ -0,0 +1,5 @@
|
|||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<title>station</title>
|
||||
<path d="M10 5c0-1.105-0.895-2-2-2s-2 0.895-2 2c0 0.709 0.369 1.331 0.925 1.686l-2.54 9.314h1.231l0.273-1h4.224l0.273 1h1.231l-2.54-9.314c0.556-0.355 0.925-0.977 0.925-1.686zM9.294 12h-2.587l0.273-1h2.042l0.273 1zM7.252 10l0.748-2.743 0.748 2.743h-1.496zM6.161 14l0.273-1h3.133l0.273 1h-3.678zM10.38 0.602c1.56 0.846 2.62 2.498 2.62 4.398s-1.059 3.552-2.62 4.398c0.689-1.096 1.12-2.66 1.12-4.398s-0.431-3.302-1.12-4.398zM13.38 0.602c1.56 0.846 2.62 2.498 2.62 4.398s-1.059 3.552-2.62 4.398c0.689-1.096 1.12-2.66 1.12-4.398s-0.431-3.302-1.12-4.398zM5.62 9.398c-1.56-0.846-2.62-2.498-2.62-4.398s1.059-3.552 2.62-4.398c-0.689 1.096-1.12 2.66-1.12 4.398s0.431 3.302 1.12 4.398zM0 5c0-1.9 1.059-3.552 2.62-4.398-0.689 1.096-1.12 2.66-1.12 4.398s0.431 3.302 1.12 4.398c-1.56-0.846-2.62-2.498-2.62-4.398z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 968 B |
|
@ -0,0 +1,9 @@
|
|||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<title>station2</title>
|
||||
<path d="M3.128 11.347l0.643-1.415c-1.389-1.193-2.271-2.961-2.271-4.932 0-1.535 0.535-2.947 1.428-4.060l-1.174-0.94c-1.136 1.414-1.754 3.159-1.754 5 0 2.137 0.832 4.146 2.343 5.657 0.249 0.249 0.511 0.478 0.785 0.69z"></path>
|
||||
<path d="M12.229 9.932l0.643 1.415c0.274-0.211 0.536-0.441 0.785-0.69 1.511-1.511 2.343-3.52 2.343-5.657 0-1.84-0.618-3.585-1.754-5l-1.174 0.94c0.893 1.113 1.428 2.525 1.428 4.060 0 1.971-0.882 3.739-2.271 4.932z"></path>
|
||||
<path d="M11.006 7.242l0.68 1.496c0.009-0.009 0.018-0.017 0.026-0.025 1.909-1.909 2.037-4.934 0.386-6.993l-1.171 0.937c1.067 1.331 1.093 3.226 0.079 4.585z"></path>
|
||||
<path d="M4.288 8.712c0.009 0.009 0.017 0.017 0.026 0.025l0.68-1.496c-1.015-1.359-0.988-3.254 0.079-4.585l-1.171-0.937c-1.651 2.060-1.523 5.084 0.386 6.993z"></path>
|
||||
<path d="M10 5c0-1.105-0.895-2-2-2s-2 0.895-2 2c0 0.583 0.25 1.108 0.648 1.473l-4.33 9.527h1.648l0.909-2h6.252l0.909 2h1.648l-4.33-9.527c0.398-0.366 0.648-0.89 0.648-1.473zM8 7.123l1.308 2.877h-2.616l1.308-2.877zM10.671 13h-5.343l0.909-2h3.525l0.909 2z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
|
@ -0,0 +1,5 @@
|
|||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<title>tv</title>
|
||||
<path d="M15.331 4.502c-1.388-0.199-2.865-0.344-4.407-0.425l2.576-2.576-1-1-3.509 3.509c-0.328-0.006-0.659-0.009-0.991-0.009v0l-4-4-1 1 3.034 3.034c-1.889 0.066-3.693 0.227-5.365 0.467-0.43 1.683-0.669 3.543-0.669 5.498s0.239 3.815 0.669 5.498c2.244 0.323 4.724 0.502 7.331 0.502s5.087-0.179 7.331-0.502c0.43-1.683 0.669-3.543 0.669-5.498s-0.239-3.815-0.669-5.498zM13.498 13.666c-1.683 0.215-3.543 0.334-5.498 0.334s-3.815-0.119-5.498-0.334c-0.323-1.122-0.502-2.362-0.502-3.666s0.179-2.543 0.502-3.666c1.683-0.215 3.543-0.334 5.498-0.334s3.815 0.119 5.498 0.334c0.323 1.122 0.502 2.362 0.502 3.666s-0.179 2.543-0.502 3.666z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 789 B |
|
@ -7,8 +7,6 @@
|
|||
|
||||
<title>traxxx</title>
|
||||
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/img/favicon/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/img/favicon/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/img/favicon/favicon-16x16.png">
|
||||
|
@ -18,6 +16,8 @@
|
|||
<meta name="msapplication-TileColor" content="#aa2c66">
|
||||
<meta name="msapplication-config" content="/img/favicon/browserconfig.xml">
|
||||
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
|
||||
<script src="/js/bundle.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -12,6 +12,7 @@ function initActorActions(store, router) {
|
|||
range = 'latest',
|
||||
}) {
|
||||
const { before, after, orderBy } = getDateRange(range);
|
||||
const includeChannels = router.currentRoute.query.channels ? router.currentRoute.query.channels.split(',') : [];
|
||||
const includeTags = router.currentRoute.query.tags ? router.currentRoute.query.tags.split(',') : [];
|
||||
const mode = router.currentRoute.query.mode || 'all';
|
||||
|
||||
|
@ -26,6 +27,7 @@ function initActorActions(store, router) {
|
|||
$selectableTags: [String],
|
||||
$includeTags: [String!],
|
||||
$mode: String!,
|
||||
${includeChannels.length > 0 ? '$includeChannels: [String!]' : ''}
|
||||
) {
|
||||
actor(id: $actorId) {
|
||||
id
|
||||
|
@ -146,12 +148,31 @@ function initActorActions(store, router) {
|
|||
slug
|
||||
priority
|
||||
}
|
||||
channels {
|
||||
id
|
||||
name
|
||||
slug
|
||||
type
|
||||
independent
|
||||
parent {
|
||||
id
|
||||
name
|
||||
slug
|
||||
type
|
||||
}
|
||||
}
|
||||
scenesConnection(
|
||||
filter: {
|
||||
date: {
|
||||
lessThan: $before,
|
||||
greaterThan: $after,
|
||||
}
|
||||
${includeChannels.length > 0 ? `
|
||||
entity: {
|
||||
slug: {
|
||||
in: $includeChannels
|
||||
}
|
||||
}` : ''}
|
||||
}
|
||||
selectedTags: $includeTags
|
||||
mode: $mode
|
||||
|
@ -176,6 +197,7 @@ function initActorActions(store, router) {
|
|||
orderBy,
|
||||
excludeTags: store.state.ui.filter,
|
||||
includeTags,
|
||||
includeChannels,
|
||||
mode,
|
||||
});
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ function curateRelease(release) {
|
|||
...release,
|
||||
actors: [],
|
||||
poster: release.poster && release.poster.media,
|
||||
tags: release.tags ? release.tags.map(({ tag }) => tag) : [],
|
||||
tags: release.tags ? release.tags.map(tag => tag.tag || tag) : [],
|
||||
};
|
||||
|
||||
if (release.scenes) curatedRelease.scenes = release.scenes.map(({ scene }) => curateRelease(scene));
|
||||
|
@ -73,7 +73,7 @@ function curateRelease(release) {
|
|||
if (release.covers) curatedRelease.covers = release.covers.map(({ media }) => media);
|
||||
if (release.trailer) curatedRelease.trailer = release.trailer.media;
|
||||
if (release.teaser) curatedRelease.teaser = release.teaser.media;
|
||||
if (release.actors) curatedRelease.actors = release.actors.map(({ actor }) => curateActor(actor, curatedRelease));
|
||||
if (release.actors) curatedRelease.actors = release.actors.map(actor => curateActor(actor.actor || actor, curatedRelease));
|
||||
if (release.movieTags && release.movieTags.length > 0) curatedRelease.tags = release.movieTags.map(({ tag }) => tag);
|
||||
if (release.movieActors && release.movieActors.length > 0) curatedRelease.actors = release.movieActors.map(({ actor }) => curateActor(actor, curatedRelease));
|
||||
|
||||
|
|
|
@ -86,7 +86,6 @@ function initEntitiesActions(store, _router) {
|
|||
or: [
|
||||
{
|
||||
slug: { equalTo: $entitySlug }
|
||||
type: { equalTo: "channel" }
|
||||
},
|
||||
{
|
||||
parent: {
|
||||
|
@ -181,6 +180,13 @@ function initEntitiesActions(store, _router) {
|
|||
type: {
|
||||
equalTo: "network"
|
||||
}
|
||||
childEntitiesConnection: {
|
||||
some: {
|
||||
type: {
|
||||
equalTo: "channel"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
independent: {
|
||||
|
@ -198,7 +204,7 @@ function initEntitiesActions(store, _router) {
|
|||
in: $entitySlugs
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
}
|
||||
) {
|
||||
id
|
||||
|
|
|
@ -93,6 +93,7 @@ const releaseTagsFragment = `
|
|||
const releasePosterFragment = `
|
||||
poster: releasesPosterByReleaseId {
|
||||
media {
|
||||
id
|
||||
index
|
||||
path
|
||||
thumbnail
|
||||
|
@ -112,6 +113,7 @@ const releasePosterFragment = `
|
|||
const releaseCoversFragment = `
|
||||
covers: releasesCovers {
|
||||
media {
|
||||
id
|
||||
index
|
||||
path
|
||||
thumbnail
|
||||
|
@ -131,6 +133,7 @@ const releaseCoversFragment = `
|
|||
const releasePhotosFragment = `
|
||||
photos: releasesPhotos {
|
||||
media {
|
||||
id
|
||||
index
|
||||
path
|
||||
thumbnail
|
||||
|
@ -150,6 +153,7 @@ const releasePhotosFragment = `
|
|||
const releaseTrailerFragment = `
|
||||
trailer: releasesTrailerByReleaseId {
|
||||
media {
|
||||
id
|
||||
index
|
||||
path
|
||||
thumbnail
|
||||
|
@ -175,7 +179,6 @@ const releaseFields = `
|
|||
date
|
||||
datePrecision
|
||||
slug
|
||||
type
|
||||
shootId
|
||||
productionDate
|
||||
comment
|
||||
|
@ -218,18 +221,6 @@ const releasesFragment = `
|
|||
) {
|
||||
releases: nodes {
|
||||
${releaseFields}
|
||||
movieActors: movieActorsByMovieId(orderBy: ACTOR_BY_ACTOR_ID__GENDER_ASC) {
|
||||
actor {
|
||||
${actorFields}
|
||||
}
|
||||
}
|
||||
movieTags: movieTagsByMovieId(orderBy: TAG_BY_TAG_ID__PRIORITY_DESC) {
|
||||
tag {
|
||||
id
|
||||
name
|
||||
slug
|
||||
}
|
||||
}
|
||||
}
|
||||
totalCount
|
||||
}
|
||||
|
@ -256,48 +247,35 @@ const releaseFragment = `
|
|||
${releaseTrailerFragment}
|
||||
${releaseTeaserFragment}
|
||||
${siteFragment}
|
||||
movieActors: movieActorsByMovieId(orderBy: ACTOR_BY_ACTOR_ID__GENDER_ASC) {
|
||||
actor {
|
||||
${actorFields}
|
||||
}
|
||||
}
|
||||
movieTags: movieTagsByMovieId(orderBy: TAG_BY_TAG_ID__PRIORITY_DESC) {
|
||||
tag {
|
||||
id
|
||||
name
|
||||
slug
|
||||
}
|
||||
}
|
||||
movies: releasesMoviesBySceneId {
|
||||
movie {
|
||||
id
|
||||
title
|
||||
date
|
||||
slug
|
||||
createdAt
|
||||
url
|
||||
${releaseCoversFragment}
|
||||
${siteFragment}
|
||||
actors: movieActorsByMovieId {
|
||||
actor {
|
||||
id
|
||||
name
|
||||
slug
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
scenes: releasesMoviesByMovieId {
|
||||
scene {
|
||||
${releaseFields}
|
||||
}
|
||||
}
|
||||
studio {
|
||||
id
|
||||
name
|
||||
slug
|
||||
url
|
||||
}
|
||||
movies: moviesScenesBySceneId {
|
||||
movie {
|
||||
id
|
||||
title
|
||||
slug
|
||||
covers: moviesCoversByReleaseId {
|
||||
media {
|
||||
index
|
||||
path
|
||||
thumbnail
|
||||
lazy
|
||||
comment
|
||||
sfw: sfwMedia {
|
||||
id
|
||||
thumbnail
|
||||
lazy
|
||||
path
|
||||
comment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import '../css/style.scss';
|
|||
|
||||
import Container from '../components/container/container.vue';
|
||||
import Icon from '../components/icon/icon.vue';
|
||||
import Footer from '../components/footer/footer.vue';
|
||||
|
||||
function formatDate(date, format = 'MMMM D, YYYY', precision = 'day') {
|
||||
if (precision === 'year') {
|
||||
|
@ -39,6 +40,7 @@ function init() {
|
|||
Vue.mixin({
|
||||
components: {
|
||||
Icon,
|
||||
Footer,
|
||||
},
|
||||
watch: {
|
||||
pageTitle(title) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { graphql } from '../api';
|
||||
import { releasesFragment, releaseFragment } from '../fragments';
|
||||
import { releasesFragment, releaseFragment, releaseFields } from '../fragments';
|
||||
import { curateRelease } from '../curate';
|
||||
import getDateRange from '../get-date-range';
|
||||
|
||||
|
@ -47,9 +47,145 @@ function initReleasesActions(store, _router) {
|
|||
return curateRelease(release);
|
||||
}
|
||||
|
||||
async function fetchMovies({ _commit }, { limit = 10, pageNumber = 1 }) {
|
||||
const { connection: { movies, totalCount } } = await graphql(`
|
||||
query Movies(
|
||||
$limit:Int = 1000,
|
||||
$offset:Int = 0,
|
||||
) {
|
||||
connection: moviesConnection(
|
||||
first: $limit
|
||||
offset: $offset
|
||||
orderBy: DATE_DESC
|
||||
) {
|
||||
movies: nodes {
|
||||
id
|
||||
title
|
||||
url
|
||||
slug
|
||||
date
|
||||
datePrecision
|
||||
actors {
|
||||
id
|
||||
name
|
||||
slug
|
||||
}
|
||||
tags {
|
||||
id
|
||||
name
|
||||
slug
|
||||
}
|
||||
entity {
|
||||
id
|
||||
name
|
||||
slug
|
||||
type
|
||||
parent {
|
||||
id
|
||||
name
|
||||
slug
|
||||
type
|
||||
}
|
||||
}
|
||||
covers: moviesCoversByReleaseId {
|
||||
media {
|
||||
id
|
||||
path
|
||||
thumbnail
|
||||
}
|
||||
}
|
||||
}
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
`, {
|
||||
limit,
|
||||
offset: Math.max(0, (pageNumber - 1)) * limit,
|
||||
});
|
||||
|
||||
return {
|
||||
movies: movies.map(release => curateRelease(release)),
|
||||
totalCount,
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchMovieById({ _commit }, movieId) {
|
||||
// const release = await get(`/releases/${releaseId}`);
|
||||
|
||||
const { movie } = await graphql(`
|
||||
query Movie($movieId: Int!) {
|
||||
movie(id: $movieId) {
|
||||
id
|
||||
title
|
||||
slug
|
||||
url
|
||||
date
|
||||
actors {
|
||||
id
|
||||
name
|
||||
age
|
||||
dateOfBirth
|
||||
birthCountry: countryByBirthCountryAlpha2 {
|
||||
alpha2
|
||||
name
|
||||
alias
|
||||
}
|
||||
avatar: avatarMedia {
|
||||
id
|
||||
path
|
||||
thumbnail
|
||||
lazy
|
||||
}
|
||||
}
|
||||
covers: moviesCoversByReleaseId {
|
||||
media {
|
||||
id
|
||||
path
|
||||
thumbnail
|
||||
}
|
||||
}
|
||||
trailer: moviesTrailerByReleaseId {
|
||||
media {
|
||||
id
|
||||
path
|
||||
}
|
||||
}
|
||||
scenes: moviesScenes {
|
||||
scene {
|
||||
${releaseFields}
|
||||
}
|
||||
}
|
||||
tags {
|
||||
id
|
||||
slug
|
||||
name
|
||||
}
|
||||
entity {
|
||||
id
|
||||
name
|
||||
slug
|
||||
type
|
||||
parent {
|
||||
id
|
||||
name
|
||||
slug
|
||||
type
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`, {
|
||||
movieId: Number(movieId),
|
||||
});
|
||||
|
||||
return curateRelease(movie);
|
||||
}
|
||||
|
||||
return {
|
||||
fetchReleases,
|
||||
fetchReleaseById,
|
||||
fetchMovies,
|
||||
fetchMovieById,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -2,14 +2,17 @@ import Vue from 'vue';
|
|||
import VueRouter from 'vue-router';
|
||||
|
||||
import Home from '../components/home/home.vue';
|
||||
import Release from '../components/releases/release.vue';
|
||||
import Scene from '../components/releases/scene.vue';
|
||||
import Movie from '../components/releases/movie.vue';
|
||||
import Entity from '../components/entities/entity.vue';
|
||||
import Networks from '../components/networks/networks.vue';
|
||||
import Actor from '../components/actors/actor.vue';
|
||||
import Actors from '../components/actors/actors.vue';
|
||||
import Movies from '../components/releases/movies.vue';
|
||||
import Tag from '../components/tags/tag.vue';
|
||||
import Tags from '../components/tags/tags.vue';
|
||||
import Search from '../components/search/search.vue';
|
||||
import Stats from '../components/stats/stats.vue';
|
||||
import NotFound from '../components/errors/404.vue';
|
||||
|
||||
Vue.use(VueRouter);
|
||||
|
@ -43,12 +46,12 @@ const routes = [
|
|||
},
|
||||
{
|
||||
path: '/scene/:releaseId/:releaseSlug?',
|
||||
component: Release,
|
||||
component: Scene,
|
||||
name: 'scene',
|
||||
},
|
||||
{
|
||||
path: '/movie/:releaseId/:releaseSlug?',
|
||||
component: Release,
|
||||
path: '/movie/:movieId/:movieSlug?',
|
||||
component: Movie,
|
||||
name: 'movie',
|
||||
},
|
||||
{
|
||||
|
@ -137,7 +140,7 @@ const routes = [
|
|||
name: 'actors',
|
||||
params: {
|
||||
...from.params,
|
||||
gender: 'female',
|
||||
gender: 'all',
|
||||
letter: 'all',
|
||||
tags: 'all',
|
||||
range: 'latest',
|
||||
|
@ -155,6 +158,11 @@ const routes = [
|
|||
component: Networks,
|
||||
name: 'networks',
|
||||
},
|
||||
{
|
||||
path: '/movies',
|
||||
component: Movies,
|
||||
name: 'movies',
|
||||
},
|
||||
{
|
||||
path: '/tags',
|
||||
component: Tags,
|
||||
|
@ -165,6 +173,11 @@ const routes = [
|
|||
component: Search,
|
||||
name: 'search',
|
||||
},
|
||||
{
|
||||
path: '/stats',
|
||||
component: Stats,
|
||||
name: 'stats',
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
component: NotFound,
|
||||
|
|
|
@ -41,7 +41,6 @@ function initUiActions(_store, _router) {
|
|||
slug
|
||||
date
|
||||
url
|
||||
type
|
||||
isNew
|
||||
entity {
|
||||
id
|
||||
|
@ -155,6 +154,32 @@ function initUiActions(_store, _router) {
|
|||
};
|
||||
}
|
||||
|
||||
async function fetchStats() {
|
||||
const {
|
||||
scenes,
|
||||
movies,
|
||||
actors,
|
||||
networks,
|
||||
channels,
|
||||
} = await graphql(`
|
||||
query Stats {
|
||||
scenes: releasesConnection { totalCount }
|
||||
movies: moviesConnection { totalCount }
|
||||
actors: actorsConnection { totalCount }
|
||||
networks: entitiesConnection(filter: { type: { equalTo: "network" } }) { totalCount }
|
||||
channels: entitiesConnection(filter: { type: { equalTo: "channel" } }) { totalCount }
|
||||
}
|
||||
`);
|
||||
|
||||
return {
|
||||
totalScenes: scenes.totalCount,
|
||||
totalMovies: movies.totalCount,
|
||||
totalActors: actors.totalCount,
|
||||
totalNetworks: networks.totalCount,
|
||||
totalChannels: channels.totalCount,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
search,
|
||||
setFilter,
|
||||
|
@ -162,6 +187,7 @@ function initUiActions(_store, _router) {
|
|||
setBatch,
|
||||
setSfw,
|
||||
setTheme,
|
||||
fetchStats,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -11,13 +11,7 @@ module.exports = {
|
|||
sfwHost: '0.0.0.0',
|
||||
sfwPort: 5001,
|
||||
},
|
||||
// include: [],
|
||||
exclude: {
|
||||
networks: [
|
||||
'gamma',
|
||||
'mindgeek',
|
||||
'julesjordan',
|
||||
],
|
||||
channels: [
|
||||
// 21sextreme, no longer updated
|
||||
'mightymistress',
|
||||
|
@ -161,14 +155,16 @@ module.exports = {
|
|||
'pervertgallery',
|
||||
'povperverts',
|
||||
],
|
||||
'kellymadison',
|
||||
'private',
|
||||
'ddfnetwork',
|
||||
'bangbros',
|
||||
'hitzefrei',
|
||||
[
|
||||
'silverstonedvd',
|
||||
'silviasaint',
|
||||
],
|
||||
'kellymadison',
|
||||
'porncz',
|
||||
'gangbangcreampie',
|
||||
'gloryholesecrets',
|
||||
'aziani',
|
||||
|
|
|
@ -612,12 +612,9 @@ exports.up = knex => Promise.resolve()
|
|||
.references('id')
|
||||
.inTable('entities');
|
||||
|
||||
table.text('type', 10)
|
||||
.defaultTo('scene');
|
||||
|
||||
table.text('shoot_id');
|
||||
table.text('entry_id');
|
||||
table.unique(['entity_id', 'entry_id', 'type']);
|
||||
table.unique(['entity_id', 'entry_id']);
|
||||
|
||||
table.text('url', 1000);
|
||||
table.text('title');
|
||||
|
@ -668,22 +665,6 @@ exports.up = knex => Promise.resolve()
|
|||
table.datetime('created_at')
|
||||
.defaultTo(knex.fn.now());
|
||||
}))
|
||||
.then(() => knex.schema.createTable('releases_movies', (table) => {
|
||||
table.integer('movie_id', 16)
|
||||
.notNullable()
|
||||
.references('id')
|
||||
.inTable('releases');
|
||||
|
||||
table.integer('scene_id', 16)
|
||||
.notNullable()
|
||||
.references('id')
|
||||
.inTable('releases');
|
||||
|
||||
table.unique(['movie_id', 'scene_id']);
|
||||
|
||||
table.datetime('created_at')
|
||||
.defaultTo(knex.fn.now());
|
||||
}))
|
||||
.then(() => knex.schema.createTable('releases_directors', (table) => {
|
||||
table.integer('release_id', 16)
|
||||
.notNullable()
|
||||
|
@ -780,6 +761,90 @@ exports.up = knex => Promise.resolve()
|
|||
.references('id')
|
||||
.inTable('releases');
|
||||
}))
|
||||
.then(() => knex.schema.createTable('movies', (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', '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');
|
||||
|
||||
table.integer('updated_batch_id', 12)
|
||||
.references('id')
|
||||
.inTable('batches');
|
||||
|
||||
table.datetime('created_at')
|
||||
.defaultTo(knex.fn.now());
|
||||
}))
|
||||
.then(() => knex.schema.createTable('movies_scenes', (table) => {
|
||||
table.integer('movie_id', 16)
|
||||
.notNullable()
|
||||
.references('id')
|
||||
.inTable('movies');
|
||||
|
||||
table.integer('scene_id', 16)
|
||||
.notNullable()
|
||||
.references('id')
|
||||
.inTable('releases');
|
||||
|
||||
table.unique(['movie_id', 'scene_id']);
|
||||
|
||||
table.datetime('created_at')
|
||||
.defaultTo(knex.fn.now());
|
||||
}))
|
||||
.then(() => knex.schema.createTable('movies_covers', (table) => {
|
||||
table.integer('release_id', 16)
|
||||
.notNullable()
|
||||
.references('id')
|
||||
.inTable('movies');
|
||||
|
||||
table.text('media_id', 21)
|
||||
.notNullable()
|
||||
.references('id')
|
||||
.inTable('media');
|
||||
|
||||
table.unique(['release_id', 'media_id']);
|
||||
}))
|
||||
.then(() => knex.schema.createTable('movies_trailers', (table) => {
|
||||
table.integer('release_id', 16)
|
||||
.unique()
|
||||
.notNullable()
|
||||
.references('id')
|
||||
.inTable('movies');
|
||||
|
||||
table.text('media_id', 21)
|
||||
.notNullable()
|
||||
.references('id')
|
||||
.inTable('media');
|
||||
}))
|
||||
// SEARCH
|
||||
.then(() => { // eslint-disable-line arrow-body-style
|
||||
// allow vim fold
|
||||
|
@ -857,18 +922,25 @@ exports.up = knex => Promise.resolve()
|
|||
ORDER BY tags.name;
|
||||
$$ LANGUAGE SQL STABLE;
|
||||
|
||||
CREATE FUNCTION actors_channels(actor actors) RETURNS SETOF entities AS $$
|
||||
SELECT entities.*
|
||||
FROM releases_actors
|
||||
LEFT JOIN releases ON releases.id = releases_actors.release_id
|
||||
LEFT JOIN entities ON entities.id = releases.entity_id
|
||||
WHERE releases_actors.actor_id = actor.id
|
||||
GROUP BY entities.id;
|
||||
$$ LANGUAGE SQL STABLE;
|
||||
|
||||
CREATE FUNCTION actors_scenes(actor actors, selected_tags text[], mode text DEFAULT 'all') RETURNS SETOF releases AS $$
|
||||
SELECT releases.*
|
||||
FROM releases
|
||||
LEFT JOIN
|
||||
releases_actors ON releases_actors.release_id = releases.id
|
||||
LEFT JOIN
|
||||
actors ON actors.id = releases_actors.actor_id
|
||||
LEFT JOIN
|
||||
releases_tags ON releases_tags.release_id = releases.id
|
||||
LEFT JOIN
|
||||
tags ON tags.id = releases_tags.tag_id
|
||||
WHERE actors.id = actor.id
|
||||
WHERE releases_actors.actor_id = actor.id
|
||||
AND CASE
|
||||
WHEN mode = 'any' AND array_length(selected_tags, 1) > 0
|
||||
THEN tags.slug = ANY(selected_tags)
|
||||
|
@ -886,6 +958,36 @@ exports.up = knex => Promise.resolve()
|
|||
END;
|
||||
$$ LANGUAGE SQL STABLE;
|
||||
|
||||
CREATE FUNCTION movies_actors(movie movies) RETURNS SETOF actors AS $$
|
||||
SELECT actors.*
|
||||
FROM movies_scenes
|
||||
LEFT JOIN
|
||||
releases ON releases.id = movies_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 movies_scenes.movie_id = movie.id
|
||||
AND actors.id IS NOT NULL
|
||||
GROUP BY actors.id
|
||||
ORDER BY actors.name, actors.gender
|
||||
$$ LANGUAGE SQL STABLE;
|
||||
|
||||
CREATE FUNCTION movies_tags(movie movies) RETURNS SETOF tags AS $$
|
||||
SELECT tags.*
|
||||
FROM movies_scenes
|
||||
LEFT JOIN
|
||||
releases ON releases.id = movies_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 movies_scenes.movie_id = movie.id
|
||||
AND tags.id IS NOT NULL
|
||||
GROUP BY tags.id
|
||||
ORDER BY tags.priority DESC
|
||||
$$ LANGUAGE SQL STABLE;
|
||||
|
||||
CREATE FUNCTION releases_is_new(release releases) RETURNS boolean AS $$
|
||||
SELECT EXISTS(SELECT true WHERE (SELECT id FROM batches ORDER BY created_at DESC LIMIT 1) = release.created_batch_id);
|
||||
$$ LANGUAGE sql STABLE;
|
||||
|
@ -895,24 +997,11 @@ exports.up = knex => Promise.resolve()
|
|||
.then(() => { // eslint-disable-line arrow-body-style
|
||||
// allow vim fold
|
||||
return knex.raw(`
|
||||
CREATE VIEW movie_actors AS
|
||||
SELECT releases_movies.movie_id, releases_actors.actor_id FROM releases_movies
|
||||
LEFT JOIN releases ON releases.id = releases_movies.scene_id
|
||||
LEFT JOIN releases_actors ON releases_actors.release_id = releases.id
|
||||
GROUP BY movie_id, actor_id;
|
||||
|
||||
CREATE VIEW movie_tags AS
|
||||
SELECT releases_movies.movie_id, releases_tags.tag_id FROM releases_movies
|
||||
LEFT JOIN releases ON releases.id = releases_movies.scene_id
|
||||
LEFT JOIN releases_tags ON releases_tags.release_id = releases.id
|
||||
GROUP BY movie_id, tag_id;
|
||||
|
||||
COMMENT ON VIEW movie_actors IS E'@foreignKey (movie_id) references releases (id)\n@foreignKey (actor_id) references actors (id)';
|
||||
COMMENT ON VIEW movie_tags IS E'@foreignKey (movie_id) references releases (id)\n@foreignKey (tag_id) references tags (id)';
|
||||
|
||||
COMMENT ON COLUMN actors.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 FUNCTION actors_tags IS E'@sortable';
|
||||
COMMENT ON FUNCTION actors_channels IS E'@sortable';
|
||||
COMMENT ON FUNCTION actors_scenes IS E'@sortable';
|
||||
`);
|
||||
});
|
||||
|
@ -920,9 +1009,6 @@ exports.up = knex => Promise.resolve()
|
|||
exports.down = (knex) => { // eslint-disable-line arrow-body-style
|
||||
// allow vim fold
|
||||
return knex.raw(`
|
||||
DROP VIEW IF EXISTS movie_actors;
|
||||
DROP VIEW IF EXISTS movie_tags;
|
||||
|
||||
DROP TABLE IF EXISTS releases_actors CASCADE;
|
||||
DROP TABLE IF EXISTS releases_movies CASCADE;
|
||||
DROP TABLE IF EXISTS releases_directors CASCADE;
|
||||
|
@ -934,6 +1020,10 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
|
|||
DROP TABLE IF EXISTS releases_tags CASCADE;
|
||||
DROP TABLE IF EXISTS releases_search CASCADE;
|
||||
|
||||
DROP TABLE IF EXISTS movies_covers CASCADE;
|
||||
DROP TABLE IF EXISTS movies_scenes CASCADE;
|
||||
DROP TABLE IF EXISTS movies_trailers CASCADE;
|
||||
|
||||
DROP TABLE IF EXISTS batches CASCADE;
|
||||
|
||||
DROP TABLE IF EXISTS actors_avatars CASCADE;
|
||||
|
@ -951,6 +1041,7 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
|
|||
DROP TABLE IF EXISTS networks_social CASCADE;
|
||||
DROP TABLE IF EXISTS tags_posters CASCADE;
|
||||
DROP TABLE IF EXISTS tags_photos CASCADE;
|
||||
DROP TABLE IF EXISTS movies CASCADE;
|
||||
DROP TABLE IF EXISTS releases CASCADE;
|
||||
DROP TABLE IF EXISTS actors CASCADE;
|
||||
DROP TABLE IF EXISTS directors CASCADE;
|
||||
|
@ -974,8 +1065,12 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
|
|||
|
||||
DROP FUNCTION IF EXISTS releases_is_new;
|
||||
DROP FUNCTION IF EXISTS actors_tags;
|
||||
DROP FUNCTION IF EXISTS actors_channels;
|
||||
DROP FUNCTION IF EXISTS actors_scenes;
|
||||
|
||||
DROP FUNCTION IF EXISTS movies_actors;
|
||||
DROP FUNCTION IF EXISTS movies_tags;
|
||||
|
||||
DROP TEXT SEARCH CONFIGURATION IF EXISTS traxxx;
|
||||
DROP TEXT SEARCH DICTIONARY IF EXISTS traxxx_dict;
|
||||
`);
|
||||
|
|
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 45 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 756 B |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 7.9 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 8.7 KiB |
After Width: | Height: | Size: 9.4 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 9.5 KiB |
After Width: | Height: | Size: 8.6 KiB |
After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |