diff --git a/assets/components/actors/actor.vue b/assets/components/actors/actor.vue
index 8792cd80..2b4eaa96 100644
--- a/assets/components/actors/actor.vue
+++ b/assets/components/actors/actor.vue
@@ -188,7 +188,6 @@
{{ actor.description }}
- Actors
+ Actors
@@ -23,7 +23,7 @@
class="nav-link"
:class="{ active: active === 'networks' }"
>
- Networks
+ Networks
@@ -33,7 +33,7 @@
class="nav-link"
:class="{ active: active === 'tags' }"
>
- Tags
+ Tags
@@ -83,8 +83,9 @@ export default {
}
.nav-link {
- display: inline-flex;
+ display: flex;
align-items: center;
+ justify-content: center;
padding: 1rem;
border-bottom: solid 5px transparent;
color: $shadow;
@@ -114,4 +115,24 @@ export default {
}
}
}
+
+@media(max-width: $breakpoint0) {
+ .nav-label {
+ display: none;
+ }
+
+ .nav .nolist {
+ display: flex;
+ }
+
+ .nav,
+ .nav-item {
+ flex-grow: 1;
+ }
+
+ .nav-link {
+
+
+ }
+}
diff --git a/assets/components/networks/networks.vue b/assets/components/networks/networks.vue
index c50f0e06..508dcb6c 100644
--- a/assets/components/networks/networks.vue
+++ b/assets/components/networks/networks.vue
@@ -31,7 +31,7 @@ export default {
+
diff --git a/assets/components/tags/tags.vue b/assets/components/tags/tags.vue
index 6ef541d4..379d7fba 100644
--- a/assets/components/tags/tags.vue
+++ b/assets/components/tags/tags.vue
@@ -1,10 +1,44 @@
@@ -12,7 +46,42 @@
import Tag from '../tile/tag.vue';
async function mounted() {
- this.tags = await this.$store.dispatch('fetchTags', { priority: [9] });
+ const tags = await this.$store.dispatch('fetchTags', {
+ slug: [
+ 'airtight',
+ 'anal',
+ 'double-anal',
+ 'double-penetration',
+ 'double-vaginal',
+ 'da-tp',
+ 'dv-tp',
+ 'triple-anal',
+ 'blowbang',
+ 'gangbang',
+ 'mff',
+ 'mfm',
+ 'orgy',
+ 'asian',
+ 'caucasian',
+ 'ebony',
+ 'interracial',
+ 'latina',
+ 'anal-creampie',
+ 'bukkake',
+ 'creampie',
+ 'facial',
+ 'oral-creampie',
+ 'swallowing',
+ ],
+ });
+
+ this.tags = tags.reduce((acc, tag) => {
+ if (acc[tag.group.slug]) {
+ return { ...acc, [tag.group.slug]: [...acc[tag.group.slug], tag] };
+ }
+
+ return { ...acc, [tag.group.slug]: [tag] };
+ }, {});
}
export default {
@@ -21,7 +90,7 @@ export default {
},
data() {
return {
- tags: [],
+ tags: {},
};
},
mounted,
@@ -30,9 +99,12 @@ export default {
diff --git a/assets/components/tile/tag.vue b/assets/components/tile/tag.vue
index cefe46a5..3923ec3c 100644
--- a/assets/components/tile/tag.vue
+++ b/assets/components/tile/tag.vue
@@ -4,15 +4,14 @@
:title="tag.name"
class="tile"
>
+ {{ tag.name }}
+
-
- {{ tag.name }}
@@ -24,11 +23,6 @@ export default {
default: null,
},
},
- data() {
- return {
- imageAvailable: true,
- };
- },
};
@@ -36,7 +30,8 @@ export default {
@import 'theme';
.tile {
- background: $background;
+ color: $text-contrast;
+ background: $profile;
display: flex;
flex-direction: column;
align-items: center;
@@ -53,20 +48,13 @@ export default {
}
.title {
- color: $text;
+ height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1rem;
- font-weight: bold;
padding: .5rem 1rem;
-}
-
-.title {
- color: $text;
- height: 100%;
- display: flex;
- align-items: center;
- margin: 0;
+ font-weight: bold;
+ text-transform: capitalize;
}
diff --git a/assets/css/_theme.scss b/assets/css/_theme.scss
index 83ab8070..ca20dffc 100644
--- a/assets/css/_theme.scss
+++ b/assets/css/_theme.scss
@@ -1,4 +1,5 @@
/* $primary: #ff886c; */
+$breakpoint0: 540px;
$breakpoint: 720px;
$breakpoint2: 900px;
$breakpoint3: 1200px;
diff --git a/assets/js/api.js b/assets/js/api.js
index bb1904c2..0f0cc945 100644
--- a/assets/js/api.js
+++ b/assets/js/api.js
@@ -1,7 +1,8 @@
import config from 'config';
async function get(endpoint, query = {}) {
- const q = new URLSearchParams(query).toString();
+ const curatedQuery = Object.entries(query).reduce((acc, [key, value]) => (value ? { ...acc, [key]: value } : acc), {}); // remove empty values
+ const q = new URLSearchParams(curatedQuery).toString();
const res = await fetch(`${config.api.url}${endpoint}?${q}`, {
method: 'GET',
diff --git a/assets/js/tags/actions.js b/assets/js/tags/actions.js
index 6a608ed2..8da5bc78 100644
--- a/assets/js/tags/actions.js
+++ b/assets/js/tags/actions.js
@@ -1,12 +1,23 @@
import { get } from '../api';
function initTagsActions(store, _router) {
- async function fetchTags({ _commit }, { tagId, limit = 100, priority }) {
+ async function fetchTags({ _commit }, {
+ tagId,
+ limit = 100,
+ slug,
+ group,
+ priority,
+ }) {
if (tagId) {
return get(`/tags/${tagId}`);
}
- return get('/tags', { limit, priority });
+ return get('/tags', {
+ limit,
+ slug,
+ priority,
+ group,
+ });
}
async function fetchTagReleases({ _commit }, tagId) {
diff --git a/migrations/20190325001339_releases.js b/migrations/20190325001339_releases.js
index 74dc1aa9..6d76dc6e 100644
--- a/migrations/20190325001339_releases.js
+++ b/migrations/20190325001339_releases.js
@@ -228,6 +228,7 @@ exports.up = knex => Promise.resolve()
table.string('quality', 6);
table.string('hash');
+ table.text('comment');
table.string('source', 1000);
table.unique(['domain', 'target_id', 'role', 'hash']);
diff --git a/package-lock.json b/package-lock.json
index d536915b..71c96baf 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9693,6 +9693,124 @@
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
},
+ "showdown": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/showdown/-/showdown-1.9.1.tgz",
+ "integrity": "sha512-9cGuS382HcvExtf5AHk7Cb4pAeQQ+h0eTr33V1mu+crYWV4KvWAw6el92bDrqGEk5d46Ai/fhbEUwqJ/mTCNEA==",
+ "requires": {
+ "yargs": "^14.2"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
+ },
+ "cliui": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+ "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+ "requires": {
+ "string-width": "^3.1.0",
+ "strip-ansi": "^5.2.0",
+ "wrap-ansi": "^5.1.0"
+ }
+ },
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "p-limit": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz",
+ "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==",
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "requires": {
+ "p-limit": "^2.0.0"
+ }
+ },
+ "p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ },
+ "wrap-ansi": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+ "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "string-width": "^3.0.0",
+ "strip-ansi": "^5.0.0"
+ }
+ },
+ "yargs": {
+ "version": "14.2.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.2.tgz",
+ "integrity": "sha512-/4ld+4VV5RnrynMhPZJ/ZpOCGSCeghMykZ3BhdFBDa9Wy/RH6uEGNWDJog+aUlq+9OM1CFTgtYRW5Is1Po9NOA==",
+ "requires": {
+ "cliui": "^5.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^15.0.0"
+ }
+ },
+ "yargs-parser": {
+ "version": "15.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.0.tgz",
+ "integrity": "sha512-xLTUnCMc4JhxrPEPUYD5IBR1mWCK/aT6+RJ/K29JY2y1vD+FhtgKK0AXRWvI262q3QSffAQuTouFIKUuHX89wQ==",
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ }
+ }
+ },
"sigmund": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
diff --git a/package.json b/package.json
index 0edaca5d..07cfcb52 100644
--- a/package.json
+++ b/package.json
@@ -86,6 +86,7 @@
"react": "^16.8.6",
"react-dom": "^16.8.6",
"sharp": "^0.23.2",
+ "showdown": "^1.9.1",
"tough-cookie": "^3.0.1",
"tty-table": "^2.7.0",
"url-pattern": "^1.0.3",
diff --git a/public/css/style.css b/public/css/style.css
index d1b81c67..36dab8a0 100644
--- a/public/css/style.css
+++ b/public/css/style.css
@@ -629,7 +629,7 @@
.networks[data-v-4709d404] {
display: grid;
- grid-template-columns: repeat(auto-fit, 15rem);
+ grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
grid-gap: 1rem;
padding: 1rem;
}
@@ -644,9 +644,6 @@
.photos .avatar-link[data-v-0a0430c7] {
display: none;
}
-.photo-link[data-v-0a0430c7] {
- height: 15rem;
-}
.photo[data-v-0a0430c7] {
width: 100%;
height: 100%;
@@ -923,26 +920,57 @@
}
/* $primary: #ff886c; */
+.description a {
+ color: #cc4466;
+ text-decoration: inherit;
+}
+.description a:hover {
+ color: #ff6c88;
+}
+
+/* $primary: #ff886c; */
+.tag[data-v-7f130e7f] {
+ display: flex;
+ flex-direction: row;
+ flex-grow: 1;
+ justify-content: stretch;
+}
+.sidebar[data-v-7f130e7f] {
+ background: #222;
+ color: #fff;
+ width: 25rem;
+ box-sizing: border-box;
+ padding: 1rem;
+}
.poster[data-v-7f130e7f] {
- width: 30rem;
- height: 18rem;
+ width: 100%;
+ height: 15rem;
-o-object-fit: cover;
object-fit: cover;
}
.title[data-v-7f130e7f] {
- display: inline-block;
- padding: 1rem;
- margin: 0 .5rem 0 0;
+ padding: 0;
+ margin: 1rem 0;
text-transform: capitalize;
}
.title .icon[data-v-7f130e7f] {
+ fill: #fff;
width: 1.25rem;
height: 1.25rem;
}
+.description[data-v-7f130e7f] {
+ padding: 0;
+ margin: 0 0 1rem 0;
+ line-height: 1.5;
+}
+.photo[data-v-7f130e7f] {
+ width: 100%;
+}
/* $primary: #ff886c; */
.tile[data-v-602c6fd8] {
- background: #fff;
+ color: #fff;
+ background: #222;
display: flex;
flex-direction: column;
align-items: center;
@@ -958,27 +986,23 @@
object-fit: cover;
}
.title[data-v-602c6fd8] {
- color: #222;
+ height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1rem;
- font-weight: bold;
padding: .5rem 1rem;
-}
-.title[data-v-602c6fd8] {
- color: #222;
- height: 100%;
- display: flex;
- align-items: center;
- margin: 0;
+ font-weight: bold;
+ text-transform: capitalize;
}
.tags[data-v-66fa6284] {
+ padding: 1rem;
+}
+.tiles[data-v-66fa6284] {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
grid-gap: .5rem;
- padding: 1rem;
}
/* $primary: #ff886c; */
@@ -1146,8 +1170,9 @@ body {
display: inline-block;
}
.nav-link[data-v-10b7ec04] {
- display: inline-flex;
+ display: flex;
align-items: center;
+ justify-content: center;
padding: 1rem;
border-bottom: solid 5px transparent;
color: rgba(0, 0, 0, 0.5);
@@ -1172,6 +1197,18 @@ body {
.nav-link:hover:not(.active) .icon[data-v-10b7ec04] {
fill: #ff6c88;
}
+@media (max-width: 540px) {
+.nav-label[data-v-10b7ec04] {
+ display: none;
+}
+.nav .nolist[data-v-10b7ec04] {
+ display: flex;
+}
+.nav[data-v-10b7ec04],
+ .nav-item[data-v-10b7ec04] {
+ flex-grow: 1;
+}
+}
/* $primary: #ff886c; */
.container {
diff --git a/public/img/logos/pervcity/analoverdose.png b/public/img/logos/pervcity/analoverdose.png
new file mode 100644
index 00000000..ab589e09
Binary files /dev/null and b/public/img/logos/pervcity/analoverdose.png differ
diff --git a/public/img/logos/pervcity/bangingbeauties.png b/public/img/logos/pervcity/bangingbeauties.png
new file mode 100644
index 00000000..f708ce57
Binary files /dev/null and b/public/img/logos/pervcity/bangingbeauties.png differ
diff --git a/public/img/logos/pervcity/chocolatebjs.png b/public/img/logos/pervcity/chocolatebjs.png
new file mode 100644
index 00000000..1dd521f6
Binary files /dev/null and b/public/img/logos/pervcity/chocolatebjs.png differ
diff --git a/public/img/logos/pervcity/network.png b/public/img/logos/pervcity/network.png
index e6907c83..6b6b2984 100644
Binary files a/public/img/logos/pervcity/network.png and b/public/img/logos/pervcity/network.png differ
diff --git a/public/img/logos/pervcity/oraloverdose.png b/public/img/logos/pervcity/oraloverdose.png
new file mode 100644
index 00000000..516cc9c8
Binary files /dev/null and b/public/img/logos/pervcity/oraloverdose.png differ
diff --git a/public/img/logos/pervcity/upherasshole.png b/public/img/logos/pervcity/upherasshole.png
new file mode 100644
index 00000000..2d8328a3
Binary files /dev/null and b/public/img/logos/pervcity/upherasshole.png differ
diff --git a/public/img/tags/airtight.jpg b/public/img/tags/airtight.jpg
deleted file mode 100644
index 0cd22fe3..00000000
Binary files a/public/img/tags/airtight.jpg and /dev/null differ
diff --git a/public/img/tags/airtight_thumb.jpg b/public/img/tags/airtight_thumb.jpg
deleted file mode 100644
index 815de4e6..00000000
Binary files a/public/img/tags/airtight_thumb.jpg and /dev/null differ
diff --git a/public/img/tags/anal.jpg b/public/img/tags/anal.jpg
deleted file mode 100644
index c52ad57f..00000000
Binary files a/public/img/tags/anal.jpg and /dev/null differ
diff --git a/public/img/tags/anal_thumb.jpg b/public/img/tags/anal_thumb.jpg
deleted file mode 100644
index e5d10351..00000000
Binary files a/public/img/tags/anal_thumb.jpg and /dev/null differ
diff --git a/public/img/tags/bdsm.jpg b/public/img/tags/bdsm.jpg
deleted file mode 100644
index 21b40081..00000000
Binary files a/public/img/tags/bdsm.jpg and /dev/null differ
diff --git a/public/img/tags/bdsm_thumb.jpg b/public/img/tags/bdsm_thumb.jpg
deleted file mode 100644
index 4bbbb415..00000000
Binary files a/public/img/tags/bdsm_thumb.jpg and /dev/null differ
diff --git a/public/img/tags/double-anal.jpg b/public/img/tags/double-anal.jpg
deleted file mode 100644
index 37b415fc..00000000
Binary files a/public/img/tags/double-anal.jpg and /dev/null differ
diff --git a/public/img/tags/double-anal_thumb.jpg b/public/img/tags/double-anal_thumb.jpg
deleted file mode 100644
index 1126f772..00000000
Binary files a/public/img/tags/double-anal_thumb.jpg and /dev/null differ
diff --git a/public/img/tags/double-penetration.jpg b/public/img/tags/double-penetration.jpg
deleted file mode 100644
index 3b21b8cc..00000000
Binary files a/public/img/tags/double-penetration.jpg and /dev/null differ
diff --git a/public/img/tags/double-penetration_thumb.jpg b/public/img/tags/double-penetration_thumb.jpg
deleted file mode 100644
index 105bdaa7..00000000
Binary files a/public/img/tags/double-penetration_thumb.jpg and /dev/null differ
diff --git a/public/img/tags/double-vaginal.jpg b/public/img/tags/double-vaginal.jpg
deleted file mode 100644
index f129c538..00000000
Binary files a/public/img/tags/double-vaginal.jpg and /dev/null differ
diff --git a/public/img/tags/double-vaginal_thumb.jpg b/public/img/tags/double-vaginal_thumb.jpg
deleted file mode 100644
index bee76b43..00000000
Binary files a/public/img/tags/double-vaginal_thumb.jpg and /dev/null differ
diff --git a/public/img/tags/gangbang.jpg b/public/img/tags/gangbang.jpg
deleted file mode 100644
index f3cb80d4..00000000
Binary files a/public/img/tags/gangbang.jpg and /dev/null differ
diff --git a/public/img/tags/gangbang_thumb.jpg b/public/img/tags/gangbang_thumb.jpg
deleted file mode 100644
index 87841fbf..00000000
Binary files a/public/img/tags/gangbang_thumb.jpg and /dev/null differ
diff --git a/public/img/tags/interracial.jpg b/public/img/tags/interracial.jpg
deleted file mode 100644
index 95493de0..00000000
Binary files a/public/img/tags/interracial.jpg and /dev/null differ
diff --git a/public/img/tags/interracial_thumb.jpg b/public/img/tags/interracial_thumb.jpg
deleted file mode 100644
index 834cfdeb..00000000
Binary files a/public/img/tags/interracial_thumb.jpg and /dev/null differ
diff --git a/public/img/tags/triple-penetration.jpg b/public/img/tags/triple-penetration.jpg
deleted file mode 100644
index 7b9daddb..00000000
Binary files a/public/img/tags/triple-penetration.jpg and /dev/null differ
diff --git a/public/img/tags/triple-penetration_thumb.jpg b/public/img/tags/triple-penetration_thumb.jpg
deleted file mode 100644
index 7826f75e..00000000
Binary files a/public/img/tags/triple-penetration_thumb.jpg and /dev/null differ
diff --git a/seeds/03_studios.js b/seeds/02_studios.js
similarity index 100%
rename from seeds/03_studios.js
rename to seeds/02_studios.js
diff --git a/seeds/02_tags.js b/seeds/03_tags.js
similarity index 86%
rename from seeds/02_tags.js
rename to seeds/03_tags.js
index de82dbca..67c5fb7d 100644
--- a/seeds/02_tags.js
+++ b/seeds/03_tags.js
@@ -17,6 +17,10 @@ const groups = [
slug: 'ethnicity',
name: 'Ethnicity',
},
+ {
+ slug: 'finish',
+ name: 'Finish',
+ },
{
slug: 'group',
name: 'Group sex',
@@ -65,7 +69,7 @@ function getTags(groupsMap) {
name: 'airtight',
slug: 'airtight',
alias_for: null,
- description: 'A cock in every penetrable hole (of a woman); one in the mouth, one in the vagina, and one in the asshole.',
+ description: 'Stuffing one cock in her ass, one in her pussy, and one in her mouth, filling all of her penetrable holes and sealing her airtight like a figurative balloon. In other words, simultaneously getting [double penetrated](/tag/double-penetration), and giving a [blowjob](/tag/blowjob) or getting [facefucked](/tag/facefuck). Being airtight implies being [gangbanged](/tag/gangbang).',
priority: 9,
group_id: groupsMap.penetration,
},
@@ -74,24 +78,21 @@ function getTags(groupsMap) {
slug: 'amateur',
alias_for: null,
},
- {
- name: 'american',
- slug: 'american',
- alias_for: null,
- group_id: groupsMap.ethnicity,
- },
{
name: 'anal creampie',
slug: 'anal-creampie',
+ priority: 7,
alias_for: null,
description: 'Ejaculating into the asshole.',
+ group_id: groupsMap.finish,
},
{
name: 'anal',
slug: 'anal',
- description: 'Penetrating the asshole with a (real) dick.',
+ description: 'Taking a cock in the asshole.',
priority: 9,
alias_for: null,
+ group_id: groupsMap.penetration,
},
{
name: 'ass fingering',
@@ -102,6 +103,7 @@ function getTags(groupsMap) {
{
name: 'anal fisting',
slug: 'anal-fisting',
+ description: 'Shoving an entire hand into the asshole.',
alias_for: null,
},
{
@@ -112,11 +114,13 @@ function getTags(groupsMap) {
{
name: 'anal toys',
slug: 'anal-toys',
+ description: 'Stuffing a toy, such as a dildo or buttplug, into the ass',
alias_for: null,
},
{
name: 'asian',
slug: 'asian',
+ priority: 7,
alias_for: null,
group_id: groupsMap.ethnicity,
},
@@ -129,7 +133,8 @@ function getTags(groupsMap) {
{
name: 'ass to mouth',
slug: 'ass-to-mouth',
- priority: 8,
+ priority: 6,
+ description: 'Sucking off a cock right after anal, giving your own or someone else`s asshole a second hand taste.',
alias_for: null,
},
{
@@ -157,6 +162,7 @@ function getTags(groupsMap) {
{
name: 'BDSM',
slug: 'bdsm',
+ priority: 8,
alias_for: null,
},
{
@@ -203,11 +209,14 @@ function getTags(groupsMap) {
{
name: 'blowjob',
slug: 'blowjob',
+ priority: 7,
alias_for: null,
},
{
name: 'blowbang',
slug: 'blowbang',
+ priority: 9,
+ description: 'Pleasuring a gang of three or more cocks by sucking and jerking off as many cocks as they can, often getting [facefucked](/tag/facefuck), groped and rubbed out, and followed by a [bukkake](/tag/bukkake). If they are getting fucked, it is a [gangbang](/tag/gangbang).',
alias_for: null,
group_id: groupsMap.group,
},
@@ -225,7 +234,10 @@ function getTags(groupsMap) {
{
name: 'bukkake',
slug: 'bukkake',
+ priority: 8,
+ description: 'Getting ejaculated on the face by a group of three or more men, often following a [blowbang](/tag/blowbang) or [gangbang](/tag/gangbang).',
alias_for: null,
+ group_id: groupsMap.finish,
},
{
name: 'cheerleader',
@@ -256,7 +268,10 @@ function getTags(groupsMap) {
{
name: 'creampie',
slug: 'creampie',
+ priority: 8,
+ description: 'Ejaculalating into her pussy, often shown visibly dripping out afterwards.',
alias_for: null,
+ group_id: groupsMap.finish,
},
{
name: 'cum licking',
@@ -284,13 +299,25 @@ function getTags(groupsMap) {
alias_for: null,
},
{
- name: 'double anal penetration',
+ name: 'double anal',
slug: 'double-anal',
+ description: 'Two cocks in the ass at the same time. If there\'s a third cock in her pussy, it is [double anal TP](/tag/da-tp).',
+ priority: 8,
alias_for: null,
+ group_id: groupsMap.penetration,
+ },
+ {
+ name: 'triple anal',
+ slug: 'triple-anal',
+ description: 'Getting fucked in the ass by not one, two but *three* cocks at the same time.',
+ priority: 7,
+ alias_for: null,
+ group_id: groupsMap.penetration,
},
{
name: 'deepthroat',
slug: 'deepthroat',
+ priority: 7,
alias_for: null,
},
{
@@ -298,6 +325,8 @@ function getTags(groupsMap) {
slug: 'double-penetration',
priority: 9,
alias_for: null,
+ description: 'Fucking two cocks at once, with one in her ass, and one in her pussy. If she has another cock in her mouth, she is [airtight](/tag/airtight).',
+ group_id: groupsMap.penetration,
},
{
name: 'dungeon',
@@ -305,9 +334,12 @@ function getTags(groupsMap) {
alias_for: null,
},
{
- name: 'double vaginal penetration',
+ name: 'double vaginal',
slug: 'double-vaginal',
+ description: 'Fucking her pussy with two cocks at the same time. If there\'s a third cock in her asshole, it is [double vaginal TP](/tag/dv-tp).',
+ priority: 8,
alias_for: null,
+ group_id: groupsMap.penetration,
},
{
name: 'double blowjob',
@@ -328,6 +360,7 @@ function getTags(groupsMap) {
{
name: 'ebony',
slug: 'ebony',
+ priority: 7,
alias_for: null,
group_id: groupsMap.ethnicity,
},
@@ -341,15 +374,10 @@ function getTags(groupsMap) {
slug: 'enhanced-boobs',
alias_for: null,
},
- {
- name: 'European',
- slug: 'european',
- alias_for: null,
- group_id: groupsMap.ethnicity,
- },
{
name: 'facefuck',
slug: 'facefuck',
+ priority: 9,
alias_for: null,
group_id: groupsMap.position,
},
@@ -363,6 +391,7 @@ function getTags(groupsMap) {
name: 'facial',
slug: 'facial',
alias_for: null,
+ group_id: groupsMap.finish,
},
{
name: 'feet',
@@ -385,8 +414,10 @@ function getTags(groupsMap) {
alias_for: null,
},
{
- name: 'FMF threesome',
- slug: 'fmf',
+ name: 'MFF threesome',
+ slug: 'mff',
+ priority: 9,
+ description: 'A threesome with two women and one guy, in which the women have sex with eachother.',
alias_for: null,
group_id: groupsMap.group,
},
@@ -398,10 +429,19 @@ function getTags(groupsMap) {
{
name: 'gangbang',
slug: 'gangbang',
+ description: 'A group of three or more guys fucking a woman, at least two at the same time, often but not necessarily involving a [blowbang](/tag/blowbang), [double penetration](/tag/airtight) and [airtight](/tag/airtight). If she only gets fucked by one guy at a time, it might be considered a [trainbang](/tag/trainbang) instead. In a reverse gangbang, multiple women fuck one man.',
alias_for: null,
priority: 9,
group_id: groupsMap.group,
},
+ {
+ name: 'trainbang',
+ slug: 'trainbang',
+ description: 'A group of three or more guys fucking a woman as in a [gangbang](/tag/gangbang), but one after the other, and never at the same time.',
+ priority: 7,
+ alias_for: null,
+ group_id: groupsMap.group,
+ },
{
name: 'gapes',
slug: 'gapes',
@@ -435,12 +475,6 @@ function getTags(groupsMap) {
alias_for: null,
group_id: groupsMap.clothing,
},
- {
- name: 'hungarian',
- slug: 'hungarian',
- alias_for: null,
- group_id: groupsMap.ethnicity,
- },
{
name: 'humiliation',
slug: 'humiliation',
@@ -456,6 +490,7 @@ function getTags(groupsMap) {
slug: 'interracial',
priority: 9,
alias_for: null,
+ group_id: groupsMap.ethnicity,
},
{
name: 'kissing',
@@ -470,7 +505,9 @@ function getTags(groupsMap) {
{
name: 'Latina',
slug: 'latina',
+ priority: 7,
alias_for: null,
+ group_id: groupsMap.ethnicity,
},
{
name: 'leather',
@@ -480,6 +517,7 @@ function getTags(groupsMap) {
{
name: 'lesbian',
slug: 'lesbian',
+ priority: 9,
alias_for: null,
},
{
@@ -513,6 +551,8 @@ function getTags(groupsMap) {
{
name: 'MFM threesome',
slug: 'mfm',
+ priority: 9,
+ description: 'Two men fucking one woman, but not eachother. Typically involves a \'spitroast\', where one guy gets a blowjob and the other fucks her pussy.',
alias_for: null,
group_id: groupsMap.group,
},
@@ -542,11 +582,15 @@ function getTags(groupsMap) {
{
name: 'oral creampie',
slug: 'oral-creampie',
+ priority: 7,
alias_for: null,
+ group_id: groupsMap.finish,
},
{
name: 'orgy',
slug: 'orgy',
+ priority: 9,
+ description: 'A group of (at least four) people having sex with eachother. If only one person is getting fucked, it is probably a [gangbang](/tag/gangbang).',
alias_for: null,
group_id: groupsMap.group,
},
@@ -610,14 +654,9 @@ function getTags(groupsMap) {
{
name: 'rough',
slug: 'rough',
+ priority: 7,
alias_for: null,
},
- {
- name: 'russian',
- slug: 'russian',
- alias_for: null,
- group_id: groupsMap.ethnicity,
- },
{
name: 'saliva',
slug: 'saliva',
@@ -729,6 +768,7 @@ function getTags(groupsMap) {
name: 'swallowing',
slug: 'swallowing',
alias_for: null,
+ group_id: groupsMap.finish,
},
{
name: 'tattoo',
@@ -764,9 +804,27 @@ function getTags(groupsMap) {
priority: 10,
alias_for: null,
},
+ {
+ name: 'double anal TP',
+ slug: 'da-tp',
+ priority: 7,
+ description: 'Triple penetration with two cocks in the ass, and one in the pussy. Also see [double vaginal TP](/tag/dv-tp).',
+ group_id: groupsMap.penetration,
+ alias_for: null,
+ },
+ {
+ name: 'double vaginal TP',
+ slug: 'dv-tp',
+ priority: 7,
+ description: 'Triple penetration with two cocks in the pussy, and one in the ass. Also see [double anal TP](/tag/da-tp).',
+ group_id: groupsMap.penetration,
+ alias_for: null,
+ },
{
name: 'triple penetration',
slug: 'triple-penetration',
+ priority: 7,
+ description: 'Three cocks fucking her from behind at the same time. This can be either [double anal TP](/tag/da-tp), or [double vaginal TP](/tag/dv-tp).',
alias_for: null,
},
{
@@ -795,8 +853,9 @@ function getTags(groupsMap) {
alias_for: null,
},
{
- name: 'white',
- slug: 'white',
+ name: 'caucasian',
+ slug: 'caucasian',
+ priority: 7,
alias_for: null,
group_id: groupsMap.ethnicity,
},
@@ -878,11 +937,11 @@ function getTagAliases(tagsMap) {
},
{
name: 'fmf',
- alias_for: tagsMap.fmf,
+ alias_for: tagsMap.mff,
},
{
name: 'ffm',
- alias_for: tagsMap.fmf,
+ alias_for: tagsMap.mff,
},
{
name: 'bgb',
@@ -1112,6 +1171,10 @@ function getTagAliases(tagsMap) {
name: 'double anal penetration (dap)',
alias_for: tagsMap['double-anal'],
},
+ {
+ name: 'tap',
+ alias_for: tagsMap['triple-anal'],
+ },
{
name: 'dpp',
alias_for: tagsMap['double-vaginal'],
@@ -1436,6 +1499,10 @@ function getTagAliases(tagsMap) {
name: 'whipping',
alias_for: tagsMap['corporal-punishment'],
},
+ {
+ name: 'white',
+ alias_for: tagsMap.caucasian,
+ },
{
name: 'work',
alias_for: tagsMap.office,
diff --git a/seeds/04_media.js b/seeds/04_media.js
new file mode 100644
index 00000000..e869cc74
--- /dev/null
+++ b/seeds/04_media.js
@@ -0,0 +1,220 @@
+const upsert = require('../src/utils/upsert');
+
+function getMedia(tagsMap) {
+ return [
+ {
+ path: 'tags/airtight/poster.jpeg',
+ target_id: tagsMap.airtight,
+ role: 'poster',
+ comment: 'Jynx Maze in "Pump My Ass Full of Cum 3" for Jules Jordan',
+ },
+ {
+ path: 'tags/airtight/2.jpeg',
+ target_id: tagsMap.airtight,
+ comment: 'Dakota Skye in "Dakota Goes Nuts" for ArchAngel',
+ },
+ {
+ path: 'tags/airtight/1.jpeg',
+ target_id: tagsMap.airtight,
+ comment: 'Chloe Amour in "DP Masters 4" for Jules Jordan',
+ },
+ {
+ path: 'tags/airtight/0.jpeg',
+ domain: 'tags',
+ target_id: tagsMap.airtight,
+ comment: 'Sheena Shaw in "Ass Worship 14" for Jules Jordan',
+ },
+ {
+ path: 'tags/anal/poster.jpeg',
+ target_id: tagsMap.anal,
+ role: 'poster',
+ comment: '',
+ },
+ {
+ path: 'tags/double-penetration/poster.jpeg',
+ target_id: tagsMap['double-penetration'],
+ role: 'poster',
+ comment: '',
+ },
+ {
+ path: 'tags/double-anal/poster.jpeg',
+ target_id: tagsMap['double-anal'],
+ role: 'poster',
+ comment: '',
+ },
+ {
+ path: 'tags/double-vaginal/poster.jpeg',
+ target_id: tagsMap['double-vaginal'],
+ role: 'poster',
+ comment: '',
+ },
+ {
+ path: 'tags/da-tp/poster.jpeg',
+ target_id: tagsMap['da-tp'],
+ role: 'poster',
+ comment: 'Ninel Mojado aka Mira Cuckold in GIO063 for LegalPorno',
+ },
+ {
+ path: 'tags/da-tp/1.jpeg',
+ target_id: tagsMap['da-tp'],
+ role: 'photo',
+ comment: 'Francys Belle in SZ1702 for LegalPorno',
+ },
+ {
+ path: 'tags/da-tp/2.jpeg',
+ target_id: tagsMap['da-tp'],
+ role: 'photo',
+ comment: 'Angel Smalls in GIO408 for LegalPorno',
+ },
+ {
+ path: 'tags/dv-tp/poster.jpeg',
+ target_id: tagsMap['dv-tp'],
+ role: 'poster',
+ comment: 'Juelz Ventura in "Gangbanged 5" for Elegant Angel',
+ },
+ {
+ path: 'tags/triple-anal/poster.jpeg',
+ target_id: tagsMap['triple-anal'],
+ role: 'poster',
+ comment: 'Kristy Black in SZ1986 for LegalPorno',
+ },
+ {
+ path: 'tags/triple-anal/1.jpeg',
+ target_id: tagsMap['triple-anal'],
+ role: 'photo',
+ comment: 'Natasha Teen in SZ2098 for LegalPorno',
+ },
+ {
+ path: 'tags/triple-anal/2.jpeg',
+ target_id: tagsMap['triple-anal'],
+ role: 'photo',
+ comment: 'Kira Thorn in GIO1018 for LegalPorno"',
+ },
+ {
+ path: 'tags/blowbang/poster.jpeg',
+ target_id: tagsMap.blowbang,
+ role: 'poster',
+ comment: '',
+ },
+ {
+ path: 'tags/gangbang/poster.jpeg',
+ target_id: tagsMap.gangbang,
+ role: 'poster',
+ comment: '',
+ },
+ {
+ path: 'tags/gangbang/1.jpeg',
+ target_id: tagsMap.gangbang,
+ role: 'photo',
+ comment: 'Ginger Lynn in "Gangbang Mystique", a photoset shot by Suze Randall for Puritan No. 10, 1984. This photo pushed the boundaries of pornography at the time, as depicting a woman \'fully occupied\' was unheard of.',
+ },
+ {
+ path: 'tags/mff/poster.jpeg',
+ target_id: tagsMap.mff,
+ role: 'poster',
+ comment: '',
+ },
+ {
+ path: 'tags/mfm/poster.jpeg',
+ target_id: tagsMap.mfm,
+ role: 'poster',
+ comment: '',
+ },
+ {
+ path: 'tags/orgy/poster.jpeg',
+ target_id: tagsMap.orgy,
+ role: 'poster',
+ comment: '',
+ },
+ {
+ path: 'tags/asian/poster.jpeg',
+ target_id: tagsMap.asian,
+ role: 'poster',
+ comment: '',
+ },
+ {
+ path: 'tags/caucasian/poster.jpeg',
+ target_id: tagsMap.caucasian,
+ role: 'poster',
+ comment: '',
+ },
+ {
+ path: 'tags/ebony/poster.jpeg',
+ target_id: tagsMap.ebony,
+ role: 'poster',
+ comment: '',
+ },
+ {
+ path: 'tags/latina/poster.jpeg',
+ target_id: tagsMap.latina,
+ role: 'poster',
+ comment: '',
+ },
+ {
+ path: 'tags/interracial/poster.jpeg',
+ target_id: tagsMap.interracial,
+ role: 'poster',
+ comment: '',
+ },
+ {
+ path: 'tags/facial/poster.jpeg',
+ target_id: tagsMap.facial,
+ role: 'poster',
+ comment: '',
+ },
+ {
+ path: 'tags/bukkake/poster.jpeg',
+ target_id: tagsMap.bukkake,
+ role: 'poster',
+ comment: '',
+ },
+ {
+ path: 'tags/swallowing/poster.jpeg',
+ target_id: tagsMap.swallowing,
+ role: 'poster',
+ comment: '',
+ },
+ {
+ path: 'tags/creampie/poster.jpeg',
+ target_id: tagsMap.creampie,
+ role: 'poster',
+ comment: '',
+ },
+ {
+ path: 'tags/anal-creampie/poster.jpeg',
+ target_id: tagsMap['anal-creampie'],
+ role: 'poster',
+ comment: '',
+ },
+ {
+ path: 'tags/oral-creampie/poster.jpeg',
+ target_id: tagsMap['oral-creampie'],
+ role: 'poster',
+ comment: '',
+ },
+ ]
+ .map((file, index) => ({
+ ...file,
+ thumbnail: file.thumbnail || file.path.replace('.jpeg', '_thumb.jpeg'),
+ mime: 'image/jpeg',
+ index,
+ domain: file.domain || 'tags',
+ role: file.role || 'photo',
+ }));
+}
+
+/* eslint-disable max-len */
+exports.seed = knex => Promise.resolve()
+ .then(async () => {
+ const [duplicates, tags] = await Promise.all([
+ knex('media').where('domain', 'tags'),
+ knex('tags').where('alias_for', null),
+ ]);
+
+ const duplicatesByPath = duplicates.reduce((acc, file) => ({ ...acc, [file.path]: file }), {});
+ const tagsMap = tags.reduce((acc, { id, slug }) => ({ ...acc, [slug]: id }), {});
+
+ const media = getMedia(tagsMap);
+
+ return upsert('media', media, duplicatesByPath, 'path', knex);
+ });
diff --git a/seeds/04_countries.js b/seeds/05_countries.js
similarity index 100%
rename from seeds/04_countries.js
rename to seeds/05_countries.js
diff --git a/src/actors.js b/src/actors.js
index ee140301..7de45948 100644
--- a/src/actors.js
+++ b/src/actors.js
@@ -375,28 +375,31 @@ async function scrapeBasicActors() {
return scrapeActors(basicActors.map(actor => actor.name));
}
-async function associateActors(release, releaseId) {
- const actorEntries = await knex('actors').whereIn('name', release.actors);
-
- const newActors = release.actors
- .map(actorName => actorName.trim())
- .filter(actorName => !actorEntries.some(actor => actor.name === actorName));
-
- const [newActorEntries, associatedActors] = await Promise.all([
- Promise.all(newActors.map(async actorName => storeActor({ name: actorName }))),
- knex('actors_associated').where('release_id', releaseId),
+async function associateActors(mappedActors, releases) {
+ const [existingActorEntries, existingAssociationEntries] = await Promise.all([
+ knex('actors').whereIn('name', Object.keys(mappedActors)),
+ knex('actors_associated').whereIn('release_id', releases.map(release => release.id)),
]);
- const newlyAssociatedActors = actorEntries
- .concat(newActorEntries)
- .filter(actorEntry => !associatedActors.some(actor => actorEntry.id === actor.id))
- .map(actor => ({
- release_id: releaseId,
- actor_id: actor.id,
- }));
+ const associations = await Promise.map(Object.entries(mappedActors), async ([actorName, releaseIds]) => {
+ const actorEntry = existingActorEntries.find(actor => actor.name === actorName)
+ || await storeActor({ name: actorName });
- await knex('actors_associated')
- .insert(newlyAssociatedActors);
+ return releaseIds
+ .map(releaseId => ({
+ release_id: releaseId,
+ actor_id: actorEntry.id,
+ }))
+ .filter(association => !existingAssociationEntries
+ // remove associations already in database
+ .some(associationEntry => associationEntry.actor_id === association.actor_id
+ && associationEntry.release_id === association.release_id));
+ });
+
+ await Promise.all([
+ knex('actors_associated').insert(associations.flat()),
+ scrapeBasicActors(),
+ ]);
}
module.exports = {
diff --git a/src/media.js b/src/media.js
index b10aa65a..becf7540 100644
--- a/src/media.js
+++ b/src/media.js
@@ -29,7 +29,7 @@ async function getThumbnail(buffer) {
}
async function createReleaseMediaDirectory(release, releaseId) {
- if (release.poster || (release.photos && release.photos.length)) {
+ if (release.poster || (release.photos && release.photos.length) || release.trailer) {
await fs.mkdir(
path.join(config.media.path, 'releases', release.site.network.slug, release.site.slug, releaseId.toString()),
{ recursive: true },
@@ -133,7 +133,7 @@ async function storePhotos(release, releaseId) {
return null;
}
}, {
- concurrency: 2,
+ concurrency: 10,
});
await knex('media')
@@ -225,7 +225,7 @@ async function storeAvatars(profile, actor) {
return null;
}
}, {
- concurrency: 2,
+ concurrency: 10,
});
const avatars = files.filter(file => file);
diff --git a/src/releases.js b/src/releases.js
index 09253705..70ba3055 100644
--- a/src/releases.js
+++ b/src/releases.js
@@ -197,7 +197,6 @@ async function storeReleaseAssets(release, releaseId) {
await createReleaseMediaDirectory(release, releaseId);
await Promise.all([
- associateActors(release, releaseId),
associateTags(release, releaseId),
storePhotos(release, releaseId),
storePoster(release, releaseId),
@@ -222,36 +221,59 @@ async function storeRelease(release) {
})
.returning('*');
- await storeReleaseAssets(release, existingRelease.id);
+ // await storeReleaseAssets(release, existingRelease.id);
console.log(`Updated release "${release.title}" (${existingRelease.id}, ${release.site.name})`);
- return updatedRelease || existingRelease;
+ return updatedRelease ? updatedRelease.id : existingRelease.id;
}
const [releaseEntry] = await knex('releases')
.insert(curatedRelease)
.returning('*');
- await storeReleaseAssets(release, releaseEntry.id);
+ // await storeReleaseAssets(release, releaseEntry.id);
console.log(`Stored release "${release.title}" (${releaseEntry.id}, ${release.site.name})`);
return releaseEntry.id;
}
async function storeReleases(releases) {
- return Promise.map(releases, async (release) => {
+ const storedReleases = await Promise.map(releases, async (release) => {
try {
const releaseId = await storeRelease(release);
- return releaseId;
+ return {
+ id: releaseId,
+ ...release,
+ };
} catch (error) {
console.error(error);
return null;
}
}, {
- concurrency: 2,
+ concurrency: 10,
});
+
+ const actors = storedReleases.reduce((acc, release) => {
+ release.actors.forEach((actor) => {
+ const trimmedActor = actor.trim();
+
+ if (acc[trimmedActor]) {
+ acc[trimmedActor] = acc[trimmedActor].concat(release.id);
+ return;
+ }
+
+ acc[trimmedActor] = [release.id];
+ });
+
+ return acc;
+ }, {});
+
+ await Promise.all([
+ associateActors(actors, storedReleases),
+ Promise.all(storedReleases.map(async release => storeReleaseAssets(release, release.id))),
+ ]);
}
module.exports = {
diff --git a/src/scrape-release.js b/src/scrape-release.js
index 2b4de7ae..e33e471c 100644
--- a/src/scrape-release.js
+++ b/src/scrape-release.js
@@ -7,7 +7,6 @@ const scrapers = require('./scrapers/scrapers');
const { storeReleases } = require('./releases');
const { findSiteByUrl } = require('./sites');
const { findNetworkByUrl } = require('./networks');
-const { scrapeBasicActors } = require('./actors');
async function findSite(url, release) {
const site = (release && release.site) || await findSiteByUrl(url);
@@ -50,7 +49,6 @@ async function scrapeRelease(url, release, deep = false) {
if (!deep && argv.save) {
// don't store release when called by site scraper
const [releaseId] = await storeReleases([scene]);
- await scrapeBasicActors();
console.log(`http://${config.web.host}:${config.web.port}/scene/${releaseId}`);
}
diff --git a/src/scrape-sites.js b/src/scrape-sites.js
index 67cc9dbc..4aaeaf32 100644
--- a/src/scrape-sites.js
+++ b/src/scrape-sites.js
@@ -9,7 +9,6 @@ const { fetchIncludedSites } = require('./sites');
const scrapers = require('./scrapers/scrapers');
const scrapeRelease = require('./scrape-release');
const { storeReleases } = require('./releases');
-const { scrapeBasicActors } = require('./actors');
function getAfterDate() {
return moment
@@ -103,40 +102,39 @@ async function scrapeSiteReleases(scraper, site) {
}
async function scrapeReleases() {
- const sites = await fetchIncludedSites();
+ const networks = await fetchIncludedSites();
- console.log(`Found ${sites.length} sites in database`);
-
- await Promise.map(sites, async (site) => {
+ const scrapedReleases = await Promise.map(networks, async network => Promise.map(network.sites, async (site) => {
const scraper = scrapers.releases[site.slug] || scrapers.releases[site.network.slug];
if (!scraper) {
console.warn(`No scraper found for '${site.name}' (${site.slug})`);
- return;
+ return [];
}
try {
- const siteReleases = await scrapeSiteReleases(scraper, site);
- const siteActors = siteReleases.reduce((acc, release) => [...acc, ...release.actors], []);
-
- console.log(siteActors);
-
- if (argv.save) {
- await storeReleases(siteReleases);
- }
+ return await scrapeSiteReleases(scraper, site);
} catch (error) {
if (argv.debug) {
console.error(`${site.id}: Failed to scrape releases`, error);
- return;
}
console.warn(`${site.id}: Failed to scrape releases`);
+
+ return [];
}
}, {
+ // 2 network sites at a time
concurrency: 2,
+ }),
+ {
+ // 5 networks at a time
+ concurrency: 5,
});
- await scrapeBasicActors();
+ if (argv.save) {
+ await storeReleases(scrapedReleases.flat(2));
+ }
}
module.exports = scrapeReleases;
diff --git a/src/sites.js b/src/sites.js
index b2c9218d..03884fdc 100644
--- a/src/sites.js
+++ b/src/sites.js
@@ -72,6 +72,25 @@ async function findSiteByUrl(url) {
return null;
}
+function sitesByNetwork(sites) {
+ const networks = sites.reduce((acc, site) => {
+ if (acc[site.network.slug]) {
+ acc[site.network.slug].sites = acc[site.network.slug].sites.concat(site);
+
+ return acc;
+ }
+
+ acc[site.network.slug] = {
+ ...site.network,
+ sites: [site],
+ };
+
+ return acc;
+ }, {});
+
+ return Object.values(networks);
+}
+
async function fetchSitesFromArgv() {
const rawSites = await knex('sites')
.select('sites.*', 'networks.name as network_name', 'networks.slug as network_slug', 'networks.parameters as network_parameters')
@@ -79,7 +98,10 @@ async function fetchSitesFromArgv() {
.orWhereIn('networks.slug', argv.networks || [])
.leftJoin('networks', 'sites.network_id', 'networks.id');
- return curateSites(rawSites, true);
+ const curatedSites = await curateSites(rawSites, true);
+ console.log(`Found ${curatedSites.length} sites in database`);
+
+ return sitesByNetwork(curatedSites);
}
async function fetchSitesFromConfig() {
@@ -94,7 +116,10 @@ async function fetchSitesFromConfig() {
.orWhereIn('network_id', networkIds)
.leftJoin('networks', 'sites.network_id', 'networks.id');
- return curateSites(rawSites, true);
+ const curatedSites = await curateSites(rawSites, true);
+ console.log(`Found ${curatedSites.length} sites in database`);
+
+ return sitesByNetwork(curatedSites);
}
async function fetchIncludedSites() {
diff --git a/src/tags.js b/src/tags.js
index 3f6ad1c4..ce68ef93 100644
--- a/src/tags.js
+++ b/src/tags.js
@@ -4,13 +4,21 @@ const knex = require('./knex');
const whereOr = require('./utils/where-or');
async function curateTag(tag) {
- const aliases = await knex('tags').where({ alias_for: tag.id });
+ const [aliases, media] = await Promise.all([
+ knex('tags').where({ alias_for: tag.id }),
+ knex('media')
+ .where('domain', 'tags')
+ .andWhere('target_id', tag.id)
+ .orderBy('index'),
+ ]);
return {
id: tag.id,
name: tag.name,
slug: tag.slug,
description: tag.description,
+ poster: media.find(photo => photo.role === 'poster'),
+ photos: media.filter(photo => photo.role === 'photo'),
group: {
id: tag.group_id,
name: tag.group_name,
@@ -31,15 +39,20 @@ async function associateTags(release, releaseId) {
return;
}
- await knex('tags_associated').insert(release.tags.map(tagId => ({
- tag_id: tagId,
- release_id: releaseId,
- })));
+ try {
+ await knex('tags_associated').insert(release.tags.map(tagId => ({
+ tag_id: tagId,
+ release_id: releaseId,
+ })));
+ } catch (error) {
+ console.log(release, error);
+ }
}
-async function fetchTags(queryObject, limit = 100) {
+async function fetchTags(queryObject, groupsQueryObject, limit = 100) {
const tags = await knex('tags')
.where(builder => whereOr(queryObject, 'tags', builder))
+ .orWhere(builder => whereOr(groupsQueryObject, 'tags_groups', builder))
.andWhere({ 'tags.alias_for': null })
.select(
'tags.*',
diff --git a/src/utils/escape-html.js b/src/utils/escape-html.js
new file mode 100644
index 00000000..845f3864
--- /dev/null
+++ b/src/utils/escape-html.js
@@ -0,0 +1,10 @@
+function escapeHtml(text) {
+ return text
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+}
+
+module.exports = escapeHtml;
diff --git a/src/web/tags.js b/src/web/tags.js
index 405ea7c0..315bc9ce 100644
--- a/src/web/tags.js
+++ b/src/web/tags.js
@@ -10,7 +10,7 @@ async function fetchTagsApi(req, res) {
const tags = await fetchTags({
id: tagId,
slug: tagSlug,
- }, req.query.limit);
+ }, null, req.query.limit);
if (tags.length > 0) {
res.send(tags[0]);
@@ -21,9 +21,16 @@ async function fetchTagsApi(req, res) {
return;
}
- const tags = await fetchTags({
- priority: req.query.priority.split(','),
- }, req.query.limit);
+ const query = {};
+ const groupsQuery = {};
+
+ if (req.query.priority) query.priority = req.query.priority.split(',');
+ if (req.query.slug) query.slug = req.query.slug.split(',');
+ if (req.query.group) {
+ groupsQuery.slug = req.query.group.split(',');
+ }
+
+ const tags = await fetchTags(query, groupsQuery, req.query.limit);
res.send(tags);
}