2 Commits

Author SHA1 Message Date
DebaucheryLibrarian
4839a3b94c Allowing HTML in disclaimer. 2023-06-19 04:06:52 +02:00
DebaucheryLibrarian
d8b641e461 Removed references to effective date for older databases. 2022-02-25 22:20:28 +01:00
109 changed files with 553 additions and 1216 deletions

52
assets/components/container/container.vue Normal file → Executable file
View File

@@ -8,35 +8,40 @@
/> />
<transition name="slide"> <transition name="slide">
<Sidebar <Sidebar v-if="showSidebar" />
v-if="showSidebar"
@toggle-sidebar="(state) => toggleSidebar(state)"
@show-filters="(state) => toggleFilters(state)"
/>
</transition> </transition>
<Header <Header />
@toggle-sidebar="(state) => toggleSidebar(state)"
@show-filters="(state) => toggleFilters(state)"
/>
<p <p
v-if="config.showDisclaimer" v-if="config.showDisclaimer"
class="disclaimer" class="disclaimer"
>{{ config.disclaimer }}</p> v-html="config.disclaimer"
/>
<p
v-if="config.showAnnouncement"
class="announcement"
v-html="config.announcement"
/>
<div <div
ref="content" ref="content"
class="content" class="content"
@scroll="scroll" @scroll="scroll"
> >
<router-view @scroll="scrollToTop" /> <RouterView @scroll="scrollToTop" />
</div> </div>
<Filters <Filters
v-if="showFilters" v-if="showFilters"
@close="toggleFilters(false)" @close="toggleFilters(false)"
/> />
<Settings
v-if="showSettings"
@close="toggleSettings(false)"
/>
</div> </div>
</template> </template>
@@ -45,6 +50,7 @@ import Warning from './warning.vue';
import Header from '../header/header.vue'; import Header from '../header/header.vue';
import Sidebar from '../sidebar/sidebar.vue'; import Sidebar from '../sidebar/sidebar.vue';
import Filters from '../filters/filters.vue'; import Filters from '../filters/filters.vue';
import Settings from '../settings/settings.vue';
function toggleSidebar(state) { function toggleSidebar(state) {
this.showSidebar = typeof state === 'boolean' ? state : !this.showSidebar; this.showSidebar = typeof state === 'boolean' ? state : !this.showSidebar;
@@ -55,6 +61,11 @@ function toggleFilters(state) {
this.showSidebar = false; this.showSidebar = false;
} }
function toggleSettings(state) {
this.showSettings = state;
this.showSidebar = false;
}
async function setConsent(consent, includeQueer) { async function setConsent(consent, includeQueer) {
if (consent) { if (consent) {
this.showWarning = false; this.showWarning = false;
@@ -88,6 +99,9 @@ function scrollToTop() {
function mounted() { function mounted() {
document.addEventListener('click', this.blur); document.addEventListener('click', this.blur);
window.addEventListener('resize', this.resize); window.addEventListener('resize', this.resize);
this.events.on('toggleSettings', this.toggleSettings);
this.events.on('toggleSidebar', this.toggleSidebar);
} }
function beforeUnmount() { function beforeUnmount() {
@@ -101,12 +115,15 @@ export default {
Sidebar, Sidebar,
Warning, Warning,
Filters, Filters,
Settings,
}, },
data() { data() {
return { return {
showSidebar: false, showSidebar: false,
showWarning: localStorage.getItem('consent') !== window.env.sessionId, showWarning: localStorage.getItem('consent') !== window.env.sessionId,
showFilters: false, showFilters: false,
showSettings: false,
selected: null,
}; };
}, },
mounted, mounted,
@@ -114,6 +131,7 @@ export default {
methods: { methods: {
toggleSidebar, toggleSidebar,
toggleFilters, toggleFilters,
toggleSettings,
setConsent, setConsent,
blur, blur,
resize, resize,
@@ -183,13 +201,21 @@ export default {
</style> </style>
<style lang="scss" scoped> <style lang="scss" scoped>
.disclaimer { .disclaimer,
.announcement {
padding: .5rem 1rem; padding: .5rem 1rem;
margin: 0; margin: 0;
color: var(--text-light); color: var(--text-light);
background: var(--warn);
font-weight: bold; font-weight: bold;
box-shadow: inset 0 0 3px var(--darken-weak); box-shadow: inset 0 0 3px var(--darken-weak);
text-align: center; text-align: center;
} }
.disclaimer {
background: var(--warn);
}
.announcement {
background: var(--notice);
}
</style> </style>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,6 +3,12 @@
<div class="content-inner"> <div class="content-inner">
<SearchBar :placeholder="`Search ${totalCount} movies`" /> <SearchBar :placeholder="`Search ${totalCount} movies`" />
<TagFilter
class="filters-filter"
:filter="filter"
:available-tags="availableTags"
/>
<div <div
ref="tiles" ref="tiles"
class="tiles" class="tiles"
@@ -30,6 +36,7 @@
import MovieTile from './movie-tile.vue'; import MovieTile from './movie-tile.vue';
import SearchBar from '../search/bar.vue'; import SearchBar from '../search/bar.vue';
import Pagination from '../pagination/pagination.vue'; import Pagination from '../pagination/pagination.vue';
import TagFilter from '../filters/tag-filter.vue';
async function fetchMovies() { async function fetchMovies() {
if (this.$route.query.query) { if (this.$route.query.query) {
@@ -73,6 +80,7 @@ export default {
MovieTile, MovieTile,
SearchBar, SearchBar,
Pagination, Pagination,
TagFilter,
}, },
data() { data() {
return { return {

View File

@@ -21,14 +21,14 @@
<Details :release="release" /> <Details :release="release" />
<button <button
v-if="release.photos?.length > 0 || release.scenesPhotos?.length > 0" v-if="release.photos.length > 0"
class="album-toggle" class="album-toggle"
@click="$router.push({ hash: '#album' })" @click="$router.push({ hash: '#album' })"
><Icon icon="grid3" />View album</button> ><Icon icon="grid3" />View album</button>
<Album <Album
v-if="showAlbum" v-if="showAlbum"
:items="[release.poster, ...(release.photos || []), ...(release.scenesPhotos || [])]" :items="[release.poster, ...release.photos]"
:title="release.title" :title="release.title"
:path="config.media.mediaPath" :path="config.media.mediaPath"
@close="$router.replace({ hash: undefined })" @close="$router.replace({ hash: undefined })"
@@ -79,23 +79,22 @@
/> />
<div <div
v-if="release.movies?.length > 0 || release.series?.length > 0" v-if="release.movies && release.movies.length > 0"
class="row" class="row"
> >
<span class="row-label">Part of</span> <span class="row-label">Part of</span>
<div class="movies"> <div class="movies">
<router-link <router-link
v-for="movie in [...release.movies, ...release.series]" v-for="movie in release.movies"
:key="`movie-${movie.id}`" :key="`movie-${movie.id}`"
:to="{ name: movie.type || 'movie', params: { releaseId: movie.id, releaseSlug: movie.slug } }" :to="{ name: 'movie', params: { releaseId: movie.id, releaseSlug: movie.slug } }"
class="movie" class="movie"
> >
<span class="movie-title">{{ movie.title }}</span> <span class="movie-title">{{ movie.title }}</span>
<img <img
v-if="movie.covers.length > 0 || movie.poster" v-if="movie.covers.length > 0"
:src="getPath(movie.covers[0] || movie.poster, 'thumbnail')" :src="getPath(movie.covers[0], 'thumbnail')"
class="movie-cover" class="movie-cover"
> >
</router-link> </router-link>
@@ -203,19 +202,6 @@
</div> </div>
</div> </div>
<div
v-if="release.qualities"
class="row"
>
<span class="row-label">Available qualities</span>
<span
v-for="quality in release.qualities"
:key="quality"
class="quality"
>{{ quality }}</span>
</div>
<div <div
v-if="release.comment" v-if="release.comment"
class="row" class="row"
@@ -257,10 +243,6 @@ async function fetchRelease(scroll = true) {
this.release = await this.$store.dispatch('fetchMovieById', this.$route.params.releaseId); this.release = await this.$store.dispatch('fetchMovieById', this.$route.params.releaseId);
} }
if (this.$route.name === 'serie') {
this.release = await this.$store.dispatch('fetchSerieById', this.$route.params.releaseId);
}
if (scroll && this.$refs.content) { if (scroll && this.$refs.content) {
this.$refs.content.scrollTop = 0; this.$refs.content.scrollTop = 0;
} }
@@ -300,7 +282,7 @@ function pageTitle() {
} }
function showAlbum() { function showAlbum() {
return (this.release.photos?.length > 0 || this.release.scenesPhotos?.length > 0) && this.$route.hash === '#album'; return this.release.photos?.length > 0 && this.$route.hash === '#album';
} }
export default { export default {
@@ -483,16 +465,6 @@ export default {
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.quality {
&::after {
content: 'p, ';
}
&:last-child::after {
content: 'p',
}
}
.releases { .releases {
margin: 0 0 .5rem 0; margin: 0 0 .5rem 0;
} }

View File

@@ -87,6 +87,7 @@ const tagSlugsByCategory = {
'titty-fucking', 'titty-fucking',
'fisting', 'fisting',
'anal-fisting', 'anal-fisting',
'fisting-dp',
], ],
group: [ group: [
'mfm', 'mfm',
@@ -107,31 +108,6 @@ const tagSlugsByCategory = {
'bukkake', 'bukkake',
'fake-cum', 'fake-cum',
], ],
roleplay: [
'family',
'parody',
'schoolgirl',
'nurse',
'maid',
'nun',
],
extreme: [
'dp',
'airtight',
'dap',
'dvp',
'triple-penetration',
'tap',
'tvp',
],
fetish: [
'bdsm',
'femdom',
'bondage',
'free-use',
'latex',
'blindfold',
],
toys: [ toys: [
'toys', 'toys',
'toy-anal', 'toy-anal',
@@ -142,6 +118,32 @@ const tagSlugsByCategory = {
'double-dildo-anal', 'double-dildo-anal',
'double-dildo-dp', 'double-dildo-dp',
], ],
roleplay: [
'family',
'parody',
'schoolgirl',
'nurse',
'maid',
'nun',
],
fetish: [
'bdsm',
'femdom',
'bondage',
'free-use',
'latex',
'blindfold',
],
extreme: [
'dp',
'airtight',
'dap',
'dvp',
'da-tp',
'dv-tp',
'tap',
'tvp',
],
misc: [ misc: [
'gaping', 'gaping',
'squirting', 'squirting',

View File

@@ -12,7 +12,6 @@ export default {
selectableTags: [ selectableTags: [
'airtight', 'airtight',
'anal', 'anal',
'bdsm',
'blowbang', 'blowbang',
'blowjob', 'blowjob',
'creampie', 'creampie',

View File

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

View File

@@ -41,11 +41,6 @@ function initEntitiesActions(store, router) {
slug slug
} }
} }
sceneTags {
id
name
slug
}
children: childEntitiesConnection( children: childEntitiesConnection(
orderBy: [PRIORITY_DESC, NAME_ASC], orderBy: [PRIORITY_DESC, NAME_ASC],
filter: { filter: {
@@ -101,7 +96,7 @@ function initEntitiesActions(store, router) {
} }
} }
] ]
effectiveDate: { date: {
lessThan: $before, lessThan: $before,
greaterThan: $after greaterThan: $after
} }

View File

@@ -367,7 +367,6 @@ const releaseFields = `
date date
datePrecision datePrecision
slug slug
qualities
shootId shootId
productionDate productionDate
comment comment
@@ -443,29 +442,6 @@ const releasesFragment = `
} }
`; `;
const mediaFields = `
id
index
path
thumbnail
lazy
isS3
comment
sfw: sfwMedia {
id
thumbnail
lazy
path
comment
}
`;
const mediaFragment = `
media {
${mediaFields}
}
`;
const releaseFragment = ` const releaseFragment = `
release(id: $releaseId) { release(id: $releaseId) {
id id
@@ -476,7 +452,6 @@ const releaseFragment = `
duration duration
createdAt createdAt
shootId shootId
qualities
productionDate productionDate
createdBatchId createdBatchId
productionLocation productionLocation
@@ -561,19 +536,6 @@ const releaseFragment = `
} }
} }
} }
series: seriesScenesBySceneId {
serie {
id
title
slug
covers: seriesCoversBySerieId(orderBy: MEDIA_BY_MEDIA_ID__INDEX_ASC) {
${mediaFragment}
}
poster: seriesPosterBySerieId {
${mediaFragment}
}
}
}
isFavorited isFavorited
isStashed(includeFavorites: false) isStashed(includeFavorites: false)
stashes: stashesScenesBySceneId( stashes: stashesScenesBySceneId(
@@ -662,8 +624,6 @@ export {
actorFields, actorFields,
actorStashesFields, actorStashesFields,
campaignsFragment, campaignsFragment,
mediaFields,
mediaFragment,
movieFields, movieFields,
releaseActorsFragment, releaseActorsFragment,
releaseFields, releaseFields,

View File

@@ -7,22 +7,22 @@ const dateRanges = {
latest: () => ({ latest: () => ({
after: '1900-01-01', after: '1900-01-01',
before: dayjs.utc().toDate(), before: dayjs.utc().toDate(),
orderBy: ['EFFECTIVE_DATE_DESC'], orderBy: ['DATE_DESC'],
}), }),
upcoming: () => ({ upcoming: () => ({
after: dayjs.utc().toDate(), after: dayjs.utc().toDate(),
before: '2100-01-01', before: '2100-01-01',
orderBy: ['EFFECTIVE_DATE_DESC'], orderBy: ['DATE_DESC'],
}), }),
new: () => ({ new: () => ({
after: '1900-01-01 00:00:00', after: '1900-01-01 00:00:00',
before: '2100-01-01', before: '2100-01-01',
orderBy: ['CREATED_AT_DESC', 'EFFECTIVE_DATE_ASC'], orderBy: ['CREATED_AT_DESC', 'DATE_ASC'],
}), }),
all: () => ({ all: () => ({
after: '1900-01-01', after: '1900-01-01',
before: '2100-01-01', before: '2100-01-01',
orderBy: ['EFFECTIVE_DATE_DESC'], orderBy: ['DATE_DESC'],
}), }),
}; };

View File

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

View File

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

View File

@@ -89,10 +89,6 @@ module.exports = {
'uksinners', 'uksinners',
// mindgeek // mindgeek
'pornhub', 'pornhub',
// insex
'paintoy',
'aganmedon',
'sensualpain',
], ],
networks: [ networks: [
// dummy network for testing // dummy network for testing

View File

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

View File

@@ -0,0 +1,8 @@
exports.up = async (knex) => knex.raw(`
CREATE VIEW movies_tagged AS
SELECT * FROM movies;
`);
exports.down = async (knex) => knex.raw(`
DROP VIEW IF EXISTS movies_tagged;
`);

View File

@@ -1,23 +0,0 @@
exports.up = async (knex) => knex.raw(`
CREATE FUNCTION entities_scene_tags(entity entities, selectable_tags text[]) RETURNS SETOF tags AS $$
SELECT tags.*
FROM releases
LEFT JOIN
releases_tags ON releases_tags.release_id = releases.id
LEFT JOIN
tags ON tags.id = releases_tags.tag_id
WHERE
releases.entity_id = entity.id
AND
CASE WHEN array_length(selectable_tags, 1) IS NOT NULL
THEN tags.slug = ANY(selectable_tags)
ELSE true
END
GROUP BY tags.id
ORDER BY tags.name;
$$ LANGUAGE SQL STABLE;
`);
exports.down = async (knex) => knex.raw(`
DROP FUNCTION IF EXISTS entities_scene_tags;
`);

View File

@@ -1,215 +0,0 @@
const config = require('config');
exports.up = async (knex) => Promise.resolve()
.then(() => knex.schema.createTable('series', (table) => {
table.increments('id', 16);
table.integer('entity_id', 12)
.references('id')
.inTable('entities')
.notNullable();
table.integer('studio_id', 12)
.references('id')
.inTable('entities');
table.text('entry_id');
table.unique(['entity_id', 'entry_id']);
table.text('url', 1000);
table.text('title');
table.text('slug');
table.timestamp('date');
table.index('date');
table.enum('date_precision', ['year', 'month', 'week', 'day', 'hour', 'minute', 'second'])
.defaultTo('day');
table.text('description');
table.boolean('deep');
table.text('deep_url', 1000);
table.text('comment');
table.integer('created_batch_id', 12)
.references('id')
.inTable('batches')
.onDelete('cascade');
table.integer('updated_batch_id', 12)
.references('id')
.inTable('batches')
.onDelete('cascade');
table.datetime('created_at')
.defaultTo(knex.fn.now());
}))
.then(() => knex.schema.createTable('series_scenes', (table) => {
table.integer('serie_id', 16)
.notNullable()
.references('id')
.inTable('series')
.onDelete('cascade');
table.integer('scene_id', 16)
.notNullable()
.references('id')
.inTable('releases')
.onDelete('cascade');
table.unique(['serie_id', 'scene_id']);
table.datetime('created_at')
.defaultTo(knex.fn.now());
}))
.then(() => knex.schema.createTable('series_trailers', (table) => {
table.integer('serie_id', 16)
.unique()
.notNullable()
.references('id')
.inTable('series')
.onDelete('cascade');
table.text('media_id', 21)
.notNullable()
.references('id')
.inTable('media');
}))
.then(() => knex.schema.createTable('series_posters', (table) => {
table.integer('serie_id', 16)
.notNullable()
.references('id')
.inTable('series')
.onDelete('cascade');
table.text('media_id', 21)
.notNullable()
.references('id')
.inTable('media')
.onDelete('cascade');
table.unique('serie_id');
}))
.then(() => knex.schema.createTable('series_covers', (table) => {
table.integer('serie_id', 16)
.notNullable()
.references('id')
.inTable('series')
.onDelete('cascade');
table.text('media_id', 21)
.notNullable()
.references('id')
.inTable('media');
table.unique(['serie_id', 'media_id']);
}))
.then(() => knex.schema.createTable('series_search', (table) => {
table.integer('serie_id', 16)
.references('id')
.inTable('series')
.onDelete('cascade');
}))
.then(() => knex.schema.createTable('stashes_series', (table) => {
table.integer('stash_id')
.notNullable()
.references('id')
.inTable('stashes')
.onDelete('cascade');
table.integer('serie_id')
.notNullable()
.references('id')
.inTable('series')
.onDelete('cascade');
table.unique(['stash_id', 'serie_id']);
table.string('comment');
table.datetime('created_at')
.notNullable()
.defaultTo(knex.fn.now());
}))
.then(() => knex.raw(`
ALTER TABLE series_search ADD COLUMN document tsvector;
CREATE UNIQUE INDEX series_search_unique ON series_search (serie_id);
CREATE INDEX series_search_index ON series_search USING GIN (document);
CREATE FUNCTION series_actors(serie series) RETURNS SETOF actors AS $$
SELECT actors.*
FROM series_scenes
LEFT JOIN
releases ON releases.id = series_scenes.scene_id
LEFT JOIN
releases_actors ON releases_actors.release_id = releases.id
LEFT JOIN
actors ON actors.id = releases_actors.actor_id
WHERE series_scenes.serie_id = serie.id
AND actors.id IS NOT NULL
GROUP BY actors.id
ORDER BY actors.name, actors.gender
$$ LANGUAGE SQL STABLE;
CREATE FUNCTION series_tags(serie series) RETURNS SETOF tags AS $$
SELECT tags.*
FROM series_scenes
LEFT JOIN
releases ON releases.id = series_scenes.scene_id
LEFT JOIN
releases_tags ON releases_tags.release_id = releases.id
LEFT JOIN
tags ON tags.id = releases_tags.tag_id
WHERE series_scenes.serie_id = serie.id
AND tags.id IS NOT NULL
GROUP BY tags.id
ORDER BY tags.priority DESC
$$ LANGUAGE SQL STABLE;
CREATE FUNCTION series_photos(serie series) RETURNS SETOF media AS $$
SELECT media.*
FROM series_scenes
LEFT JOIN
releases ON releases.id = series_scenes.scene_id
INNER JOIN
releases_photos ON releases_photos.release_id = releases.id
LEFT JOIN
media ON media.id = releases_photos.media_id
WHERE series_scenes.serie_id = serie.id
GROUP BY media.id
ORDER BY media.index ASC
$$ LANGUAGE SQL STABLE;
GRANT ALL ON ALL TABLES IN SCHEMA public TO :visitor;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO :visitor;
ALTER TABLE stashes_series ENABLE ROW LEVEL SECURITY;
CREATE POLICY stashes_policy ON stashes_series
USING (EXISTS (
SELECT *
FROM stashes
WHERE stashes.id = stashes_series.stash_id
AND (stashes.user_id = current_user_id() OR stashes.public)
));
`, {
visitor: knex.raw(config.database.query.user),
}));
exports.down = async (knex) => Promise.resolve()
.then(() => knex.raw(`
DROP FUNCTION IF EXISTS series_actors;
DROP FUNCTION IF EXISTS series_tags;
DROP FUNCTION IF EXISTS series_photos;
DROP TABLE IF EXISTS stashes_series CASCADE;
DROP TABLE IF EXISTS series_scenes CASCADE;
DROP TABLE IF EXISTS series_trailers CASCADE;
DROP TABLE IF EXISTS series_posters CASCADE;
DROP TABLE IF EXISTS series_covers CASCADE;
DROP TABLE IF EXISTS series_search CASCADE;
DROP TABLE IF EXISTS series CASCADE;
`));

View File

@@ -1,49 +0,0 @@
const config = require('config');
exports.up = async (knex) => Promise.resolve()
.then(() => knex.raw(`
ALTER FUNCTION movies_photos(movie movies) RENAME TO movies_scenes_photos;
ALTER FUNCTION series_photos(serie series) RENAME TO series_scenes_photos;
`))
.then(() => knex.schema.createTable('movies_photos', (table) => {
table.integer('movie_id', 16)
.notNullable()
.references('id')
.inTable('movies')
.onDelete('cascade');
table.text('media_id', 21)
.notNullable()
.references('id')
.inTable('media');
table.unique(['movie_id', 'media_id']);
}))
.then(() => knex.schema.createTable('series_photos', (table) => {
table.integer('serie_id', 16)
.notNullable()
.references('id')
.inTable('series')
.onDelete('cascade');
table.text('media_id', 21)
.notNullable()
.references('id')
.inTable('media');
table.unique(['serie_id', 'media_id']);
}))
.then(() => knex.raw(`
GRANT ALL ON ALL TABLES IN SCHEMA public TO :visitor;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO :visitor;
`, {
visitor: knex.raw(config.database.query.user),
}));
exports.down = async (knex) => knex.raw(`
DROP TABLE IF EXISTS movies_photos CASCADE;
DROP TABLE IF EXISTS series_photos CASCADE;
ALTER FUNCTION movies_scenes_photos(movie movies) RENAME TO movies_photos;
ALTER FUNCTION series_scenes_photos(serie series) RENAME TO series_photos;
`);

View File

@@ -1,25 +0,0 @@
exports.up = async (knex) => knex.raw(`
CREATE MATERIALIZED VIEW entities_stats
AS
WITH RECURSIVE relations AS (
SELECT entities.id, entities.parent_id, count(releases.id) AS releases_count, count(releases.id) AS total_count
FROM entities
LEFT JOIN releases ON releases.entity_id = entities.id
GROUP BY entities.id
UNION ALL
SELECT entities.id AS entity_id, count(releases.id) AS releases_count, count(releases.id) + relations.total_count AS total_count
FROM entities
INNER JOIN relations ON relations.id = entities.parent_id
LEFT JOIN releases ON releases.entity_id = entities.id
GROUP BY entities.id
)
SELECT relations.id AS entity_id, relations.releases_count
FROM relations;
`);
exports.down = async (knex) => knex.raw(`
DROP MATERIALIZED VIEW entities_stats;
`);

View File

@@ -1,7 +0,0 @@
exports.up = async (knex) => knex.schema.alterTable('releases', (table) => {
table.specificType('qualities', 'text[]');
});
exports.down = async (knex) => knex.schema.alterTable('releases', (table) => {
table.dropColumn('qualities');
});

View File

@@ -1,12 +0,0 @@
exports.up = async (knex) => knex.raw(`
CREATE MATERIALIZED VIEW entities_stats
AS
SELECT entities.id AS entity_id, count(releases.id) AS releases_count
FROM entities
LEFT JOIN releases ON releases.entity_id = entities.id
GROUP BY entities.id;
`);
exports.down = async (knex) => knex.raw(`
DROP MATERIALIZED VIEW entities_stats;
`);

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "traxxx", "name": "traxxx",
"version": "1.213.9", "version": "1.209.4",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "traxxx", "name": "traxxx",
"version": "1.213.9", "version": "1.209.4",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@casl/ability": "^5.2.2", "@casl/ability": "^5.2.2",

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -225,7 +225,6 @@ const networks = [
description: 'DigitalPlayground.com is the leader in high quality adult blockbuster movies and award winning sex parodies that feature the most exclusive pornstars online! Adult Film Database of adult movies.', description: 'DigitalPlayground.com is the leader in high quality adult blockbuster movies and award winning sex parodies that feature the most exclusive pornstars online! Adult Film Database of adult movies.',
parameters: { parameters: {
actorPath: 'modelprofile', actorPath: 'modelprofile',
forceDeep: true, // Digital Playground has movie and series information not available in the latest updates API
}, },
parent: 'mindgeek', parent: 'mindgeek',
}, },
@@ -412,9 +411,6 @@ const networks = [
url: 'https://www.milehighmedia.com', url: 'https://www.milehighmedia.com',
description: 'MileHighMedia.com is the only niche porn network you need! Watch lesbian sex, hardcore fucking and family porn stories with the hottest teens & MILFs!', description: 'MileHighMedia.com is the only niche porn network you need! Watch lesbian sex, hardcore fucking and family porn stories with the hottest teens & MILFs!',
parent: 'mindgeek', parent: 'mindgeek',
parameters: {
forceDeep: true, // Mile High Media has movie and series information not available in the latest updates API
},
}, },
{ {
slug: 'mofos', slug: 'mofos',

View File

@@ -810,13 +810,6 @@ const sites = [
parameters: { siteId: 3261 }, parameters: { siteId: 3261 },
parent: 'bang', parent: 'bang',
}, },
{
name: 'Bang! Podcast',
slug: 'bangpodcast',
url: 'https://www.bang.com/videos?in=bang!%20podcast',
parameters: { siteId: 6305 },
parent: 'bang',
},
// BANGBROS // BANGBROS
{ {
name: 'Ass Parade', name: 'Ass Parade',
@@ -2653,7 +2646,7 @@ const sites = [
{ {
slug: 'digitalplayground', slug: 'digitalplayground',
name: 'Digital Playground', name: 'Digital Playground',
url: 'https://www.digitalplayground.com', url: 'https://www.digitalplayground.com/scenes',
description: '', description: '',
parameters: { extract: true }, parameters: { extract: true },
parent: 'digitalplayground', parent: 'digitalplayground',
@@ -2687,13 +2680,6 @@ const sites = [
description: '', description: '',
parent: 'digitalplayground', parent: 'digitalplayground',
}, },
{
slug: 'dpstarsexchallenges',
name: 'DP Star Sex Challenges',
url: 'https://www.digitalplayground.com/scenes?site=210',
parent: 'digitalplayground',
hasLogo: false,
},
{ {
slug: 'blockbuster', slug: 'blockbuster',
name: 'Blockbuster', name: 'Blockbuster',
@@ -2714,142 +2700,163 @@ const sites = [
{ {
slug: 'blacksonblondes', slug: 'blacksonblondes',
name: 'Blacks On Blondes', name: 'Blacks On Blondes',
url: 'https://www.blacksonblondes.com', url: 'https://www.blacksonblondes.com/tour',
description: 'Blacks On Blondes is the Worlds Largest and Best Interracial Sex and Interracial Porn website. Black Men and White Women. BlacksOnBlondes has 23 years worth of Hardcore Interracial Content. Featuring the entire Legendary Dogfart Movie Archive', description: 'Blacks On Blondes is the Worlds Largest and Best Interracial Sex and Interracial Porn website. Black Men and White Women. BlacksOnBlondes has 23 years worth of Hardcore Interracial Content. Featuring the entire Legendary Dogfart Movie Archive',
parent: 'dogfartnetwork', parent: 'dogfartnetwork',
}, },
{ {
slug: 'cuckoldsessions', slug: 'cuckoldsessions',
name: 'Cuckold Sessions', name: 'Cuckold Sessions',
url: 'https://www.cuckoldsessions.com', url: 'https://www.cuckoldsessions.com/tour',
description: 'Dogfart, the #1 Interracial Network in the World Presents CuckoldSessions.com - Hardcore Cuckold Fetish Videos', description: 'Dogfart, the #1 Interracial Network in the World Presents CuckoldSessions.com/tour - Hardcore Cuckold Fetish Videos',
parent: 'dogfartnetwork', parent: 'dogfartnetwork',
}, },
{ {
slug: 'gloryhole', slug: 'gloryhole',
name: 'Glory Hole', name: 'Glory Hole',
url: 'https://www.gloryhole.com', url: 'https://www.gloryhole.com/tour',
description: '',
parent: 'dogfartnetwork', parent: 'dogfartnetwork',
}, },
{ {
slug: 'blacksoncougars', slug: 'blacksoncougars',
name: 'Blacks On Cougars', name: 'Blacks On Cougars',
url: 'https://www.blacksoncougars.com', url: 'https://www.blacksoncougars.com/tour',
description: '',
parent: 'dogfartnetwork', parent: 'dogfartnetwork',
}, },
{ {
slug: 'wefuckblackgirls', slug: 'wefuckblackgirls',
name: 'We Fuck Black Girls', name: 'We Fuck Black Girls',
alias: ['wfbg'], alias: ['wfbg'],
url: 'https://www.wefuckblackgirls.com', url: 'https://www.wefuckblackgirls.com/tour',
description: '',
parent: 'dogfartnetwork', parent: 'dogfartnetwork',
}, },
{ {
slug: 'watchingmymomgoblack', slug: 'watchingmymomgoblack',
name: 'Watching My Mom Go Black', name: 'Watching My Mom Go Black',
url: 'https://www.watchingmymomgoblack.com', url: 'https://www.watchingmymomgoblack.com/tour',
description: '',
parent: 'dogfartnetwork', parent: 'dogfartnetwork',
}, },
{ {
slug: 'interracialblowbang', slug: 'interracialblowbang',
name: 'Interracial Blowbang', name: 'Interracial Blowbang',
url: 'https://www.interracialblowbang.com', url: 'https://www.interracialblowbang.com/tour',
description: '',
parent: 'dogfartnetwork', parent: 'dogfartnetwork',
}, },
{ {
slug: 'cumbang', slug: 'cumbang',
name: 'Cumbang', name: 'Cumbang',
url: 'https://www.cumbang.com', url: 'https://www.cumbang.com/tour',
description: '',
parent: 'dogfartnetwork', parent: 'dogfartnetwork',
}, },
{ {
slug: 'interracialpickups', slug: 'interracialpickups',
name: 'Interracial Pickups', name: 'Interracial Pickups',
url: 'https://www.interracialpickups.com', url: 'https://www.interracialpickups.com/tour',
description: '',
parent: 'dogfartnetwork', parent: 'dogfartnetwork',
}, },
{ {
slug: 'watchingmydaughtergoblack', slug: 'watchingmydaughtergoblack',
name: 'Watching My Daughter Go Black', name: 'Watching My Daughter Go Black',
url: 'https://www.watchingmydaughtergoblack.com', url: 'https://www.watchingmydaughtergoblack.com/tour',
description: '',
parent: 'dogfartnetwork', parent: 'dogfartnetwork',
}, },
{ {
slug: 'zebragirls', slug: 'zebragirls',
name: 'Zebra Girls', name: 'Zebra Girls',
url: 'https://www.zebragirls.com', url: 'https://www.zebragirls.com/tour',
description: '',
parent: 'dogfartnetwork', parent: 'dogfartnetwork',
}, },
{ {
slug: 'gloryholeinitiations', slug: 'gloryholeinitiations',
name: 'Gloryhole Initiations', name: 'Gloryhole Initiations',
url: 'https://www.gloryhole-initiations.com', url: 'https://www.gloryhole-initiations.com/tour',
description: '',
parent: 'dogfartnetwork', parent: 'dogfartnetwork',
}, },
{ {
slug: 'dogfartbehindthescenes', slug: 'dogfartbehindthescenes',
name: 'Dogfart Behind The Scenes', name: 'Dogfart Behind The Scenes',
url: 'https://www.dogfartbehindthescenes.com', url: 'https://www.dogfartbehindthescenes.com/tour',
description: '',
parent: 'dogfartnetwork', parent: 'dogfartnetwork',
}, },
{ {
slug: 'blackmeatwhitefeet', slug: 'blackmeatwhitefeet',
name: 'Black Meat White Feet', name: 'Black Meat White Feet',
url: 'https://www.blackmeatwhitefeet.com', url: 'https://www.blackmeatwhitefeet.com/tour',
description: '',
parent: 'dogfartnetwork', parent: 'dogfartnetwork',
}, },
{ {
slug: 'springthomas', slug: 'springthomas',
name: 'Spring Thomas', name: 'Spring Thomas',
url: 'https://www.springthomas.com', url: 'https://www.springthomas.com/tour',
description: '',
parent: 'dogfartnetwork', parent: 'dogfartnetwork',
}, },
{ {
slug: 'katiethomas', slug: 'katiethomas',
name: 'Katie Thomas', name: 'Katie Thomas',
url: 'https://www.katiethomas.com', url: 'https://www.katiethomas.com/tour',
description: '',
parent: 'dogfartnetwork', parent: 'dogfartnetwork',
}, },
{ {
slug: 'ruthblackwell', slug: 'ruthblackwell',
name: 'Ruth Blackwell', name: 'Ruth Blackwell',
url: 'https://www.ruthblackwell.com', url: 'https://www.ruthblackwell.com/tour',
description: '',
parent: 'dogfartnetwork', parent: 'dogfartnetwork',
}, },
{ {
slug: 'candymonroe', slug: 'candymonroe',
name: 'Candy Monroe', name: 'Candy Monroe',
url: 'https://www.candymonroe.com', url: 'https://www.candymonroe.com/tour',
description: '',
parent: 'dogfartnetwork', parent: 'dogfartnetwork',
}, },
{ {
slug: 'wifewriting', slug: 'wifewriting',
name: 'Wife Writing', name: 'Wife Writing',
url: 'https://www.wifewriting.com', url: 'https://www.wifewriting.com/tour',
description: '',
parent: 'dogfartnetwork', parent: 'dogfartnetwork',
}, },
{ {
slug: 'barbcummings', slug: 'barbcummings',
name: 'Barb Cummings', name: 'Barb Cummings',
url: 'https://www.barbcummings.com', url: 'https://www.barbcummings.com/tour',
description: '',
parent: 'dogfartnetwork', parent: 'dogfartnetwork',
}, },
{ {
slug: 'theminion', slug: 'theminion',
name: 'The Minion', name: 'The Minion',
url: 'https://www.theminion.com', url: 'https://www.theminion.com/tour',
description: '',
parent: 'dogfartnetwork', parent: 'dogfartnetwork',
}, },
{ {
slug: 'blacksonboys', slug: 'blacksonboys',
name: 'Blacks On Boys', name: 'Blacks On Boys',
url: 'https://www.blacksonboys.com', url: 'https://www.blacksonboys.com/tour',
description: '',
parent: 'dogfartnetwork', parent: 'dogfartnetwork',
}, },
{ {
slug: 'gloryholesandhandjobs', slug: 'gloryholesandhandjobs',
name: 'Gloryholes And Handjobs', name: 'Gloryholes And Handjobs',
url: 'https://www.gloryholesandhandjobs.com', url: 'https://www.gloryholesandhandjobs.com/tour',
description: '',
parent: 'dogfartnetwork', parent: 'dogfartnetwork',
}, },
// DORCEL // DORCEL
@@ -4198,6 +4205,7 @@ const sites = [
tags: ['bdsm'], tags: ['bdsm'],
parent: 'insex', parent: 'insex',
parameters: { parameters: {
scraper: 'alt',
latest: 'https://www.sexuallybroken.com/sb', latest: 'https://www.sexuallybroken.com/sb',
}, },
}, },
@@ -4208,20 +4216,13 @@ const sites = [
url: 'https://www.infernalrestraints.com', url: 'https://www.infernalrestraints.com',
tags: ['bdsm'], tags: ['bdsm'],
parent: 'insex', parent: 'insex',
parameters: {
latest: 'https://www.infernalrestraints.com/ir',
},
}, },
{ {
slug: 'hardtied', slug: 'hardtied',
name: 'Hardtied', name: 'Hardtied',
alias: ['ht'],
url: 'https://www.hardtied.com', url: 'https://www.hardtied.com',
tags: ['bdsm'], tags: ['bdsm'],
parent: 'insex', parent: 'insex',
parameters: {
latest: 'https://www.hardtied.com/ht',
},
}, },
{ {
slug: 'realtimebondage', slug: 'realtimebondage',
@@ -4230,9 +4231,6 @@ const sites = [
url: 'https://www.realtimebondage.com', url: 'https://www.realtimebondage.com',
tags: ['bdsm', 'live'], tags: ['bdsm', 'live'],
parent: 'insex', parent: 'insex',
parameters: {
latest: 'https://www.realtimebondage.com/rtb',
},
}, },
{ {
slug: 'topgrl', slug: 'topgrl',
@@ -4242,6 +4240,7 @@ const sites = [
tags: ['bdsm', 'femdom'], tags: ['bdsm', 'femdom'],
parent: 'insex', parent: 'insex',
parameters: { parameters: {
scraper: 'alt',
latest: 'https://www.topgrl.com/tg', latest: 'https://www.topgrl.com/tg',
}, },
}, },
@@ -5265,7 +5264,7 @@ const sites = [
{ {
slug: 'bigdicksatschool', slug: 'bigdicksatschool',
name: 'Big Dicks At School', name: 'Big Dicks At School',
url: 'https://www.men.com/scenes?site=252', url: 'https://www.bigdicksatschool.com',
description: '', description: '',
parameters: { siteId: 252 }, parameters: { siteId: 252 },
tags: ['gay'], tags: ['gay'],
@@ -5274,7 +5273,7 @@ const sites = [
{ {
slug: 'drillmyhole', slug: 'drillmyhole',
name: 'Drill My Hole', name: 'Drill My Hole',
url: 'https://www.men.com/scenes?site=253', url: 'https://www.drillmyhole.com',
description: '', description: '',
parameters: { siteId: 253 }, parameters: { siteId: 253 },
tags: ['gay'], tags: ['gay'],
@@ -5283,7 +5282,7 @@ const sites = [
{ {
slug: 'str8togay', slug: 'str8togay',
name: 'Str8 to Gay', name: 'Str8 to Gay',
url: 'https://www.men.com/scenes?site=254', url: 'https://www.str8togay.com',
tags: ['gay'], tags: ['gay'],
parameters: { siteId: 254 }, parameters: { siteId: 254 },
parent: 'men', parent: 'men',
@@ -5291,7 +5290,7 @@ const sites = [
{ {
slug: 'thegayoffice', slug: 'thegayoffice',
name: 'The Gay Office', name: 'The Gay Office',
url: 'https://www.men.com/scenes?site=255', url: 'https://www.thegayoffice.com',
tags: ['gay'], tags: ['gay'],
parameters: { siteId: 255 }, parameters: { siteId: 255 },
parent: 'men', parent: 'men',
@@ -5299,7 +5298,7 @@ const sites = [
{ {
slug: 'jizzorgy', slug: 'jizzorgy',
name: 'Jizz Orgy', name: 'Jizz Orgy',
url: 'https://www.men.com/scenes?site=256', url: 'https://www.jizzorgy.com',
tags: ['gay'], tags: ['gay'],
parameters: { siteId: 256 }, parameters: { siteId: 256 },
parent: 'men', parent: 'men',
@@ -5307,7 +5306,7 @@ const sites = [
{ {
slug: 'menofuk', slug: 'menofuk',
name: 'Men of UK', name: 'Men of UK',
url: 'https://www.men.com/scenes?site=258', url: 'https://www.menofuk.com',
tags: ['gay'], tags: ['gay'],
parameters: { siteId: 258 }, parameters: { siteId: 258 },
parent: 'men', parent: 'men',
@@ -5315,7 +5314,7 @@ const sites = [
{ {
slug: 'toptobottom', slug: 'toptobottom',
name: 'Top to Bottom', name: 'Top to Bottom',
url: 'https://www.men.com/scenes?site=259', url: 'https://www.toptobottom.com',
tags: ['gay'], tags: ['gay'],
parameters: { siteId: 259 }, parameters: { siteId: 259 },
parent: 'men', parent: 'men',
@@ -5323,7 +5322,7 @@ const sites = [
{ {
slug: 'godsofmen', slug: 'godsofmen',
name: 'Gods of Men', name: 'Gods of Men',
url: 'https://www.men.com/scenes?site=260', url: 'https://www.godsofmen.com',
tags: ['gay'], tags: ['gay'],
parameters: { siteId: 260 }, parameters: { siteId: 260 },
parent: 'men', parent: 'men',
@@ -5414,10 +5413,7 @@ const sites = [
name: 'Doghouse Digital', name: 'Doghouse Digital',
url: 'https://www.doghousedigital.com', url: 'https://www.doghousedigital.com',
alias: ['dhd'], alias: ['dhd'],
parameters: { parameters: { siteId: 321 },
siteId: 321,
native: true,
},
parent: 'milehighmedia', parent: 'milehighmedia',
}, },
{ {
@@ -5433,10 +5429,7 @@ const sites = [
name: 'Reality Junkies', name: 'Reality Junkies',
url: 'https://www.realityjunkies.com', url: 'https://www.realityjunkies.com',
alias: ['rj'], alias: ['rj'],
parameters: { parameters: { siteId: 324 },
siteId: 324,
native: true,
},
parent: 'milehighmedia', parent: 'milehighmedia',
}, },
{ {
@@ -5444,10 +5437,7 @@ const sites = [
name: 'Sweetheart Video', name: 'Sweetheart Video',
url: 'https://www.sweetheartvideo.com', url: 'https://www.sweetheartvideo.com',
alias: ['shv'], alias: ['shv'],
parameters: { parameters: { siteId: 325 },
siteId: 325,
native: true,
},
parent: 'milehighmedia', parent: 'milehighmedia',
}, },
{ {
@@ -5455,10 +5445,7 @@ const sites = [
name: 'Sweet Sinner', name: 'Sweet Sinner',
url: 'https://www.sweetsinner.com', url: 'https://www.sweetsinner.com',
alias: ['ss'], alias: ['ss'],
parameters: { parameters: { siteId: 326 },
siteId: 326,
native: true,
},
parent: 'milehighmedia', parent: 'milehighmedia',
}, },
{ {
@@ -5467,10 +5454,7 @@ const sites = [
alias: ['fs'], alias: ['fs'],
tags: ['family'], tags: ['family'],
url: 'https://www.familysinners.com', url: 'https://www.familysinners.com',
parameters: { parameters: { siteId: 317 },
siteId: 317,
native: true,
},
parent: 'milehighmedia', parent: 'milehighmedia',
}, },
{ {
@@ -5479,10 +5463,7 @@ const sites = [
url: 'https://www.iconmale.com', url: 'https://www.iconmale.com',
alias: ['im'], alias: ['im'],
tags: ['gay'], tags: ['gay'],
parameters: { parameters: { native: true, siteId: 328 },
siteId: 328,
native: true,
},
parent: 'milehighmedia', parent: 'milehighmedia',
}, },
// MOFOS // MOFOS
@@ -6896,13 +6877,6 @@ const sites = [
tourId: 9, tourId: 9,
}, },
}, },
{
slug: 'dpdiva',
name: 'DP Diva',
url: 'http://dpdiva.com',
parent: 'pervcity',
tags: ['dp', 'anal'],
},
// PIERRE WOODMAN // PIERRE WOODMAN
{ {
slug: 'woodmancastingx', slug: 'woodmancastingx',
@@ -8605,10 +8579,7 @@ const sites = [
name: 'Dane Jones', name: 'Dane Jones',
alias: ['dnj'], alias: ['dnj'],
url: 'https://www.danejones.com/', url: 'https://www.danejones.com/',
parameters: { parameters: { siteId: 290 },
siteId: 290,
native: true,
},
parent: 'sexyhub', parent: 'sexyhub',
}, },
{ {
@@ -8616,10 +8587,7 @@ const sites = [
name: 'Lesbea', name: 'Lesbea',
alias: ['lsb'], alias: ['lsb'],
url: 'https://www.lesbea.com', url: 'https://www.lesbea.com',
parameters: { parameters: { siteId: 291 },
siteId: 291,
native: true,
},
tags: ['lesbian'], tags: ['lesbian'],
parent: 'sexyhub', parent: 'sexyhub',
}, },

View File

@@ -727,6 +727,8 @@ const tagMedia = [
['da-tp', 7, 'Polly Petrova in YE069', 'analvids'], ['da-tp', 7, 'Polly Petrova in YE069', 'analvids'],
['da-tp', 5, 'Venera Maxima in GIO1287'], ['da-tp', 5, 'Venera Maxima in GIO1287'],
['da-tp', 6, 'Adriana Chechik in "Gangbang Me"', 'hardx'], ['da-tp', 6, 'Adriana Chechik in "Gangbang Me"', 'hardx'],
['da-tp', 0, 'Natasha Teen in SZ2164'],
['da-tp', 1, 'Francys Belle in SZ1702', 'analvids'],
['dap', 7, 'Adriana Chechik in "DP Masters 6"', 'julesjordan'], ['dap', 7, 'Adriana Chechik in "DP Masters 6"', 'julesjordan'],
['dap', 10, 'Kira Noir', 'hardx'], ['dap', 10, 'Kira Noir', 'hardx'],
['dap', 'emily_pink_legalporno', 'Emily Pink', 'analvids'], ['dap', 'emily_pink_legalporno', 'Emily Pink', 'analvids'],
@@ -1047,7 +1049,6 @@ const tagMedia = [
['toy-dp', 0, 'Marley Brinx, Ivy Lebelle and Lyra Law in "Marley Brinx First GGDP"', 'lesbianx'], ['toy-dp', 0, 'Marley Brinx, Ivy Lebelle and Lyra Law in "Marley Brinx First GGDP"', 'lesbianx'],
['toys', 1, 'Chloe Lamour in "Curives In All The Right Places"', 'wetandpuffy'], ['toys', 1, 'Chloe Lamour in "Curives In All The Right Places"', 'wetandpuffy'],
['toys', 'shawna_lenee_sunrisekings', 'Shawna Lenee', 'sunrisekings'], ['toys', 'shawna_lenee_sunrisekings', 'Shawna Lenee', 'sunrisekings'],
['triple-penetration', 'lucky_bee_analvids', 'Lucky Bee', 'analvids'],
['triple-penetration', 'angela_white_julesjordan', 'Angela White in "Her Biggest Gangbang Ever"', 'julesjordan'], ['triple-penetration', 'angela_white_julesjordan', 'Angela White in "Her Biggest Gangbang Ever"', 'julesjordan'],
['triple-penetration', 'ria_sunn_legalporno', 'Ria Sunn in SZ2082', 'analvids'], ['triple-penetration', 'ria_sunn_legalporno', 'Ria Sunn in SZ2082', 'analvids'],
['tvp', 'september_reign_wefuckblackgirls', 'September Reign in "Second Appearance"', 'wefuckblackgirls'], ['tvp', 'september_reign_wefuckblackgirls', 'September Reign in "Second Appearance"', 'wefuckblackgirls'],

View File

@@ -14,6 +14,6 @@
"prefer-destructuring": "off", "prefer-destructuring": "off",
"template-curly-spacing": "off", "template-curly-spacing": "off",
"object-curly-newline": "off", "object-curly-newline": "off",
"max-len": [2, {"code": 300, "tabWidth": 4, "ignoreUrls": true}] "max-len": [2, {"code": 300, "tabWidth": 4, "ignoreUrls": true}],
} }
} }

View File

@@ -20,7 +20,6 @@ const scrapers = require('./scrapers/scrapers').actors;
const argv = require('./argv'); const argv = require('./argv');
const include = require('./utils/argv-include')(argv); const include = require('./utils/argv-include')(argv);
const bulkInsert = require('./utils/bulk-insert'); const bulkInsert = require('./utils/bulk-insert');
const chunk = require('./utils/chunk');
const logger = require('./logger')(__filename); const logger = require('./logger')(__filename);
const { toBaseReleases } = require('./deep'); const { toBaseReleases } = require('./deep');
@@ -1049,42 +1048,33 @@ async function flushProfiles(actorIdsOrNames) {
logger.info(`Removed ${deleteCount} profiles`); logger.info(`Removed ${deleteCount} profiles`);
} }
async function deleteActors(allActorIdsOrNames) { async function deleteActors(actorIdsOrNames) {
const deleteCounts = await Promise.map(chunk(allActorIdsOrNames), async (actorIdsOrNames) => { const actors = await knex('actors')
const actors = await knex('actors') .whereIn('id', actorIdsOrNames.filter((idOrName) => typeof idOrName === 'number'))
.whereIn('id', actorIdsOrNames.filter((idOrName) => typeof idOrName === 'number')) .orWhere((builder) => {
.orWhere((builder) => { builder
builder .whereIn('name', actorIdsOrNames.filter((idOrName) => typeof idOrName === 'string'))
.whereIn('name', actorIdsOrNames.filter((idOrName) => typeof idOrName === 'string')) .whereNull('entity_id');
.whereNull('entity_id'); });
});
const actorIds = actors.map((actor) => actor.id); const actorIds = actors.map((actor) => actor.id);
const sceneIds = await knex('releases_actors') const sceneIds = await knex('releases_actors')
.select('releases.id') .select('releases.id')
.whereIn('actor_id', actorIds) .whereIn('actor_id', actorIds)
.leftJoin('releases', 'releases.id', 'releases_actors.release_id') .leftJoin('releases', 'releases.id', 'releases_actors.release_id')
.pluck('id'); .pluck('id');
const [deletedScenesCount, deletedActorsCount] = await Promise.all([ const [deletedScenesCount, deletedActorsCount] = await Promise.all([
deleteScenes(sceneIds), deleteScenes(sceneIds),
knex('actors') knex('actors')
.whereIn('id', actorIds) .whereIn('id', actorIds)
.delete(), .delete(),
]); ]);
return { deletedScenesCount, deletedActorsCount };
}, { concurrency: 10 });
const deletedActorsCount = deleteCounts.reduce((acc, count) => acc + count.deletedActorsCount, 0);
const deletedScenesCount = deleteCounts.reduce((acc, count) => acc + count.deletedScenesCount, 0);
await flushOrphanedMedia(); await flushOrphanedMedia();
logger.info(`Removed ${deletedActorsCount} actors with ${deletedScenesCount} scenes`); logger.info(`Removed ${deletedActorsCount} actors with ${deletedScenesCount} scenes`);
return deletedActorsCount;
} }
async function flushActors() { async function flushActors() {

View File

@@ -194,7 +194,6 @@ const { argv } = yargs
alias: 'pics', alias: 'pics',
}) })
.option('videos', { .option('videos', {
alias: 'video',
describe: 'Include any trailers or teasers', describe: 'Include any trailers or teasers',
type: 'boolean', type: 'boolean',
default: true, default: true,
@@ -239,7 +238,6 @@ const { argv } = yargs
default: false, default: false,
}) })
.option('level', { .option('level', {
alias: 'log-level',
describe: 'Log level', describe: 'Log level',
type: 'string', type: 'string',
default: process.env.NODE_ENV === 'development' ? 'silly' : 'info', default: process.env.NODE_ENV === 'development' ? 'silly' : 'info',

View File

@@ -6,7 +6,7 @@ const inquirer = require('inquirer');
const logger = require('./logger')(__filename); const logger = require('./logger')(__filename);
const argv = require('./argv'); const argv = require('./argv');
const knex = require('./knex'); const knex = require('./knex');
const { deleteScenes, deleteMovies, deleteSeries } = require('./releases'); const { deleteScenes, deleteMovies } = require('./releases');
const { flushOrphanedMedia } = require('./media'); const { flushOrphanedMedia } = require('./media');
const { resolveScraper, resolveLayoutScraper } = require('./scrapers/resolve'); const { resolveScraper, resolveLayoutScraper } = require('./scrapers/resolve');
@@ -359,39 +359,29 @@ async function flushEntities(networkSlugs = [], channelSlugs = []) {
.leftJoin('movies', 'movies.entity_id', 'selected_entities.id') .leftJoin('movies', 'movies.entity_id', 'selected_entities.id')
.pluck('movies.id'); .pluck('movies.id');
const serieIds = await entityQuery if (sceneIds.length === 0 && movieIds.length === 0) {
.clone() logger.info(`No scenes or movies found to remove for ${entitySlugs}`);
.select('series.id')
.distinct('series.id')
.whereNotNull('series.id')
.from('selected_entities')
.leftJoin('series', 'series.entity_id', 'selected_entities.id')
.pluck('series.id');
if (sceneIds.length === 0 && movieIds.length === 0 && serieIds.length === 0) {
logger.info(`No scenes, movies or series found to remove for ${entitySlugs}`);
return; return;
} }
const confirmed = await inquirer.prompt([{ const confirmed = await inquirer.prompt([{
type: 'confirm', type: 'confirm',
name: 'flushEntities', name: 'flushEntities',
message: `You are about to remove ${sceneIds.length} scenes, ${movieIds.length} movies and ${serieIds.length} series for ${entitySlugs}. Are you sure?`, message: `You are about to remove ${sceneIds.length} scenes and ${movieIds.length} movies for ${entitySlugs}. Are you sure?`,
default: false, default: false,
}]); }]);
if (!confirmed.flushEntities) { if (!confirmed.flushEntities) {
logger.warn(`Confirmation rejected, not flushing scenes, movies or series for: ${entitySlugs}`); logger.warn(`Confirmation rejected, not flushing scenes or movies for: ${entitySlugs}`);
return; return;
} }
const [deletedScenesCount, deletedMoviesCount, deletedSeriesCount] = await Promise.all([ const [deletedScenesCount, deletedMoviesCount] = await Promise.all([
deleteScenes(sceneIds), deleteScenes(sceneIds),
deleteMovies(movieIds), deleteMovies(movieIds),
deleteSeries(serieIds),
]); ]);
logger.info(`Removed ${deletedScenesCount} scenes, ${deletedMoviesCount} movies and ${deletedSeriesCount} series for ${entitySlugs}`); logger.info(`Removed ${deletedScenesCount} scenes and ${deletedMoviesCount} movies for ${entitySlugs}`);
await flushOrphanedMedia(); await flushOrphanedMedia();
} }

View File

@@ -21,7 +21,6 @@ const argv = require('./argv');
const knex = require('./knex'); const knex = require('./knex');
const http = require('./utils/http'); const http = require('./utils/http');
const bulkInsert = require('./utils/bulk-insert'); const bulkInsert = require('./utils/bulk-insert');
const chunk = require('./utils/chunk');
const { get } = require('./utils/qu'); const { get } = require('./utils/qu');
const pipeline = util.promisify(stream.pipeline); const pipeline = util.promisify(stream.pipeline);
@@ -64,10 +63,10 @@ function sampleMedias(medias, limit = argv.mediaLimit, preferLast = true) {
? chunks.slice(0, -1).concat(chunks.slice(-1).reverse()) ? chunks.slice(0, -1).concat(chunks.slice(-1).reverse())
: chunks; : chunks;
const groupedMedias = lastPreferredChunks.map((mediaChunk) => { const groupedMedias = lastPreferredChunks.map((chunk) => {
// merge chunked medias into single media with grouped fallback priorities, // merge chunked medias into single media with grouped fallback priorities,
// so the first sources of each media is preferred over all second sources, etc. // so the first sources of each media is preferred over all second sources, etc.
const sources = mediaChunk const sources = chunk
.reduce((accSources, media) => { .reduce((accSources, media) => {
media.sources.forEach((source, index) => { media.sources.forEach((source, index) => {
if (!accSources[index]) { if (!accSources[index]) {
@@ -83,8 +82,8 @@ function sampleMedias(medias, limit = argv.mediaLimit, preferLast = true) {
.flat(); .flat();
return { return {
id: mediaChunk[0].id, id: chunk[0].id,
role: mediaChunk[0].role, role: chunk[0].role,
sources, sources,
}; };
}); });
@@ -236,41 +235,22 @@ async function findSourceDuplicates(baseMedias) {
.filter(Boolean); .filter(Boolean);
const [existingSourceMedia, existingExtractMedia] = await Promise.all([ const [existingSourceMedia, existingExtractMedia] = await Promise.all([
// my try to check thousands of URLs at once, don't pass all of them to a single query knex('media').whereIn('source', sourceUrls),
chunk(sourceUrls).reduce(async (chain, sourceUrlsChunk) => { knex('media').whereIn('source_page', extractUrls),
const accUrls = await chain;
const existingUrls = await knex('media').whereIn('source', sourceUrlsChunk);
return [...accUrls, ...existingUrls];
}, []),
chunk(extractUrls).reduce(async (chain, extractUrlsChunk) => {
const accUrls = await chain;
const existingUrls = await knex('media').whereIn('source_page', extractUrlsChunk);
return [...accUrls, ...existingUrls];
}, []),
]); ]);
const existingSourceMediaByUrl = itemsByKey(existingSourceMedia, 'source'); const existingSourceMediaByUrl = itemsByKey(existingSourceMedia, 'source');
const existingExtractMediaByUrl = itemsByKey(existingExtractMedia, 'source_page'); const existingExtractMediaByUrl = itemsByKey(existingExtractMedia, 'source_page');
return { return { existingSourceMediaByUrl, existingExtractMediaByUrl };
existingSourceMediaByUrl,
existingExtractMediaByUrl,
};
} }
async function findHashDuplicates(medias) { async function findHashDuplicates(medias) {
const hashes = medias.map((media) => media.meta?.hash || media.entry?.hash).filter(Boolean); const hashes = medias.map((media) => media.meta?.hash || media.entry?.hash).filter(Boolean);
const existingHashMediaEntries = await chunk(hashes, 2).reduce(async (chain, hashesChunk) => { const existingHashMediaEntries = await knex('media').whereIn('hash', hashes);
const accHashes = await chain;
const existingHashes = await knex('media').whereIn('hash', hashesChunk);
return [...accHashes, ...existingHashes];
}, []);
const existingHashMediaEntriesByHash = itemsByKey(existingHashMediaEntries, 'hash'); const existingHashMediaEntriesByHash = itemsByKey(existingHashMediaEntries, 'hash');
const uniqueHashMedias = medias.filter((media) => !media.entry && !existingHashMediaEntriesByHash[media.meta?.hash]); const uniqueHashMedias = medias.filter((media) => !media.entry && !existingHashMediaEntriesByHash[media.meta?.hash]);
const { selfDuplicateMedias, selfUniqueMediasByHash } = uniqueHashMedias.reduce((acc, media) => { const { selfDuplicateMedias, selfUniqueMediasByHash } = uniqueHashMedias.reduce((acc, media) => {
@@ -620,11 +600,11 @@ async function fetchSource(source, baseMedia) {
const hashStream = new stream.PassThrough(); const hashStream = new stream.PassThrough();
let size = 0; let size = 0;
hashStream.on('data', (streamChunk) => { hashStream.on('data', (chunk) => {
size += streamChunk.length; size += chunk.length;
if (hasherReady) { if (hasherReady) {
hasher.write(streamChunk); hasher.write(chunk);
} }
}); });
@@ -981,12 +961,9 @@ async function flushOrphanedMedia() {
await deleteS3Objects(orphanedMedia.filter((media) => media.is_s3)); await deleteS3Objects(orphanedMedia.filter((media) => media.is_s3));
} }
try { await fsPromises.rm(path.join(config.media.path, 'temp'), { recursive: true });
await fsPromises.rm(path.join(config.media.path, 'temp'), { recursive: true });
logger.info('Cleared temporary media directory'); logger.info('Cleared temporary media directory');
} catch (error) {
logger.warn(`Failed to clear temporary media directory: ${error.message}`);
}
} }
module.exports = { module.exports = {

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