diff --git a/assets/components/releases/release.vue b/assets/components/releases/release.vue
index d8d4594c..4e857ef2 100644
--- a/assets/components/releases/release.vue
+++ b/assets/components/releases/release.vue
@@ -50,6 +50,20 @@
icon="question2"
/>
+
+
+
+
@@ -223,7 +237,7 @@ import Actor from '../actors/tile.vue';
import Releases from './releases.vue';
import Scroll from '../scroll/scroll.vue';
-async function fetchRelease() {
+async function fetchRelease(scroll = true) {
if (this.$route.name === 'scene') {
this.release = await this.$store.dispatch('fetchReleaseById', this.$route.params.releaseId);
}
@@ -232,11 +246,37 @@ async function fetchRelease() {
this.release = await this.$store.dispatch('fetchMovieById', this.$route.params.releaseId);
}
- if (this.$refs.content) {
+ if (scroll && this.$refs.content) {
this.$refs.content.scrollTop = 0;
}
}
+async function stashScene() {
+ this.$store.dispatch('stashScene', {
+ sceneId: this.release.id,
+ stashId: this.$store.getters.favorites.id,
+ });
+
+ this.fetchRelease(false);
+}
+
+async function unstashScene() {
+ this.$store.dispatch('unstashScene', {
+ sceneId: this.release.id,
+ stashId: this.$store.getters.favorites.id,
+ });
+
+ this.fetchRelease(false);
+}
+
+function me() {
+ return this.$store.state.auth.user;
+}
+
+function isStashed() {
+ return this.release.stashes?.length > 0;
+}
+
function bannerBackground() {
return (this.release.poster && this.getBgPath(this.release.poster, 'thumbnail'))
|| (this.release.covers.length > 0 && this.getBgPath(this.release.covers[0], 'thumbnail'));
@@ -271,6 +311,8 @@ export default {
computed: {
pageTitle,
bannerBackground,
+ isStashed,
+ me,
showAlbum,
},
watch: {
@@ -279,6 +321,8 @@ export default {
mounted: fetchRelease,
methods: {
fetchRelease,
+ stashScene,
+ unstashScene,
},
};
@@ -353,6 +397,22 @@ export default {
color: var(--shadow);
}
+.stash.icon {
+ width: 1.5rem;
+ height: 1.5rem;
+ padding: 0 1rem;
+ fill: var(--darken);
+
+ &.stashed {
+ fill: var(--primary);
+ }
+
+ &:hover {
+ fill: var(--primary);
+ cursor: pointer;
+ }
+}
+
.album-toggle {
height: fit-content;
display: inline-flex;
diff --git a/assets/js/fragments.js b/assets/js/fragments.js
index d4e6a0e5..ce88777a 100644
--- a/assets/js/fragments.js
+++ b/assets/js/fragments.js
@@ -346,6 +346,21 @@ const releaseFragment = `
}
}
}
+ stashes: stashesScenesBySceneId(
+ filter: {
+ stash: {
+ userId: {
+ equalTo: $userId
+ }
+ }
+ }
+ ) @include(if: $hasAuth) {
+ stash {
+ id
+ name
+ slug
+ }
+ }
}
`;
diff --git a/assets/js/releases/actions.js b/assets/js/releases/actions.js
index 384d57fc..b5d06ff1 100644
--- a/assets/js/releases/actions.js
+++ b/assets/js/releases/actions.js
@@ -37,11 +37,17 @@ function initReleasesActions(store, router) {
// const release = await get(`/releases/${releaseId}`);
const { release } = await graphql(`
- query Release($releaseId:Int!) {
+ query Release(
+ $releaseId: Int!
+ $hasAuth: Boolean!
+ $userId: Int
+ ) {
${releaseFragment}
}
`, {
releaseId: Number(releaseId),
+ hasAuth: !!store.state.auth.user,
+ userId: store.state.auth.user?.id,
});
if (!release) {
diff --git a/assets/js/stashes/actions.js b/assets/js/stashes/actions.js
index 0188d8a1..4d3dbee4 100644
--- a/assets/js/stashes/actions.js
+++ b/assets/js/stashes/actions.js
@@ -9,9 +9,19 @@ function initStashesActions(_store, _router) {
await del(`/stashes/${stashId}/actors/${actorId}`);
}
+ async function stashScene(context, { sceneId, stashId }) {
+ await post(`/stashes/${stashId}/scenes`, { sceneId });
+ }
+
+ async function unstashScene(context, { sceneId, stashId }) {
+ await del(`/stashes/${stashId}/scenes/${sceneId}`);
+ }
+
return {
stashActor,
+ stashScene,
unstashActor,
+ unstashScene,
};
}
diff --git a/assets/js/users/actions.js b/assets/js/users/actions.js
index c62f236d..fa3a074e 100644
--- a/assets/js/users/actions.js
+++ b/assets/js/users/actions.js
@@ -1,4 +1,6 @@
import { graphql } from '../api';
+import { releaseFields } from '../fragments';
+import { curateUser } from '../curate';
function initUsersActions(_store, _router) {
async function fetchUser(context, username) {
@@ -43,45 +45,7 @@ function initUsersActions(_store, _router) {
scenes: stashesScenes {
comment
scene {
- id
- title
- slug
- url
- date
- actors: releasesActors {
- actor {
- id
- name
- slug
- }
- }
- tags: releasesTags {
- tag {
- id
- name
- slug
- }
- }
- entity {
- id
- name
- slug
- independent
- parent {
- id
- name
- slug
- independent
- }
- }
- poster: releasesPosterByReleaseId {
- media {
- path
- thumbnail
- lazy
- isS3
- }
- }
+ ${releaseFields}
}
}
}
@@ -91,7 +55,7 @@ function initUsersActions(_store, _router) {
username,
});
- return user;
+ return curateUser(user);
}
return {
diff --git a/migrations/20190325001339_releases.js b/migrations/20190325001339_releases.js
index 60b19005..6de2caba 100644
--- a/migrations/20190325001339_releases.js
+++ b/migrations/20190325001339_releases.js
@@ -1328,7 +1328,6 @@ exports.up = knex => Promise.resolve()
));
`, {
visitor: knex.raw(config.database.query.user),
- password: knex.raw(config.database.query.password),
});
})
// VIEWS AND COMMENTS
diff --git a/src/app.js b/src/app.js
index 82fef6f1..785d8da7 100644
--- a/src/app.js
+++ b/src/app.js
@@ -21,7 +21,7 @@ function logActive() {
setTimeout(() => {
log();
logActive();
- }, 600000);
+ }, argv.logActive || 60000);
}
async function init() {
diff --git a/src/stashes.js b/src/stashes.js
index 47bb4e79..a61fffdc 100644
--- a/src/stashes.js
+++ b/src/stashes.js
@@ -48,19 +48,31 @@ async function stashScene(sceneId, stashId, sessionUser) {
await knex('stashes_scenes')
.insert({
stash_id: stash.id,
- actor_id: sceneId,
+ scene_id: sceneId,
});
}
async function unstashActor(actorId, stashId, sessionUser) {
await knex
- .from('stashes_actors')
- .whereIn('stashes_actors.id', knex('stashes_actors')
- .select('stashes_actors.id')
+ .from('stashes_actors AS deletable')
+ .where('deletable.actor_id', actorId)
+ .where('deletable.stash_id', stashId)
+ .whereExists(knex('stashes_actors') // verify user owns this stash
.leftJoin('stashes', 'stashes.id', 'stashes_actors.stash_id')
- .where('stashes.user_id', sessionUser.id) // verify user owns this stash
- .where('stashes_actors.actor_id', actorId)
- .where('stashes_actors.stash_id', stashId))
+ .where('stashes_actors.stash_id', knex.raw('deletable.stash_id'))
+ .where('stashes.user_id', sessionUser.id))
+ .delete();
+}
+
+async function unstashScene(sceneId, stashId, sessionUser) {
+ await knex
+ .from('stashes_scenes AS deletable')
+ .where('deletable.scene_id', sceneId)
+ .where('deletable.stash_id', stashId)
+ .whereExists(knex('stashes_scenes') // verify user owns this stash
+ .leftJoin('stashes', 'stashes.id', 'stashes_scenes.stash_id')
+ .where('stashes_scenes.stash_id', knex.raw('deletable.stash_id'))
+ .where('stashes.user_id', sessionUser.id))
.delete();
}
@@ -68,6 +80,6 @@ module.exports = {
curateStash,
stashActor,
stashScene,
- // unstashScene,
+ unstashScene,
unstashActor,
};
diff --git a/src/utils/http.js b/src/utils/http.js
index 4573f833..99856ffc 100644
--- a/src/utils/http.js
+++ b/src/utils/http.js
@@ -80,20 +80,9 @@ function getLimiter(options = {}, url) {
return limiters[interval][concurrency];
}
-async function request(method = 'get', url, body, requestOptions = {}, limiter, timeout) {
+async function request(method = 'get', url, body, requestOptions = {}, limiter) {
const http = requestOptions.session || bhttp;
-
- const options = {
- ...defaultOptions,
- ...requestOptions,
- headers: {
- ...defaultOptions.headers,
- ...requestOptions.headers,
- },
- responseTimeout: requestOptions.responseTimeout || requestOptions.timeout || defaultOptions.timeout,
- stream: !!requestOptions.destination,
- session: null,
- };
+ const options = requestOptions;
const withProxy = useProxy(url);
@@ -107,8 +96,10 @@ async function request(method = 'get', url, body, requestOptions = {}, limiter,
? http[method](url, body, options)
: http[method](url, options));
- timeout.cancel();
+ return res;
+}
+async function finalizeResult(res, options) {
if (options.destination) {
// res.on('progress', (bytes, totalBytes) => logger.silly(`Downloaded ${Math.round((bytes / totalBytes) * 100)}% of ${url}`));
@@ -140,21 +131,39 @@ async function request(method = 'get', url, body, requestOptions = {}, limiter,
function getTimeout(options, url) {
return new Promise((resolve, reject, onCancel) => {
- const timeoutId = setTimeout(() => {
+ const timeout = setTimeout(() => {
reject(new Error(`URL ${url} timed out`));
}, (options?.timeout || defaultOptions.timeout) + 10000);
- onCancel(() => clearTimeout(timeoutId));
+ onCancel(() => {
+ clearTimeout(timeout);
+ });
});
}
-async function scheduleRequest(method = 'get', url, body, options) {
+async function scheduleRequest(method = 'get', url, body, requestOptions = {}) {
+ const options = {
+ ...defaultOptions,
+ ...requestOptions,
+ headers: {
+ ...defaultOptions.headers,
+ ...requestOptions.headers,
+ },
+ responseTimeout: requestOptions.responseTimeout || requestOptions.timeout || defaultOptions.timeout,
+ stream: !!requestOptions.destination,
+ session: null,
+ };
+
const limiter = getLimiter(options, url);
const timeout = getTimeout(options, url);
- const result = await limiter.schedule(() => Promise.race([request(method, url, body, options, limiter, timeout), timeout]));
+ const result = await limiter.schedule(async () => Promise.race([request(method, url, body, options, limiter), timeout]));
- return result;
+ timeout.cancel();
+
+ const curatedResult = await finalizeResult(result, options);
+
+ return curatedResult;
}
async function get(url, options) {