diff --git a/assets/components/movies/movies.vue b/assets/components/movies/movies.vue
index 678079a8..f9e7b9d4 100644
--- a/assets/components/movies/movies.vue
+++ b/assets/components/movies/movies.vue
@@ -43,7 +43,7 @@ export default {
.tiles {
display: grid;
- grid-template-columns: repeat(auto-fill, minmax(30rem, 1fr));
+ grid-template-columns: repeat(auto-fill, minmax(25rem, 1fr));
grid-gap: 1rem;
}
diff --git a/assets/components/movies/tile.vue b/assets/components/movies/tile.vue
index 45881d4e..d18533a3 100644
--- a/assets/components/movies/tile.vue
+++ b/assets/components/movies/tile.vue
@@ -12,16 +12,96 @@
+
+
{{ movie.title }}
+
+
+
+
-
- {{ movie.entity.name }}
@@ -41,7 +121,7 @@ export default {
display: flex;
flex-direction: column;
background: var(--background);
- box-shadow: 0 0 3px var(--darken);
+ box-shadow: 0 0 3px var(--darken-weak);
font-size: 0;
}
@@ -55,6 +135,9 @@ export default {
}
.details {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
color: var(--text-light);
background: var(--profile);
padding: .5rem 1rem;
@@ -62,17 +145,101 @@ export default {
font-weight: bold;
}
-.cover {
- width: 12rem;
+.entity-link,
+.date {
+ display: flex;
+ align-items: center;
+ color: inherit;
+ font-size: inherit;
+ text-decoration: none;
- img {
- width: 100%;
+ .favicon {
+ width: 1rem;
+ height: 1rem;
+ margin: 0 .5rem 0 0;
+ }
+
+ .icon {
+ fill: var(--highlight-weak);
+ margin: 0 .25rem 0 0;
+ }
+
+ &:hover .icon {
+ fill: var(--text-light);
}
}
+.cover {
+ height: 16rem;
+ box-shadow: 0 0 3px var(--darken-weak);
+
+ img {
+ height: 100%;
+ max-width: 12rem;
+ object-fit: cover;
+ object-position: center ;
+ }
+}
+
+.info {
+ flex-grow: 1;
+ overflow: hidden;
+}
+
.title {
+ box-sizing: border-box;
padding: 1rem;
margin: 0;
font-size: 1rem;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
+}
+
+.actors {
+ padding: 0 1rem;
+ margin: 0 0 1rem 0;
+ line-height: 1.5;
+}
+
+.actor:not(:last-child)::after {
+ content: ',';
+ margin: 0 .25rem 0 0;
+ font-size: 1rem;
+}
+
+.actor-link {
+ font-size: 1rem;
+ color: var(--link);
+ text-decoration: none;
+
+ &:hover {
+ color: var(--primary);
+ }
+}
+
+.tags {
+ padding: .2rem 1rem 0 1rem;
+ height: 1.75rem;
+ line-height: 2;
+ overflow: hidden;
+}
+
+.tag {
+ margin: 0 0 .5rem 0;
+}
+
+.tag-link {
+ background: var(--background);
+ font-size: .75rem;
+ padding: .25rem .5rem;
+ color: var(--shadow);
+ font-weight: bold;
+ text-decoration: none;
+ box-shadow: 0 0 3px var(--shadow-weak);
+
+ &:hover {
+ color: var(--primary);
+ }
}
diff --git a/assets/components/tags/tag.vue b/assets/components/tags/tag.vue
index 95b9829c..4f4434ad 100644
--- a/assets/components/tags/tag.vue
+++ b/assets/components/tags/tag.vue
@@ -123,7 +123,7 @@ export default {
}
.title {
- padding: .75rem 1rem;
+ padding: .5rem 1rem;
margin: 0;
flex-shrink: 0;
text-transform: capitalize;
diff --git a/assets/js/curate.js b/assets/js/curate.js
index d7deedef..0999a3ac 100644
--- a/assets/js/curate.js
+++ b/assets/js/curate.js
@@ -64,7 +64,7 @@ function curateRelease(release) {
...release,
actors: [],
poster: release.poster && release.poster.media,
- tags: release.tags ? release.tags.map(({ tag }) => tag) : [],
+ tags: release.tags ? release.tags.map(tag => tag.tag || tag) : [],
};
if (release.scenes) curatedRelease.scenes = release.scenes.map(({ scene }) => curateRelease(scene));
@@ -73,7 +73,7 @@ function curateRelease(release) {
if (release.covers) curatedRelease.covers = release.covers.map(({ media }) => media);
if (release.trailer) curatedRelease.trailer = release.trailer.media;
if (release.teaser) curatedRelease.teaser = release.teaser.media;
- if (release.actors) curatedRelease.actors = release.actors.map(({ actor }) => curateActor(actor, curatedRelease));
+ if (release.actors) curatedRelease.actors = release.actors.map(actor => curateActor(actor.actor || actor, curatedRelease));
if (release.movieTags && release.movieTags.length > 0) curatedRelease.tags = release.movieTags.map(({ tag }) => tag);
if (release.movieActors && release.movieActors.length > 0) curatedRelease.actors = release.movieActors.map(({ actor }) => curateActor(actor, curatedRelease));
diff --git a/assets/js/releases/actions.js b/assets/js/releases/actions.js
index 01403b1f..a24b2a7d 100644
--- a/assets/js/releases/actions.js
+++ b/assets/js/releases/actions.js
@@ -65,10 +65,27 @@ function initReleasesActions(store, _router) {
slug
date
datePrecision
+ actors {
+ id
+ name
+ slug
+ }
+ tags {
+ id
+ name
+ slug
+ }
entity {
id
name
slug
+ type
+ parent {
+ id
+ name
+ slug
+ type
+ }
}
covers: moviesCoversByReleaseId {
media {
diff --git a/migrations/20190325001339_releases.js b/migrations/20190325001339_releases.js
index de54d1a6..0d9271f0 100644
--- a/migrations/20190325001339_releases.js
+++ b/migrations/20190325001339_releases.js
@@ -958,6 +958,36 @@ exports.up = knex => Promise.resolve()
END;
$$ LANGUAGE SQL STABLE;
+ CREATE FUNCTION movies_actors(movie movies) RETURNS SETOF actors AS $$
+ SELECT actors.*
+ FROM movies_scenes
+ LEFT JOIN
+ releases ON releases.id = movies_scenes.scene_id
+ LEFT JOIN
+ releases_actors ON releases_actors.release_id = releases.id
+ LEFT JOIN
+ actors ON actors.id = releases_actors.actor_id
+ WHERE movies_scenes.movie_id = movie.id
+ AND actors.id IS NOT NULL
+ GROUP BY actors.id
+ ORDER BY actors.name, actors.gender
+ $$ LANGUAGE SQL STABLE;
+
+ CREATE FUNCTION movies_tags(movie movies) RETURNS SETOF tags AS $$
+ SELECT tags.*
+ FROM movies_scenes
+ LEFT JOIN
+ releases ON releases.id = movies_scenes.scene_id
+ LEFT JOIN
+ releases_tags ON releases_tags.release_id = releases.id
+ LEFT JOIN
+ tags ON tags.id = releases_tags.tag_id
+ WHERE movies_scenes.movie_id = movie.id
+ AND tags.id IS NOT NULL
+ GROUP BY tags.id
+ ORDER BY tags.priority DESC
+ $$ LANGUAGE SQL STABLE;
+
CREATE FUNCTION releases_is_new(release releases) RETURNS boolean AS $$
SELECT EXISTS(SELECT true WHERE (SELECT id FROM batches ORDER BY created_at DESC LIMIT 1) = release.created_batch_id);
$$ LANGUAGE sql STABLE;
@@ -1038,6 +1068,9 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
DROP FUNCTION IF EXISTS actors_channels;
DROP FUNCTION IF EXISTS actors_scenes;
+ DROP FUNCTION IF EXISTS movies_actors;
+ DROP FUNCTION IF EXISTS movies_tags;
+
DROP TEXT SEARCH CONFIGURATION IF EXISTS traxxx;
DROP TEXT SEARCH DICTIONARY IF EXISTS traxxx_dict;
`);
diff --git a/public/img/tags/dap/8.jpeg b/public/img/tags/dap/8.jpeg
new file mode 100644
index 00000000..7df0800e
Binary files /dev/null and b/public/img/tags/dap/8.jpeg differ
diff --git a/public/img/tags/dap/lazy/8.jpeg b/public/img/tags/dap/lazy/8.jpeg
new file mode 100644
index 00000000..32b1028b
Binary files /dev/null and b/public/img/tags/dap/lazy/8.jpeg differ
diff --git a/public/img/tags/dap/thumbs/0.jpeg b/public/img/tags/dap/thumbs/0.jpeg
index 287c6738..20afbf30 100644
Binary files a/public/img/tags/dap/thumbs/0.jpeg and b/public/img/tags/dap/thumbs/0.jpeg differ
diff --git a/public/img/tags/dap/thumbs/1.jpeg b/public/img/tags/dap/thumbs/1.jpeg
index 5a1629f7..31c7c7d3 100644
Binary files a/public/img/tags/dap/thumbs/1.jpeg and b/public/img/tags/dap/thumbs/1.jpeg differ
diff --git a/public/img/tags/dap/thumbs/2.jpeg b/public/img/tags/dap/thumbs/2.jpeg
index c73b2382..91718cfa 100644
Binary files a/public/img/tags/dap/thumbs/2.jpeg and b/public/img/tags/dap/thumbs/2.jpeg differ
diff --git a/public/img/tags/dap/thumbs/3.jpeg b/public/img/tags/dap/thumbs/3.jpeg
index 60494667..f99425b5 100644
Binary files a/public/img/tags/dap/thumbs/3.jpeg and b/public/img/tags/dap/thumbs/3.jpeg differ
diff --git a/public/img/tags/dap/thumbs/4.jpeg b/public/img/tags/dap/thumbs/4.jpeg
index 2b7ccae5..e352334a 100644
Binary files a/public/img/tags/dap/thumbs/4.jpeg and b/public/img/tags/dap/thumbs/4.jpeg differ
diff --git a/public/img/tags/dap/thumbs/5.jpeg b/public/img/tags/dap/thumbs/5.jpeg
index 5318dbdc..6afe7d9a 100644
Binary files a/public/img/tags/dap/thumbs/5.jpeg and b/public/img/tags/dap/thumbs/5.jpeg differ
diff --git a/public/img/tags/dap/thumbs/6.jpeg b/public/img/tags/dap/thumbs/6.jpeg
index ea6382ef..b03a62af 100644
Binary files a/public/img/tags/dap/thumbs/6.jpeg and b/public/img/tags/dap/thumbs/6.jpeg differ
diff --git a/public/img/tags/dap/thumbs/7.jpeg b/public/img/tags/dap/thumbs/7.jpeg
index 05421d91..e71fe80d 100644
Binary files a/public/img/tags/dap/thumbs/7.jpeg and b/public/img/tags/dap/thumbs/7.jpeg differ
diff --git a/public/img/tags/dap/thumbs/8.jpeg b/public/img/tags/dap/thumbs/8.jpeg
new file mode 100644
index 00000000..149da6ab
Binary files /dev/null and b/public/img/tags/dap/thumbs/8.jpeg differ
diff --git a/public/img/tags/dap/thumbs/poster.jpeg b/public/img/tags/dap/thumbs/poster.jpeg
index ce429ede..20a48b7e 100644
Binary files a/public/img/tags/dap/thumbs/poster.jpeg and b/public/img/tags/dap/thumbs/poster.jpeg differ
diff --git a/seeds/04_media.js b/seeds/04_media.js
index 947baf95..bc8c5e2e 100644
--- a/seeds/04_media.js
+++ b/seeds/04_media.js
@@ -686,6 +686,7 @@ const tagPhotos = [
['dap', 2, 'Lana Rhoades in "Lana Rhoades Unleashed" for HardX'],
['dap', 6, 'Sheena Shaw in "Ass Worship 14" for Jules Jordan'],
['dap', 5, 'Riley Reid in "The Gangbang of Riley Reid" for Jules Jordan'],
+ ['dap', 8, 'Lady Gang in SZ2478 LegalPorno'],
['dap', 'poster', 'Haley Reed in "Young Hot Ass" for Evil Angel'],
['dap', 0, 'Nicole Black doing double anal during a gangbang in GIO971 for LegalPorno'],
['dap', 1, 'Ria Sunn in SZ1801 for LegalPorno'],
diff --git a/src/app.js b/src/app.js
index 38c615ac..3413ced6 100644
--- a/src/app.js
+++ b/src/app.js
@@ -49,12 +49,14 @@ async function init() {
}
if (argv.save) {
- if (deepScenes.length + deepMovieScenes.length > 0) {
- await storeScenes(deepScenes.concat(deepMovieScenes));
+ if (deepScenes.length > 0 || deepMovieScenes.length > 0) {
+ await storeScenes(deepScenes || []);
}
if (deepMovies.length > 0) {
- await storeMovies(deepMovies);
+ const storedMovieScenes = await storeScenes(deepMovieScenes);
+
+ await storeMovies(deepMovies, storedMovieScenes);
}
}
diff --git a/src/deep.js b/src/deep.js
index 49a6cda2..1916b99c 100644
--- a/src/deep.js
+++ b/src/deep.js
@@ -165,7 +165,9 @@ async function fetchScenes(baseReleasesOrUrls) {
}
async function fetchMovies(baseReleasesOrUrls) {
- return fetchReleases(baseReleasesOrUrls, 'movie');
+ const movies = await fetchReleases(baseReleasesOrUrls, 'movie');
+
+ return movies;
}
module.exports = {
diff --git a/src/store-releases.js b/src/store-releases.js
index a9c1671b..08bc4576 100644
--- a/src/store-releases.js
+++ b/src/store-releases.js
@@ -255,7 +255,38 @@ async function storeScenes(releases) {
return releasesWithId;
}
-async function storeMovies(movies) {
+async function associateMovieScenes(movies, movieScenes) {
+ const movieScenesByEntityIdAndEntryId = movieScenes.reduce((acc, scene) => ({
+ ...acc,
+ [scene.entity.id]: {
+ ...acc[scene.entity.id],
+ [scene.entryId]: scene,
+ },
+ }), {});
+
+ const associations = movies.map((movie) => {
+ if (!movie.scenes) {
+ return null;
+ }
+
+ return movie.scenes.map((scene) => {
+ const movieScene = movieScenesByEntityIdAndEntryId[movie.entity.id]?.[scene.entryId];
+
+ if (movieScene) {
+ return {
+ movie_id: movie.id,
+ scene_id: movieScene.id,
+ };
+ }
+
+ return null;
+ });
+ }).flat().filter(Boolean);
+
+ await knex.batchInsert('movies_scenes', associations);
+}
+
+async function storeMovies(movies, movieScenes) {
const { uniqueReleases } = await filterDuplicateReleases(movies);
const [batchId] = await knex('batches').insert({ comment: null }).returning('id');
@@ -264,6 +295,7 @@ async function storeMovies(movies) {
const moviesWithId = attachReleaseIds(movies, storedMovies);
+ await associateMovieScenes(moviesWithId, movieScenes);
await associateReleaseMedia(moviesWithId, 'movies');
return storedMovies;
diff --git a/src/updates.js b/src/updates.js
index 9ec3d642..8e3dcea1 100644
--- a/src/updates.js
+++ b/src/updates.js
@@ -177,7 +177,7 @@ async function scrapeMovies(scraper, entity) {
}
async function scrapeChannelReleases(scraper, channelEntity, preData) {
- const [latestReleases, upcomingReleases] = await Promise.all([
+ const [latestReleases, upcomingReleases, movies] = await Promise.all([
argv.latest
? scrapeLatestReleases(scraper, channelEntity, preData)
: [],
@@ -189,6 +189,8 @@ async function scrapeChannelReleases(scraper, channelEntity, preData) {
: [],
]);
+ console.log(movies);
+
logger.info(`Fetching ${latestReleases.length} latest and ${upcomingReleases.length} upcoming updates for '${channelEntity.name}' (${channelEntity.parent?.name})`);
return [...latestReleases, ...upcomingReleases];