Compare commits

..

5 Commits

Author SHA1 Message Date
DebaucheryLibrarian 6584a46d53 1.119.0 2020-07-15 05:12:43 +02:00
DebaucheryLibrarian 5b886b3917 Improved actor extraction for fcuk scraper. Changed 'copyright' to 'credit'. Redused entity page favicon size. 2020-07-15 05:12:29 +02:00
DebaucheryLibrarian c62df2228b Added scraper for FCUK's coed sites. 2020-07-15 04:51:39 +02:00
DebaucheryLibrarian 17b3ba1272 Added partial 'fcuk' (Exploited College Girls) scraper. Added file parameter for actor names and scene URLs. 2020-07-15 03:24:47 +02:00
DebaucheryLibrarian eca54c2a09 Improved sidebar design, added sfw and theme toggles. 2020-07-15 00:15:00 +02:00
99 changed files with 589 additions and 156 deletions

View File

@ -43,7 +43,7 @@
>
<img
:src="sfw ? `/img/${actor.avatar.sfw.thumbnail}` : `/media/${actor.avatar.thumbnail}`"
:title="actor.avatar.copyright && `© ${actor.avatar.copyright}`"
:title="actor.avatar.credit && `© ${actor.avatar.credit}`"
class="avatar"
>
</a>

View File

