diff --git a/assets/components/header/filter-bar.vue b/assets/components/header/filter-bar.vue index 5f73c972..20709932 100644 --- a/assets/components/header/filter-bar.vue +++ b/assets/components/header/filter-bar.vue @@ -3,47 +3,80 @@ + + + + + + @@ -82,6 +115,10 @@ function range(state) { return state.ui.range; } +function batch(state) { + return state.ui.batch; +} + async function setFilter(newFilter) { this.$store.dispatch('setFilter', newFilter); @@ -94,6 +131,12 @@ async function setRange(newRange) { await this.fetchReleases(); } +async function setBatch(newBatch) { + this.$store.dispatch('setBatch', newBatch); + + await this.fetchReleases(); +} + export default { components: { Filters, @@ -108,11 +151,13 @@ export default { ...mapState({ filter, range, + batch, }), }, methods: { setFilter, setRange, + setBatch, }, }; diff --git a/assets/components/tile/release.vue b/assets/components/tile/release.vue index 97a6a630..bf716026 100644 --- a/assets/components/tile/release.vue +++ b/assets/components/tile/release.vue @@ -44,7 +44,7 @@ v-tooltip.bottom="release.url && `View scene on ${release.site.name}`" :title="release.url && `View scene on ${release.site.name}`" :href="release.url" - :class="{ upcoming: isAfter(release.date, new Date()) }" + :class="{ upcoming: isAfter(release.date, new Date()), new: release.isNew }" target="_blank" rel="noopener noreferrer" class="date" @@ -53,7 +53,7 @@ ({ + latest: () => ({ after: '1900-01-01', before: dayjs(new Date()).add(1, 'day').format('YYYY-MM-DD'), }), @@ -27,8 +27,13 @@ function after(state) { return dateRanges[state.range]().after; } +function isNew(state) { + return state.batch === 'new' ? [true] : [true, false]; +} + export default { rangeDates, before, after, + isNew, }; diff --git a/assets/js/ui/mutations.js b/assets/js/ui/mutations.js index 56fb11b5..85b7ce6a 100644 --- a/assets/js/ui/mutations.js +++ b/assets/js/ui/mutations.js @@ -6,7 +6,12 @@ function setRange(state, range) { state.range = range; } +function setBatch(state, batch) { + state.batch = batch; +} + export default { setFilter, setRange, + setBatch, }; diff --git a/assets/js/ui/state.js b/assets/js/ui/state.js index 318904b2..88922f43 100644 --- a/assets/js/ui/state.js +++ b/assets/js/ui/state.js @@ -1,7 +1,9 @@ const storedFilter = localStorage.getItem('filter'); const storedRange = localStorage.getItem('range'); +const storedBatch = localStorage.getItem('batch'); export default { filter: storedFilter ? storedFilter.split(',') : ['gay', 'transsexual'], - range: storedRange || 'new', + range: storedRange || 'latest', + batch: storedBatch || 'all', }; diff --git a/migrations/20190325001339_releases.js b/migrations/20190325001339_releases.js index 4e9cd188..14348a50 100644 --- a/migrations/20190325001339_releases.js +++ b/migrations/20190325001339_releases.js @@ -327,6 +327,13 @@ exports.up = knex => Promise.resolve() table.datetime('created_at') .defaultTo(knex.fn.now()); })) + .then(() => knex.schema.createTable('batches', (table) => { + table.increments('id', 12); + table.text('comment'); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) .then(() => knex.schema.createTable('releases', (table) => { table.increments('id', 16); @@ -362,7 +369,14 @@ exports.up = knex => Promise.resolve() table.boolean('deep'); table.string('deep_url', 1000); - table.string('batch'); + table.integer('created_batch_id', 12) + .references('id') + .inTable('batches'); + + table.integer('updated_batch_id', 12) + .references('id') + .inTable('batches'); + table.datetime('created_at') .defaultTo(knex.fn.now()); })) @@ -508,6 +522,10 @@ exports.up = knex => Promise.resolve() url ILIKE ('%' || search || '%') $$ LANGUAGE SQL STABLE; + CREATE FUNCTION releases_is_new(release releases) RETURNS boolean AS $$ + SELECT NOT EXISTS(SELECT true FROM batches WHERE batches.id = release.created_batch_id + 1 LIMIT 1); + $$ LANGUAGE sql STABLE; + COMMENT ON COLUMN actors.height IS E'@omit read,update,create,delete,all,many'; COMMENT ON COLUMN actors.weight IS E'@omit read,update,create,delete,all,many'; `)); @@ -527,6 +545,7 @@ exports.down = knex => knex.raw(` DROP TABLE IF EXISTS releases_teasers CASCADE; DROP TABLE IF EXISTS releases_tags CASCADE; DROP TABLE IF EXISTS releases_search CASCADE; + DROP TABLE IF EXISTS batches CASCADE; DROP TABLE IF EXISTS actors_avatars CASCADE; DROP TABLE IF EXISTS actors_photos CASCADE; DROP TABLE IF EXISTS actors_social CASCADE; diff --git a/src/media.js b/src/media.js index c321139a..e510a623 100644 --- a/src/media.js +++ b/src/media.js @@ -271,7 +271,7 @@ function groupItems(items) { } async function storeMedia(sources, domain, role, { entropyFilter = 2.5 } = {}) { - const presentSources = sources.filter(Boolean); + const presentSources = sources.filter(source => typeof source === 'string' || Array.isArray(source) || (source && source.src)); if (presentSources.length === 0) { return {}; diff --git a/src/releases.js b/src/releases.js index 93f72a34..a2ab4d63 100644 --- a/src/releases.js +++ b/src/releases.js @@ -202,7 +202,7 @@ async function attachStudio(release) { }; } -async function curateReleaseEntry(release) { +async function curateReleaseEntry(release, batchId, existingRelease) { const slug = slugify(release.title, { encode: true, limit: config.titleSlugLength, @@ -227,6 +227,8 @@ async function curateReleaseEntry(release) { // rating: release.rating && release.rating.stars && Math.floor(release.rating.stars), deep: typeof release.deep === 'boolean' ? release.deep : false, deep_url: release.deepUrl, + updated_batch_id: batchId, + ...(!existingRelease && { created_batch_id: batchId }), }; return curatedRelease; @@ -380,6 +382,7 @@ async function updateReleasesSearch(releaseIds) { sites.slug || ' ' || networks.name || ' ' || networks.slug || ' ' || + releases.shoot_id || ' ' || EXTRACT(YEAR FROM releases.date) || ' ' || CAST(EXTRACT(MONTH FROM releases.date) AS VARCHAR) || ' ' || CAST(EXTRACT(DAY FROM releases.date) AS VARCHAR) || ' ' || @@ -408,7 +411,7 @@ async function updateReleasesSearch(releaseIds) { } } -async function storeRelease(release) { +async function storeRelease(release, batchId) { const existingRelease = await knex('releases') .where({ entry_id: release.entryId, @@ -416,7 +419,7 @@ async function storeRelease(release) { }) .first(); - const curatedRelease = await curateReleaseEntry(release); + const curatedRelease = await curateReleaseEntry(release, batchId, existingRelease); if (existingRelease && !argv.redownload) { return existingRelease; @@ -453,11 +456,13 @@ async function storeRelease(release) { } async function storeReleases(releases) { + const [batchId] = await knex('batches').insert({ comment: null }).returning('id'); + const storedReleases = await Promise.map(releases, async (release) => { try { const releaseWithChannelSite = await attachChannelSite(release); const releaseWithStudio = await attachStudio(releaseWithChannelSite); - const { id, slug } = await storeRelease(releaseWithStudio); + const { id, slug } = await storeRelease(releaseWithStudio, batchId); return { id, diff --git a/src/scrapers/private.js b/src/scrapers/private.js index ba5dcb26..502aad72 100644 --- a/src/scrapers/private.js +++ b/src/scrapers/private.js @@ -84,12 +84,12 @@ async function scrapeScene(html, url, site) { release.likes = Number($('.content-desc #social-actions #likes').text()); const posterScript = $('script:contains(poster)').html(); - const posterLink = posterScript.slice(posterScript.indexOf('https://'), posterScript.indexOf('.jpg') + 4); - release.poster = $('meta[property="og:image"]').attr('content') || posterLink; + const posterLink = posterScript?.slice(posterScript.indexOf('https://'), posterScript.indexOf('.jpg') + 4); + release.poster = $('meta[property="og:image"]').attr('content') || posterLink || $('#trailer_player_finished img').attr('src'); - release.trailer = { - src: $('meta[property="og:video"]').attr('content') || $('#videojs-trailer source').attr('src'), - }; + const trailer = $('meta[property="og:video"]').attr('content') || $('#videojs-trailer source').attr('src'); + + if (trailer) release.trailer = { src: trailer }; release.photos = await getPhotos(release.entryId, site); release.movie = $('a[data-track="FULL MOVIE"]').attr('href'); diff --git a/src/web/plugins/releases.js b/src/web/plugins/releases.js index 109f2d95..ec0bf68a 100644 --- a/src/web/plugins/releases.js +++ b/src/web/plugins/releases.js @@ -4,8 +4,14 @@ const { makeExtendSchemaPlugin, gql } = require('graphile-utils'); const schemaExtender = makeExtendSchemaPlugin(_build => ({ typeDefs: gql` + extend type Release {} `, resolvers: { + Release: { + async foo(_parent, _args, _context, _info) { + // template + }, + }, }, }));