diff --git a/assets/components/releases/clips.vue b/assets/components/releases/clips.vue
new file mode 100644
index 00000000..3494a577
--- /dev/null
+++ b/assets/components/releases/clips.vue
@@ -0,0 +1,89 @@
+
+
+ -
+
+
+
+
+
+
+
+
{{ clip.description }}
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/components/releases/media.vue b/assets/components/releases/media.vue
index 249d8eb7..c491a9bb 100644
--- a/assets/components/releases/media.vue
+++ b/assets/components/releases/media.vue
@@ -105,20 +105,20 @@ function sfw() {
}
function photos() {
- const photosWithChapterPosters = (this.release.photos || []).concat(this.release.chapters ? this.release.chapters.map(chapter => chapter.poster) : []);
+ const photosWithClipPosters = (this.release.photos || []).concat(this.release.clips ? this.release.clips.map(clip => clip.poster) : []);
if (this.release.trailer || this.release.teaser) {
// poster will be on trailer video
- return photosWithChapterPosters;
+ return photosWithClipPosters;
}
if (this.release.poster) {
// no trailer, add poster to photos
- return [this.release.poster].concat(this.release.photos).concat(photosWithChapterPosters);
+ return [this.release.poster].concat(this.release.photos).concat(photosWithClipPosters);
}
// no poster available
- return photosWithChapterPosters;
+ return photosWithClipPosters;
}
export default {
diff --git a/assets/components/releases/scene-tile.vue b/assets/components/releases/scene-tile.vue
index 5fd53b39..4540f91b 100644
--- a/assets/components/releases/scene-tile.vue
+++ b/assets/components/releases/scene-tile.vue
@@ -87,10 +87,17 @@
Duration
-
- {{ Math.floor(release.duration / 3600).toString().padStart(2, '0') }}:
- {{ Math.floor((release.duration % 3600) / 60).toString().padStart(2, '0') }}:
- {{ (release.duration % 60).toString().padStart(2, '0') }}
-
+
{{ formatDuration(release.duration) }}
+
+ Clips
+
+
+
+
curateRelease(scene));
if (release.movies) curatedRelease.movies = release.movies.map(({ movie }) => curateRelease(movie));
- if (release.chapters) curatedRelease.chapters = release.chapters.map(chapter => curateRelease(chapter));
+ if (release.clips) curatedRelease.clips = release.clips.map(clip => curateRelease(clip));
if (release.photos) curatedRelease.photos = release.photos.map(({ media }) => media);
if (release.covers) curatedRelease.covers = release.covers.map(({ media }) => media);
if (release.trailer) curatedRelease.trailer = release.trailer.media;
diff --git a/assets/js/fragments.js b/assets/js/fragments.js
index 45f015e4..0f0374cb 100644
--- a/assets/js/fragments.js
+++ b/assets/js/fragments.js
@@ -255,19 +255,19 @@ const releaseFragment = `
${releaseTrailerFragment}
${releaseTeaserFragment}
${siteFragment}
- chapters {
+ clips {
id
title
description
duration
- tags: chaptersTags {
+ tags: clipsTags {
tag {
id
name
slug
}
}
- poster: chaptersPosterByChapterId {
+ poster: clipsPosterByClipId {
media {
index
path
diff --git a/assets/js/main.js b/assets/js/main.js
index faeae36c..ce3ab4a5 100644
--- a/assets/js/main.js
+++ b/assets/js/main.js
@@ -14,6 +14,20 @@ import Container from '../components/container/container.vue';
import Icon from '../components/icon/icon.vue';
import Footer from '../components/footer/footer.vue';
+function formatDuration(duration, forceHours) {
+ const hours = Math.floor(duration / 3600);
+ const minutes = Math.floor((duration % 3600) / 60);
+ const seconds = Math.floor(duration % 60);
+
+ const [formattedHours, formattedMinutes, formattedSeconds] = [hours, minutes, seconds].map(segment => segment.toString().padStart(2, '0'));
+
+ if (duration >= 3600 || forceHours) {
+ return `${formattedHours}:${formattedMinutes}:${formattedSeconds}`;
+ }
+
+ return `${formattedMinutes}:${formattedSeconds}`;
+}
+
function formatDate(date, format = 'MMMM D, YYYY', precision = 'day') {
if (precision === 'year') {
const newFormat = format.match(/Y+/);
@@ -54,6 +68,7 @@ function init() {
},
methods: {
formatDate,
+ formatDuration,
isAfter: (dateA, dateB) => dayjs(dateA).isAfter(dateB),
isBefore: (dateA, dateB) => dayjs(dateA).isBefore(dateB),
},
diff --git a/migrations/20190325001339_releases.js b/migrations/20190325001339_releases.js
index 2f3d2cb1..bbad1083 100644
--- a/migrations/20190325001339_releases.js
+++ b/migrations/20190325001339_releases.js
@@ -853,7 +853,7 @@ exports.up = knex => Promise.resolve()
.references('id')
.inTable('media');
}))
- .then(() => knex.schema.createTable('chapters', (table) => {
+ .then(() => knex.schema.createTable('clips', (table) => {
table.increments('id', 16);
table.integer('release_id', 12)
@@ -861,9 +861,9 @@ exports.up = knex => Promise.resolve()
.inTable('releases')
.notNullable();
- table.integer('chapter', 6);
+ table.integer('clip', 6);
- table.unique(['release_id', 'chapter']);
+ table.unique(['release_id', 'clip']);
table.text('title');
table.text('description');
@@ -882,44 +882,44 @@ exports.up = knex => Promise.resolve()
table.datetime('created_at')
.defaultTo(knex.fn.now());
}))
- .then(() => knex.schema.createTable('chapters_posters', (table) => {
- table.integer('chapter_id', 16)
+ .then(() => knex.schema.createTable('clips_posters', (table) => {
+ table.integer('clip_id', 16)
.notNullable()
.references('id')
- .inTable('chapters');
+ .inTable('clips');
table.text('media_id', 21)
.notNullable()
.references('id')
.inTable('media');
- table.unique('chapter_id');
+ table.unique('clip_id');
}))
- .then(() => knex.schema.createTable('chapters_photos', (table) => {
- table.integer('chapter_id', 16)
+ .then(() => knex.schema.createTable('clips_photos', (table) => {
+ table.integer('clip_id', 16)
.notNullable()
.references('id')
- .inTable('chapters');
+ .inTable('clips');
table.text('media_id', 21)
.notNullable()
.references('id')
.inTable('media');
- table.unique(['chapter_id', 'media_id']);
+ table.unique(['clip_id', 'media_id']);
}))
- .then(() => knex.schema.createTable('chapters_tags', (table) => {
+ .then(() => knex.schema.createTable('clips_tags', (table) => {
table.integer('tag_id', 12)
.notNullable()
.references('id')
.inTable('tags');
- table.integer('chapter_id', 16)
+ table.integer('clip_id', 16)
.notNullable()
.references('id')
- .inTable('chapters');
+ .inTable('clips');
- table.unique(['tag_id', 'chapter_id']);
+ table.unique(['tag_id', 'clip_id']);
}))
// SEARCH
.then(() => { // eslint-disable-line arrow-body-style
@@ -1100,9 +1100,9 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
DROP TABLE IF EXISTS movies_scenes CASCADE;
DROP TABLE IF EXISTS movies_trailers CASCADE;
- DROP TABLE IF EXISTS chapters_tags CASCADE;
- DROP TABLE IF EXISTS chapters_posters CASCADE;
- DROP TABLE IF EXISTS chapters_photos CASCADE;
+ DROP TABLE IF EXISTS clips_tags CASCADE;
+ DROP TABLE IF EXISTS clips_posters CASCADE;
+ DROP TABLE IF EXISTS clips_photos CASCADE;
DROP TABLE IF EXISTS batches CASCADE;
@@ -1122,7 +1122,7 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
DROP TABLE IF EXISTS tags_posters CASCADE;
DROP TABLE IF EXISTS tags_photos CASCADE;
DROP TABLE IF EXISTS movies CASCADE;
- DROP TABLE IF EXISTS chapters CASCADE;
+ DROP TABLE IF EXISTS clips CASCADE;
DROP TABLE IF EXISTS releases CASCADE;
DROP TABLE IF EXISTS actors CASCADE;
DROP TABLE IF EXISTS directors CASCADE;
diff --git a/seeds/00_tags.js b/seeds/00_tags.js
index cd532887..cfc25f98 100644
--- a/seeds/00_tags.js
+++ b/seeds/00_tags.js
@@ -58,7 +58,7 @@ const groups = [
const tags = [
{
- name: '3d',
+ name: '3D',
slug: '3d',
description: 'Available in 3D.',
},
diff --git a/seeds/03_studios.js b/seeds/03_studios.js
index 3f210473..19de0282 100644
--- a/seeds/03_studios.js
+++ b/seeds/03_studios.js
@@ -146,6 +146,12 @@ const studios = [
url: 'https://www.legalporno.com/studios/kinky-sex',
parent: 'legalporno',
},
+ {
+ slug: 'sexyangelproductions',
+ name: 'Sexy Angel Productions',
+ url: 'https://www.legalporno.com/studios/sexy-angel-productions',
+ parent: 'legalporno',
+ },
{
slug: 'nfstudio',
name: 'N&F Studio',
diff --git a/src/scrapers/inthecrack.js b/src/scrapers/inthecrack.js
index 3dad77a3..29a98066 100644
--- a/src/scrapers/inthecrack.js
+++ b/src/scrapers/inthecrack.js
@@ -38,7 +38,7 @@ function scrapeScene({ query, html }, url, channel) {
release.poster = qu.prefixUrl(html.match(/background-image: url\('(.*)'\)/)?.[1], channel.url);
- release.chapters = query.all('.ClipOuter').map((el) => {
+ release.clips = query.all('.ClipOuter').map((el) => {
const chapter = {};
chapter.title = query.text(el, 'h4');
diff --git a/src/scrapers/vixen.js b/src/scrapers/vixen.js
index cc940f11..9ec8f280 100644
--- a/src/scrapers/vixen.js
+++ b/src/scrapers/vixen.js
@@ -49,7 +49,10 @@ async function getTrailer(scene, site, url) {
file: scene.previewVideoUrl1080P,
sizes: qualities.join('+'),
type: 'trailer',
- }, { referer: url });
+ }, {
+ referer: url,
+ origin: site.url,
+ });
if (!tokenRes.ok) {
return null;
diff --git a/src/store-releases.js b/src/store-releases.js
index 1cfd0998..6562422a 100644
--- a/src/store-releases.js
+++ b/src/store-releases.js
@@ -243,44 +243,44 @@ async function updateReleasesSearch(releaseIds) {
}
}
-async function storeChapters(releases) {
- const chapters = releases.map(release => release.chapters?.map((chapter, index) => ({
- title: chapter.title,
- description: chapter.description,
+async function storeClips(releases) {
+ const clips = releases.map(release => release.clips?.map((clip, index) => ({
+ title: clip.title,
+ description: clip.description,
releaseId: release.id,
- chapter: index + 1,
- duration: chapter.duration,
- poster: chapter.poster,
- photos: chapter.photos,
- tags: chapter.tags,
+ clip: index + 1,
+ duration: clip.duration,
+ poster: clip.poster,
+ photos: clip.photos,
+ tags: clip.tags,
}))).flat().filter(Boolean);
- const curatedChapterEntries = chapters.map(chapter => ({
- title: chapter.title,
- description: chapter.description,
- duration: chapter.duration,
- release_id: chapter.releaseId,
- chapter: chapter.chapter,
+ const curatedClipEntries = clips.map(clip => ({
+ title: clip.title,
+ description: clip.description,
+ duration: clip.duration,
+ release_id: clip.releaseId,
+ clip: clip.clip,
}));
- const storedChapters = await bulkInsert('chapters', curatedChapterEntries);
- const chapterIdsByReleaseIdAndChapter = storedChapters.reduce((acc, chapter) => ({
+ const storedClips = await bulkInsert('clips', curatedClipEntries);
+ const clipIdsByReleaseIdAndClip = storedClips.reduce((acc, clip) => ({
...acc,
- [chapter.release_id]: {
- ...acc[chapter.release_id],
- [chapter.chapter]: chapter.id,
+ [clip.release_id]: {
+ ...acc[clip.release_id],
+ [clip.clip]: clip.id,
},
}), {});
- const chaptersWithId = chapters.map(chapter => ({
- ...chapter,
- id: chapterIdsByReleaseIdAndChapter[chapter.releaseId][chapter.chapter],
+ const clipsWithId = clips.map(clip => ({
+ ...clip,
+ id: clipIdsByReleaseIdAndClip[clip.releaseId][clip.clip],
}));
- await associateReleaseTags(chaptersWithId, 'chapter');
+ await associateReleaseTags(clipsWithId, 'clip');
// media is more error-prone, associate separately
- await associateReleaseMedia(chaptersWithId, 'chapter');
+ await associateReleaseMedia(clipsWithId, 'clip');
}
async function storeScenes(releases) {
@@ -318,7 +318,7 @@ async function storeScenes(releases) {
await scrapeActors(actors.map(actor => actor.name));
}
- await storeChapters(releasesWithId);
+ await storeClips(releasesWithId);
logger.info(`Stored ${storedReleaseEntries.length} releases`);