@ -33,6 +33,13 @@
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">
@ -143,8 +150,6 @@ export default {
</script>
<style lang="scss">
@import 'theme';
.gender-link {
&.selected .gender .icon {
fill: var(--text-light);
@ -173,7 +178,7 @@ export default {
</style>
<style lang="scss" scoped>
@import 'theme';
@import 'breakpoints';
.actors {
display: flex;
@ -253,13 +258,15 @@ export default {
}
}
@media(max-width: $breakpoint0) {
.tiles {
grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
}
@media(max-width: $breakpoint) {
.genders {
flex-direction: column;
}
}
@media(max-width: $breakpoint-micro) {
.tiles {
grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
}
}
</style>

View File

@ -18,7 +18,7 @@
:src="sfw ? `/img/${actor.avatar.sfw.thumbnail}` : `/media/${actor.avatar.thumbnail}`"
:data-src="sfw ? `/img/${actor.avatar.sfw.thumbnail}` : `/media/${actor.avatar.thumbnail}`"
:data-loading="sfw ? `/img/${actor.avatar.sfw.lazy}` : `/media/${actor.avatar.lazy}`"
:title="actor.avatar.copyright && `© ${actor.avatar.copyright}`"
:title="actor.avatar.credit && `© ${actor.avatar.credit}`"
class="avatar photo"
@load="$parent.$emit('load')"
>
@ -36,7 +36,7 @@
:src="sfw ? `/img/${photo.sfw.thumbnail}` : `/media/${photo.thumbnail}`"
:data-src="sfw ? `/img/${photo.sfw.thumbnail}` : `/media/${photo.thumbnail}`"
:data-loading="sfw ? `/img/${photo.sfw.lazy}` : `/media/${photo.lazy}`"
:title="`© ${photo.copyright || photo.entity.name}`"
:title="`© ${photo.credit || photo.entity.name}`"
class="photo"
@load="$parent.$emit('load')"
>

View File

@ -215,11 +215,14 @@ export default {
height: 2.5rem;
}
.logo-parent,
.favicon {
.logo-parent {
height: 1.5rem;
}
.favicon {
height: 1rem;
}
.name {
color: var(--text-light);
display: flex;

View File

@ -1,11 +1,16 @@
<template>
<header class="header">
<div class="header-nav">
<Icon
icon="menu"
<div
class="sidebar-toggle"
@click.native.stop="toggleSidebar"
@click.stop="toggleSidebar"
>
<Icon icon="menu" />
<div
class="logo"
v-html="logo"
/>
</div>
<router-link
to="/"
@ -68,7 +73,7 @@
<div class="header-toggles">
<Icon
v-show="!sfw"
v-tooltip="'Hit S to use SFW mode'"
v-tooltip="'Hit S to enable safe mode'"
icon="flower"
class="toggle noselect"
@click.native="setSfw(true)"
@ -76,9 +81,9 @@
<Icon
v-show="sfw"
v-tooltip="'Hit N to use NSFW mode'"
icon="flower"
class="toggle active noselect"
v-tooltip="'Hit N to disable safe mode'"
icon="evil2"
class="toggle noselect"
@click.native="setSfw(false)"
/>
@ -207,14 +212,27 @@ export default {
.sidebar-toggle {
display: none;
height: 100%;
.logo {
fill: var(--primary);
padding: .5rem;
}
.icon {
display: inline-block;
fill: var(--shadow-modest);
padding: 0 1rem;
width: 1.5rem;
height: 100%;
}
&:hover {
fill: var(--primary);
cursor: pointer;
.icon {
fill: var(--primary);
}
}
}
@ -335,16 +353,13 @@ export default {
}
@media(max-width: $breakpoint-micro) {
.nav {
.nav,
.header-logo {
display: none;
}
.sidebar-toggle {
display: inline-block;
}
.header-logo {
padding: 0 0 0 .5rem;
display: flex;
}
}

View File

@ -1,8 +1,10 @@
<template>
<div class="sidebar-container">
<div
class="sidebar"
@click.stop
>
<div class="sidebar-section">
<div class="sidebar-header">
<Icon
icon="cross2"
@ -88,11 +90,57 @@
</ul>
</nav>
</div>
<div class="sidebar-section toggles noselect">
<label
v-show="sfw"
class="toggle"
@click="setSfw(false)"
><Icon icon="evil2" />Disable safe mode</label>
<label
v-show="!sfw"
class="toggle"
@click="setSfw(true)"
><Icon icon="flower" />Enable safe mode</label>
<label
v-show="theme === 'dark'"
class="toggle"
@click="setTheme('light')"
><Icon icon="sun" />Use light theme</label>
<label
v-show="theme === 'light'"
class="toggle"
@click="setTheme('dark')"
><Icon icon="moon" />Use dark theme</label>
</div>
</div>
</div>
</template>
<script>
import { mapState } from 'vuex';
import logo from '../../img/logo.svg';
function sfw(state) {
return state.ui.sfw;
}
function theme(state) {
return state.ui.theme;
}
function setTheme(newTheme) {
this.$store.dispatch('setTheme', newTheme);
}
function setSfw(enabled) {
this.$store.dispatch('setSfw', enabled);
}
export default {
props: {
toggleSidebar: {
@ -105,17 +153,34 @@ export default {
logo,
};
},
computed: {
...mapState({
sfw,
theme,
}),
},
methods: {
setTheme,
setSfw,
},
};
</script>
<style lang="scss" scoped>
.sidebar-container {
height: 100%;
width: 100%;
position: absolute;
z-index: 10;
background: var(--darken-hint);
}
.sidebar {
display: flex;
flex-direction: column;
justify-content: space-between;
width: 15rem;
height: 100%;
position: absolute;
z-index: 10;
color: var(--text);
background: var(--background);
box-shadow: 0 0 3px var(--darken);
@ -130,13 +195,13 @@ export default {
}
.sidebar-close {
width: 1.5rem;
width: 1.25rem;
height: 100%;
padding: 0 1rem;
fill: var(--darken);
padding: 0 1.125rem;
fill: var(--shadow-modest);
&:hover {
fill: var(--text);
fill: var(--primary);
cursor: pointer;
}
}
@ -167,19 +232,49 @@ export default {
}
.nav-link {
color: var(--shadow-strong);
color: var(--shadow);
display: block;
padding: 1rem;
text-decoration: none;
font-weight: bold;
&:hover {
color: var(--primary);
color: var(--shadow-strong);
}
&.active {
background: var(--primary);
color: var(--text-light);
color: var(--primary);
}
}
.toggles {
border-top: solid 1px var(--shadow-hint);
margin: .5rem 0 0 0;
}
.toggle {
display: flex;
align-self: flex-end;
padding: 1rem;
color: var(--shadow);
font-weight: bold;
.icon {
fill: var(--shadow);
margin: 0 1rem 0 0;
}
&.active .icon {
fill: var(--primary);
}
&:hover {
cursor: pointer;
color: var(--shadow-strong);
&:not(.active) .icon {
fill: var(--shadow-strong);
}
}
}

View File

@ -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>cherry</title>
<path d="M13 6c-1.111 0-2.080 0.604-2.599 1.501-1.043-0.305-2.026-0.688-2.939-1.149-1.219-0.616-2.317-1.369-3.264-2.238-1.212-1.113-1.939-2.219-2.198-2.728v-0.385c0-0.552-0.448-1-1-1s-1 0.448-1 1 0.448 1 1 1c0.006 0 0.013-0 0.019-0 0.117 1.618 0.765 3.819 1.719 5.781 0.587 1.208 1.248 2.255 1.965 3.11 0.026 0.031 0.051 0.061 0.077 0.091-0.484 0.533-0.78 1.241-0.78 2.018 0 1.657 1.343 3 3 3s3-1.343 3-3-1.343-3-3-3c-0.52 0-1.009 0.132-1.435 0.365-0.689-0.796-1.342-1.816-1.928-3.021-0.599-1.233-1.077-2.59-1.359-3.797 0.325 0.391 0.722 0.82 1.2 1.263 1.015 0.94 2.191 1.752 3.497 2.416 0.958 0.486 1.987 0.892 3.077 1.215-0.034 0.181-0.052 0.368-0.052 0.56 0 1.657 1.343 3 3 3s3-1.343 3-3-1.343-3-3-3z"></path>
</svg>

After

Width:  |  Height:  |  Size: 873 B

View File

@ -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>cool2</title>
<path d="M8 0c-4.418 0-8 3.582-8 8s3.582 8 8 8c4.418 0 8-3.582 8-8s-3.582-8-8-8zM8 13c-0.757 0-1.475-0.169-2.118-0.47l0.518-0.864c0.49 0.214 1.031 0.334 1.6 0.334 1.456 0 2.731-0.778 3.43-1.942l0.858 0.515c-0.874 1.454-2.467 2.427-4.288 2.427zM13 6c0 0.55-0.45 1-1 1h-2c-0.55 0-1-0.45-1-1h-2c0 0.55-0.45 1-1 1h-2c-0.55 0-1-0.45-1-1v-1.5c0-0.275 0.225-0.5 0.5-0.5h3c0.275 0 0.5 0.225 0.5 0.5v0.5h2v-0.5c0-0.275 0.225-0.5 0.5-0.5h3c0.275 0 0.5 0.225 0.5 0.5v1.5z"></path>
</svg>

After

Width:  |  Height:  |  Size: 629 B

View File

@ -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>evil2</title>
<path d="M16 1c0-0.711-0.149-1.387-0.416-2-0.525 1.201-1.507 2.155-2.726 2.643-1.347-1.031-3.030-1.643-4.857-1.643s-3.51 0.613-4.857 1.643c-1.22-0.488-2.202-1.443-2.726-2.643-0.268 0.613-0.416 1.289-0.416 2 0 1.15 0.388 2.208 1.040 3.053-0.662 1.165-1.040 2.512-1.040 3.947 0 4.418 3.582 8 8 8s8-3.582 8-8c0-1.436-0.378-2.783-1.040-3.947 0.652-0.845 1.040-1.903 1.040-3.053zM9.001 5.946c0.032-0.741 0.706-1.234 1.275-1.518 0.543-0.271 1.080-0.407 1.102-0.413 0.268-0.067 0.539 0.096 0.606 0.364s-0.096 0.539-0.364 0.606c-0.275 0.070-0.602 0.189-0.89 0.334 0.166 0.179 0.268 0.418 0.268 0.681 0 0.552-0.448 1-1 1s-1-0.448-1-1c0-0.018 0.001-0.036 0.002-0.054zM4.015 4.379c0.067-0.268 0.338-0.431 0.606-0.364 0.023 0.006 0.559 0.141 1.102 0.413 0.568 0.284 1.243 0.776 1.275 1.518 0.001 0.018 0.002 0.036 0.002 0.054 0 0.552-0.448 1-1 1s-1-0.448-1-1c0-0.263 0.102-0.503 0.268-0.681-0.288-0.144-0.614-0.264-0.89-0.334-0.268-0.067-0.431-0.338-0.364-0.606zM8 13c-1.82 0-3.413-0.973-4.288-2.427l1.286-0.772c0.612 1.018 1.727 1.699 3.002 1.699s2.389-0.681 3.002-1.699l1.286 0.772c-0.874 1.454-2.467 2.427-4.288 2.427z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -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>fire</title>
<path d="M5.016 16c-1.066-2.219-0.498-3.49 0.321-4.688 0.897-1.312 1.129-2.61 1.129-2.61s0.706 0.917 0.423 2.352c1.246-1.387 1.482-3.598 1.293-4.445 2.817 1.969 4.021 6.232 2.399 9.392 8.631-4.883 2.147-12.19 1.018-13.013 0.376 0.823 0.448 2.216-0.313 2.893-1.287-4.879-4.468-5.879-4.468-5.879 0.376 2.516-1.364 5.268-3.042 7.324-0.059-1.003-0.122-1.696-0.649-2.656-0.118 1.823-1.511 3.309-1.889 5.135-0.511 2.473 0.383 4.284 3.777 6.197z"></path>
</svg>

After

Width:  |  Height:  |  Size: 606 B

View File

@ -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>grid6</title>
<path d="M0 0h7v7h-7zM9 0h7v7h-7zM0 9h7v7h-7zM9 9h7v7h-7z"></path>
</svg>

After

Width:  |  Height:  |  Size: 226 B

View File

@ -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>leaf</title>
<path d="M15.802 2.102c-1.73-1.311-4.393-2.094-7.124-2.094-3.377 0-6.129 1.179-7.549 3.235-0.667 0.965-1.036 2.109-1.097 3.398-0.054 1.148 0.139 2.418 0.573 3.784 1.482-4.444 5.622-7.923 10.395-7.923 0 0-4.466 1.175-7.274 4.816-0.002 0.002-0.039 0.048-0.103 0.136-0.564 0.754-1.055 1.612-1.423 2.583-0.623 1.482-1.2 3.515-1.2 5.965h2c0 0-0.304-1.91 0.224-4.106 0.873 0.118 1.654 0.177 2.357 0.177 1.839 0 3.146-0.398 4.115-1.252 0.868-0.765 1.347-1.794 1.854-2.882 0.774-1.663 1.651-3.547 4.198-5.002 0.146-0.083 0.24-0.234 0.251-0.402s-0.063-0.329-0.197-0.431z"></path>
</svg>

After

Width:  |  Height:  |  Size: 729 B

View File

@ -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>menu7</title>
<path d="M1 3h14v2h-14v-2z"></path>
<path d="M1 7h14v2h-14v-2z"></path>
<path d="M1 11h14v2h-14v-2z"></path>
</svg>

After

Width:  |  Height:  |  Size: 268 B

View File

@ -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>screwdriver</title>
<path d="M15.852 12.352l-6.204-6.204c-0.345-0.345-1.2-0.067-2.043 0.617l-4.605-4.605-0.5-1.16-1.601-1-0.899 0.899 1 1.601 1.16 0.5 4.605 4.605c-0.685 0.843-0.962 1.698-0.617 2.043 0 0 0 0 0 0l6.203 6.203c0.405 0.405 1.518-0.049 2.484-1.016s1.421-2.079 1.016-2.484zM13.92 13.92c-0.116 0.116-0.268 0.174-0.42 0.174s-0.304-0.058-0.42-0.174l-5-5c-0.232-0.232-0.232-0.608 0-0.84s0.608-0.232 0.84 0l5 5c0.232 0.232 0.232 0.608 0 0.84z"></path>
</svg>

After

Width:  |  Height:  |  Size: 603 B

View File

@ -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>screwdriver2</title>
<path d="M15.852 12.352l-3.403-3.403c-0.649 0.149-1.358-0.029-1.863-0.535s-0.683-1.214-0.535-1.863l-0.403-0.403c-0.345-0.345-1.2-0.067-2.043 0.617l-4.605-4.605-0.5-1.16-1.601-1-0.899 0.899 1 1.601 1.16 0.5 4.605 4.605c-0.685 0.843-0.962 1.698-0.617 2.043 0 0 0 0 0 0l0.403 0.403c0.649-0.149 1.358 0.029 1.863 0.535s0.683 1.214 0.535 1.863l3.403 3.403c0.405 0.405 1.518-0.049 2.484-1.016s1.421-2.079 1.016-2.484zM13.92 13.92c-0.116 0.116-0.268 0.174-0.42 0.174s-0.304-0.058-0.42-0.174l-5-5c-0.232-0.232-0.232-0.608 0-0.84s0.608-0.232 0.84 0l5 5c0.232 0.232 0.232 0.608 0 0.84z"></path>
</svg>

After

Width:  |  Height:  |  Size: 751 B

View File

@ -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>skull</title>
<path d="M12.803 3.197c-1.417-1.417-3.3-2.197-5.303-2.197s-3.887 0.78-5.303 2.197-2.197 3.3-2.197 5.303c0 0.276 0.224 0.5 0.5 0.5 1.93 0 3.5 1.57 3.5 3.5v2c0 0.276 0.224 0.5 0.5 0.5s0.5-0.224 0.5-0.5v-2c0-0.276 0.224-0.5 0.5-0.5s0.5 0.224 0.5 0.5v3c0 0.276 0.224 0.5 0.5 0.5s0.5-0.224 0.5-0.5v-3c0-0.276 0.224-0.5 0.5-0.5s0.5 0.224 0.5 0.5v3c0 0.276 0.224 0.5 0.5 0.5s0.5-0.224 0.5-0.5v-3c0-0.276 0.224-0.5 0.5-0.5s0.5 0.224 0.5 0.5v2c0 0.276 0.224 0.5 0.5 0.5s0.5-0.224 0.5-0.5v-2c0-1.93 1.57-3.5 3.5-3.5 0.276 0 0.5-0.224 0.5-0.5 0-2.003-0.78-3.887-2.197-5.303zM6 7c0 0.55-0.45 1-1 1h-1c-0.55 0-1-0.45-1-1v-1c0-0.55 0.45-1 1-1h1c0.55 0 1 0.45 1 1v1zM12 7c0 0.55-0.45 1-1 1h-1c-0.55 0-1-0.45-1-1v-1c0-0.55 0.45-1 1-1h1c0.55 0 1 0.45 1 1v1z"></path>
</svg>

After

Width:  |  Height:  |  Size: 909 B

View File

@ -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>smile2</title>
<path d="M8 0c-4.418 0-8 3.582-8 8s3.582 8 8 8 8-3.582 8-8-3.582-8-8-8zM11 4c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1zM5 4c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1zM8 13c-1.82 0-3.413-0.973-4.288-2.427l1.286-0.772c0.612 1.018 1.727 1.699 3.002 1.699s2.389-0.681 3.002-1.699l1.286 0.772c-0.874 1.454-2.467 2.427-4.288 2.427z"></path>
</svg>

After

Width:  |  Height:  |  Size: 525 B

View File

@ -262,9 +262,9 @@ function initActorActions(store, router) {
letter,
gender,
}) {
const genderFilter = gender === null
? 'isNull: true'
: `equalTo: "${gender}"`;
const genderFilter = (gender === null && 'gender: { isNull: true }')
|| (gender === 'all' && ' ')
|| `gender: { equalTo: "${gender}" }`;
const { connection: { actors, totalCount } } = await graphql(`
query Actors(
@ -283,10 +283,8 @@ function initActorActions(store, router) {
name: {
startsWith: $letter
}
gender: {
${genderFilter}
}
}
) {
totalCount
actors: nodes {

View File

@ -116,7 +116,7 @@ function initUiActions(_store, _router) {
thumbnail
lazy
comment
copyright
credit
}
birthCountry: countryByBirthCountryAlpha2 {
alpha2
@ -135,7 +135,7 @@ function initUiActions(_store, _router) {
thumbnail
lazy
comment
copyright
credit
}
birthCountry: countryByBirthCountryAlpha2 {
alpha2

View File

@ -37,7 +37,7 @@ exports.up = knex => Promise.resolve()
table.float('entropy');
table.text('scraper', 32);
table.text('copyright', 100);
table.text('credit', 100);
table.text('source', 2100);
table.text('source_page', 2100);
@ -279,6 +279,9 @@ exports.up = knex => Promise.resolve()
table.integer('hip', 3);
table.boolean('natural_boobs');
table.integer('penis_length', 3);
table.integer('penis_girth', 3);
table.integer('height', 3);
table.integer('weight', 3);
table.text('eyes');
@ -349,6 +352,9 @@ exports.up = knex => Promise.resolve()
table.integer('hip', 3);
table.boolean('natural_boobs');
table.integer('penis_length', 3);
table.integer('penis_girth', 3);
table.integer('height', 3);
table.integer('weight', 3);
table.text('eyes');

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "traxxx",
"version": "1.118.1",
"version": "1.119.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "traxxx",
"version": "1.118.1",
"version": "1.119.0",
"description": "All the latest porn releases in one place",
"main": "src/app.js",
"scripts": {

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -180,6 +180,7 @@ const tags = [
{
name: 'behind the scenes',
slug: 'behind-the-scenes',
priority: 6,
},
{
name: 'big dick',

View File

@ -139,6 +139,10 @@ const networks = [
url: 'https://www.cherrypimps.com',
description: 'CherryPimps your premium porn site to Download and Stream the hottest and most exclusive 4K HD videos and pictures on your phone, tablet, TV or console.',
},
{
slug: 'fcuk',
name: 'Fcuk',
},
{
slug: 'freeones',
name: 'FreeOnes',

View File

@ -2085,6 +2085,66 @@ const sites = [
url: 'https://www.freeones.com',
parent: 'freeones',
},
// FCUK
{
name: 'Exploited College Girls',
slug: 'exploitedcollegegirls',
alias: ['excogi', 'ecg'],
url: 'https://exploitedcollegegirls.com',
parent: 'fcuk',
parameters: {
blog: true,
},
},
{
name: 'Backroom Casting Couch',
slug: 'backroomcastingcouch',
url: 'https://backroomcastingcouch.com',
parent: 'fcuk',
parameters: {
blog: true,
},
},
{
name: 'Black Ambush',
slug: 'blackambush',
alias: ['interracial', 'bbc'],
url: 'https://blackambush.com',
parent: 'fcuk',
parameters: {
blog: true,
},
},
{
name: 'Nebraska Coeds',
slug: 'nebraskacoeds',
url: 'https://nebraskacoeds.com',
parent: 'fcuk',
},
{
name: 'South Beach Coeds',
slug: 'southbeachcoeds',
url: 'https://southbeachcoeds.com',
parent: 'fcuk',
},
{
name: 'Spring Break Life',
slug: 'springbreaklife',
url: 'https://springbreaklife.com',
parent: 'fcuk',
},
{
name: 'Euro Coeds',
slug: 'eurocoeds',
url: 'https://eurocoeds.com',
parent: 'fcuk',
},
{
name: 'After Hours Exposed',
slug: 'afterhoursexposed',
url: 'https://afterhoursexposed.com',
parent: 'fcuk',
},
// FOR BONDAGE
{
name: 'Crowd Bondage',

View File

@ -571,7 +571,7 @@ const sfw = Object.entries({
['iFBIdX54BOk', 'Keagan Henman'],
],
})
.map(([category, photos]) => photos.map(([photo, copyright], index) => ({
.map(([category, photos]) => photos.map(([photo, credit], index) => ({
id: photo,
path: `sfw/${category}/${photo}.jpeg`,
thumbnail: `sfw/${category}/thumbs/${photo}.jpeg`,
@ -580,8 +580,7 @@ const sfw = Object.entries({
sfw_media_id: null,
group: category,
index,
copyright,
comment: `Courtesy of ${copyright}`,
credit,
})))
.flat();

View File

@ -10,6 +10,7 @@ const fetchUpdates = require('./updates');
const { fetchScenes, fetchMovies } = require('./deep');
const { storeReleases, updateReleasesSearch } = require('./store-releases');
const { scrapeActors } = require('./actors');
const getFileEntries = require('./utils/file-entries');
async function init() {
if (argv.server) {
@ -21,13 +22,19 @@ async function init() {
await updateReleasesSearch();
}
const actors = argv.actors && await scrapeActors(argv.actors);
const actorsFromFile = argv.actorsFile && await getFileEntries(argv.actorsFile);
const actorNames = (argv.actors || []).concat(actorsFromFile || []);
const actors = actorNames.length > 0 && await scrapeActors(actorNames);
const actorBaseScenes = argv.actors && argv.actorScenes && actors.map(actor => actor.releases).flat().filter(Boolean);
const updateBaseScenes = (argv.all || argv.channels || argv.networks) && await fetchUpdates();
const scenesFromFile = argv.scenesFile && await getFileEntries(argv.scenesFile);
const sceneUrls = (argv.scenes || []).concat(scenesFromFile || []);
const deepScenes = argv.deep
? await fetchScenes([...(argv.scenes || []), ...(updateBaseScenes || []), ...(actorBaseScenes || [])])
? await fetchScenes([...(sceneUrls), ...(updateBaseScenes || []), ...(actorBaseScenes || [])])
: [...(updateBaseScenes || []), ...(actorBaseScenes || [])];
const sceneMovies = deepScenes && argv.sceneMovies && deepScenes.map(scene => scene.movie).filter(Boolean);

View File

@ -30,6 +30,10 @@ const { argv } = yargs
type: 'array',
alias: 'actor',
})
.option('actors-file', {
describe: 'Scrape actors names from file',
type: 'string',
})
.option('actor-scenes', {
describe: 'Fetch all scenes for an actor',
type: 'boolean',
@ -53,10 +57,14 @@ const { argv } = yargs
alias: 'with-profiles',
default: false,
})
.option('scene', {
.option('scenes', {
describe: 'Scrape scene info from URL',
type: 'array',
alias: 'scenes',
alias: 'scene',
})
.option('scenes-file', {
describe: 'Scrape scene info from URLs in a file',
type: 'string',
})
.option('movie', {
describe: 'Scrape movie info from URL',

View File

@ -101,7 +101,7 @@ function toBaseSource(rawSource) {
if (rawSource.attempts) baseSource.attempts = rawSource.attempts;
if (rawSource.queueMethod) baseSource.queueMethod = rawSource.queueMethod;
if (rawSource.copyright) baseSource.copyright = rawSource.copyright;
if (rawSource.credit !== undefined) baseSource.credit = rawSource.credit;
if (rawSource.comment) baseSource.comment = rawSource.comment;
if (rawSource.group) baseSource.group = rawSource.group;
@ -569,7 +569,7 @@ function curateMediaEntry(media, index) {
source: media.src,
source_page: media.url,
scraper: media.scraper,
copyright: media.copyright,
credit: media.credit,
comment: media.comment,
};
@ -685,7 +685,7 @@ async function associateAvatars(profiles) {
? {
...profile,
avatarBaseMedia: toBaseMedias([profile.avatar], 'avatars', {
copyright: profile.network?.name || profile.site?.name || null,
credit: (profile.credit !== undefined && (profile.network?.name || profile.site?.name)) || null,
scraper: profile.scraper || null,
})[0],
}

View File

@ -69,7 +69,7 @@ function scrapeProfile(html) {
profile.avatar = {
src: `http://www.boobpedia.com${avatarPath}`,
copyright: null,
credit: null,
};
}

155
src/scrapers/fcuk.js Normal file
View File

@ -0,0 +1,155 @@
'use strict';
const qu = require('../utils/qu');
// TODO: profile scraping
function scrapeLatestBlog(scenes, channel) {
return scenes.map(({ query }) => {
const release = {};
release.url = query.url('a.more:not([href*="/join.php"])', 'href', { origin: channel.url });
if (release.url) {
release.entryId = new URL(release.url).pathname.match(/\/scene\/(\d+)\/(\d+)/).slice(1, 3).join('-');
} else {
release.entryId = query.img('.bigthumb').match(/\/scenes\/(\w+)/)?.[1];
}
release.title = query.q('h5 strong', true)?.match(/. - (.+)$/)[1] || query.text('.videos h3');
release.description = query.text('p');
release.date = query.date('h5 strong, .videos h3', 'MMM. DD, YYYY', /\w+. \d{2}, \d{4}/);
// remove common patterns so only the name is left
const curatedTitle = release.title.replace(/\b(part \d|\banal|bts)\b/gi, '').trim();
if (!/\band\b/.test(curatedTitle) && new RegExp(curatedTitle).test(release.description)) {
// scene title is probably the actor name
release.actors = [release.title];
}
release.poster = query.img('.bigthumb', null, { origin: channel.url });
release.photos = query.imgs('.smallthumb', null, { origin: channel.url });
release.tags = query.all('a[href*="/keywords"]', true);
return release;
});
}
function scrapeAll(scenes, channel) {
return scenes.map(({ query }) => {
const release = {};
release.url = query.url('.updateInfo h5 a:not([href*="content/"]):not([href*="#coming"])');
release.entryId = query.url('.updateThumb img', 'alt');
release.title = query.q('.updateInfo h5 a', true);
release.actors = query.all('.tour_update_models a', true);
release.date = query.date('.availdate, .updateInfo p span:nth-child(2)', 'MM/DD/YYYY');
release.poster = query.img('.updateThumb img');
const trailer = query.q('.updateInfo h5 a', 'onclick')?.match(/'(.+)'/)?.[1];
if (trailer) {
release.trailer = {
src: `${channel.url}${trailer}`,
};
}
return release;
});
}
function scrapeSceneBlog({ query }, url, channel) {
const release = {};
release.entryId = new URL(url).pathname.match(/\/scene\/(\d+)\/(\d+)/).slice(1, 3).join('-');
release.title = query.text('h4 strong, .videos h3');
release.description = query.q('#about p, .videos p', true);
const actors = query.urls('a[href*="/girl/"]').map(actorUrl => actorUrl.match(/video-([\w\s]+)/)?.[1]).filter(Boolean);
if (actors.length > 0) {
release.actors = actors;
} else {
// release.actors = [query.q('.previewmed h5 strong', true)?.match(/^([\w\s]+),/)?.[0] || query.q('.videos h3', true)].filter(Boolean);
release.actors = [release.title];
}
release.tags = query.all('.info a[href*="/keywords"], .buttons a[href*="/keywords"]', true);
release.poster = query.img('#info .main-preview, .bigthumb', null, { origin: channel.url });
release.photos = [query.img('.previewmed img', null, { origin: channel.url })].concat(query.imgs('.hd-clip img, .smallthumb', null, { origin: channel.url })).filter(photo => photo);
return release;
}
function scrapeScene({ query, html }, url, channel) {
const release = {};
release.title = query.q('.updatesBlock h2', true);
release.poster = query.meta('property="og:image"');
release.entryId = release.poster.match(/\/content\/(.*)\//)?.[1];
const trailer = html.match(/src="(.+\.mp4)"/)?.[1];
if (trailer) {
release.trailer = {
src: `${channel.url}${trailer}`,
};
}
return release;
}
async function fetchLatestBlog(channel, page) {
const url = `${channel.url}/free/updates/videos/${(page - 1) * 10}`;
const res = await qu.getAll(url, '.videos');
return res.ok ? scrapeLatestBlog(res.items, channel) : res.status;
}
async function fetchLatest(channel, page = 1) {
if (channel.parameters?.blog) {
return fetchLatestBlog(channel, page);
}
const url = `${channel.url}/categories/Movies_${page}_d.html`;
const res = await qu.getAll(url, '.bodyArea .updateItem');
return res.ok ? scrapeAll(res.items, channel) : res.status;
}
async function fetchUpcoming(channel) {
if (channel.parameters?.blog) {
return [];
}
const res = await qu.getAll(channel.url, '#owl-upcomingScenes .updateItem');
return res.ok ? scrapeAll(res.items, channel) : res.status;
}
async function fetchScene(url, channel) {
const res = await qu.get(url);
if (res.ok) {
if (channel.parameters?.blog) {
return scrapeSceneBlog(res.item, url, channel);
}
return scrapeScene(res.item, url, channel);
}
return res.status;
}
module.exports = {
fetchLatest,
fetchScene,
fetchUpcoming,
};

View File

@ -52,7 +52,7 @@ function scrapeProfile(html, actorName) {
profile.social = Array.from(document.querySelectorAll('.profile-meta-item a.social-icons'), el => el.href);
const avatar = document.querySelector('.profile-image-large img').src;
if (!avatar.match('placeholder')) profile.avatar = { src: avatar, copyright: null };
if (!avatar.match('placeholder')) profile.avatar = { src: avatar, credit: null };
return profile;
}

View File

@ -19,6 +19,7 @@ const evilangel = require('./evilangel');
const fakehub = require('./fakehub');
const famedigital = require('./famedigital');
const fantasymassage = require('./fantasymassage');
const fcuk = require('./fcuk');
const fullpornnetwork = require('./fullpornnetwork');
const girlsway = require('./girlsway');
const hush = require('./hush');
@ -93,6 +94,7 @@ module.exports = {
fakehub,
famedigital,
fantasymassage,
fcuk,
forbondage: porndoe,
fullpornnetwork,
girlsway,

View File

@ -53,15 +53,15 @@ async function filterUniqueReleases(latestReleases, accReleases) {
}
function needNextPage(uniqueReleases, pageAccReleases) {
if (argv.last && pageAccReleases.length < argv.last) {
// request for last N releases not yet satisfied
return true;
}
if (uniqueReleases.length === 0) {
return false;
}
if (argv.last && pageAccReleases.length < argv.last) {
// TODO: find a way to paginate if scraper filters page with multiple channels, see Kelly Madison
return true;
}
if (uniqueReleases.every(release => !!release.date)) {
const oldestReleaseOnPage = uniqueReleases
.sort((releaseA, releaseB) => releaseB.date - releaseA.date)

16
src/utils/file-entries.js Normal file
View File

@ -0,0 +1,16 @@
'use strict';
const fs = require('fs');
async function getFileEntries(location) {
if (!location) {
throw new Error('No filepath provided');
}
const file = await fs.promises.readFile(location, 'utf-8');
const entries = file.split(/\n/).map(entry => entry.trim()).filter(Boolean);
return entries;
}
module.exports = getFileEntries;

View File

@ -125,7 +125,7 @@ function date(context, selector, format, match, attr = 'textContent') {
return extractDate(dateString, format, match);
}
function image(context, selector = 'img', attr, origin, protocol = 'https') {
function image(context, selector = 'img', attr, { origin, protocol = 'https' } = {}) {
const imageEl = (attr && q(context, selector, attr))
|| q(context, selector, 'data-src')
|| q(context, selector, 'src');
@ -133,7 +133,7 @@ function image(context, selector = 'img', attr, origin, protocol = 'https') {
return prefixUrl(imageEl, origin, protocol);
}
function images(context, selector = 'img', attr, origin, protocol = 'https') {
function images(context, selector = 'img', attr, { origin, protocol = 'https' } = {}) {
const attribute = attr
|| (q(context, selector, 'data-src') && 'data-src')
|| (q(context, selector, 'src') && 'src');
@ -143,31 +143,31 @@ function images(context, selector = 'img', attr, origin, protocol = 'https') {
return imageEls.map(imageEl => prefixUrl(imageEl, origin, protocol));
}
function url(context, selector = 'a', attr = 'href', origin, protocol = 'https') {
function url(context, selector = 'a', attr = 'href', { origin, protocol = 'https' } = {}) {
const urlEl = q(context, selector, attr);
return attr ? prefixUrl(urlEl, origin, protocol) : urlEl;
}
function urls(context, selector = 'a', attr = 'href', origin, protocol = 'https') {
function urls(context, selector = 'a', attr = 'href', { origin, protocol = 'https' } = {}) {
const urlEls = all(context, selector, attr);
return attr ? urlEls.map(urlEl => prefixUrl(urlEl, origin, protocol)) : urlEls;
}
function poster(context, selector = 'video', attr = 'poster', origin, protocol = 'https') {
function poster(context, selector = 'video', attr = 'poster', { origin, protocol = 'https' } = {}) {
const posterEl = q(context, selector, attr);
return attr ? prefixUrl(posterEl, origin, protocol) : posterEl;
}
function video(context, selector = 'source', attr = 'src', origin, protocol = 'https') {
function video(context, selector = 'source', attr = 'src', { origin, protocol = 'https' } = {}) {
const trailerEl = q(context, selector, attr);
return attr ? prefixUrl(trailerEl, origin, protocol) : trailerEl;
}
function videos(context, selector = 'source', attr = 'src', origin, protocol = 'https') {
function videos(context, selector = 'source', attr = 'src', { origin, protocol = 'https' } = {}) {
const trailerEls = all(context, selector, attr);
return attr ? trailerEls.map(trailerEl => prefixUrl(trailerEl, origin, protocol)) : trailerEls;