Refactored 21sextury scraper.
3
.babelrc
|
@ -3,5 +3,6 @@
|
|||
[
|
||||
"@babel/preset-env"
|
||||
]
|
||||
]
|
||||
],
|
||||
"plugins": ["@babel/plugin-proposal-optional-chaining"]
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
"root": true,
|
||||
"extends": ["airbnb-base", "plugin:vue/recommended"],
|
||||
"parserOptions": {
|
||||
"parser": "babel-eslint",
|
||||
"ecmaVersion": 2019,
|
||||
"sourceType": "module"
|
||||
},
|
||||
|
|
|
@ -293,6 +293,7 @@ export default {
|
|||
|
||||
.logo {
|
||||
display: inline-block;
|
||||
filter: drop-shadow(0 0 1px $shadow);
|
||||
}
|
||||
|
||||
.logo-site {
|
||||
|
@ -300,6 +301,7 @@ export default {
|
|||
max-width: 15rem;
|
||||
object-fit: contain;
|
||||
object-position: 100% 50%;
|
||||
filter: drop-shadow(0 0 1px $shadow);
|
||||
}
|
||||
|
||||
.logo-network {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<div class="sidebar">
|
||||
<a
|
||||
v-if="tag.poster"
|
||||
:href="`/media/${tag.poster.path}`"
|
||||
:href="`/img/${tag.poster.path}`"
|
||||
:title="tag.poster.comment"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
|
|
|
@ -98,6 +98,8 @@ export default {
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import 'theme';
|
||||
|
||||
.tags {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
|
|
@ -30,8 +30,8 @@ export default {
|
|||
@import 'theme';
|
||||
|
||||
.tile {
|
||||
color: $text-contrast;
|
||||
background: $profile;
|
||||
color: $text;
|
||||
background: $background;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
|
|
@ -86,5 +86,6 @@ module.exports = {
|
|||
media: {
|
||||
path: './',
|
||||
thumbnailSize: 320, // width for 16:9 will be exactly 576px
|
||||
limit: 25, // max number of photos per release
|
||||
},
|
||||
};
|
||||
|
|
76
package.json
|
@ -5,10 +5,12 @@
|
|||
"main": "src/app.js",
|
||||
"scripts": {
|
||||
"postinstall": "npm run migrate && npm run seed",
|
||||
"start": "node src/app.js",
|
||||
"start": "node dist/init.js",
|
||||
"webpack": "webpack --env=production --mode=production",
|
||||
"webpack-dev": "webpack --env=development --mode=development",
|
||||
"webpack-watch": "webpack --progress --colors --watch --env=development --mode=development",
|
||||
"babel": "babel src -d dist",
|
||||
"babel-watch": "babel src -w -d dist",
|
||||
"eslint": "eslint src/",
|
||||
"eslint-watch": "esw --watch src/",
|
||||
"knex": "knex",
|
||||
|
@ -32,68 +34,70 @@
|
|||
"author": "Niels Simenon",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.2.3",
|
||||
"@babel/core": "^7.4.5",
|
||||
"@babel/preset-env": "^7.4.5",
|
||||
"autoprefixer": "^9.5.1",
|
||||
"@babel/cli": "^7.7.5",
|
||||
"@babel/core": "^7.7.5",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.7.5",
|
||||
"@babel/preset-env": "^7.7.6",
|
||||
"autoprefixer": "^9.7.3",
|
||||
"babel-cli": "^6.26.0",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"babel-loader": "^8.0.6",
|
||||
"babel-preset-airbnb": "^3.2.0",
|
||||
"babel-preset-airbnb": "^3.3.2",
|
||||
"babel-register": "^6.26.0",
|
||||
"css-loader": "^2.1.1",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-config-airbnb": "^17.1.0",
|
||||
"eslint-config-airbnb-base": "^13.1.0",
|
||||
"eslint-loader": "^2.1.2",
|
||||
"eslint-plugin-import": "^2.17.3",
|
||||
"eslint-plugin-jsx-a11y": "^6.2.1",
|
||||
"eslint-plugin-react": "^7.13.0",
|
||||
"eslint-plugin-vue": "^5.2.2",
|
||||
"eslint-config-airbnb": "^17.1.1",
|
||||
"eslint-config-airbnb-base": "^13.2.0",
|
||||
"eslint-loader": "^2.2.1",
|
||||
"eslint-plugin-import": "^2.18.2",
|
||||
"eslint-plugin-jsx-a11y": "^6.2.3",
|
||||
"eslint-plugin-react": "^7.17.0",
|
||||
"eslint-plugin-vue": "^6.0.1",
|
||||
"eslint-watch": "^4.0.2",
|
||||
"mini-css-extract-plugin": "^0.7.0",
|
||||
"node-sass": "^4.12.0",
|
||||
"node-sass": "^4.13.0",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"raw-loader": "^2.0.0",
|
||||
"sass-loader": "^7.1.0",
|
||||
"sass-loader": "^7.3.1",
|
||||
"style-loader": "^0.23.1",
|
||||
"vue-loader": "^15.7.0",
|
||||
"vue-loader": "^15.7.2",
|
||||
"vue-template-compiler": "^2.6.10",
|
||||
"webpack": "^4.32.2",
|
||||
"webpack-cli": "^3.3.2"
|
||||
"webpack": "^4.41.2",
|
||||
"webpack-cli": "^3.3.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"bhttp": "^1.2.4",
|
||||
"blake2": "^4.0.0",
|
||||
"bluebird": "^3.5.4",
|
||||
"bluebird": "^3.7.2",
|
||||
"body-parser": "^1.19.0",
|
||||
"cheerio": "^1.0.0-rc.2",
|
||||
"cheerio": "^1.0.0-rc.3",
|
||||
"cli-confirm": "^1.0.1",
|
||||
"config": "^3.0.1",
|
||||
"dayjs": "^1.8.14",
|
||||
"express": "^4.16.4",
|
||||
"config": "^3.2.4",
|
||||
"dayjs": "^1.8.17",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^3.0.3",
|
||||
"express-react-views": "^0.11.0",
|
||||
"fs-extra": "^7.0.1",
|
||||
"jsdom": "^15.2.0",
|
||||
"knex": "^0.16.3",
|
||||
"knex-migrate": "^1.7.1",
|
||||
"jsdom": "^15.2.1",
|
||||
"knex": "^0.16.5",
|
||||
"knex-migrate": "^1.7.4",
|
||||
"mime": "^2.4.4",
|
||||
"moment": "^2.24.0",
|
||||
"opn": "^5.4.0",
|
||||
"pg": "^7.9.0",
|
||||
"opn": "^5.5.0",
|
||||
"pg": "^7.14.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"sharp": "^0.23.2",
|
||||
"react": "^16.12.0",
|
||||
"react-dom": "^16.12.0",
|
||||
"sharp": "^0.23.4",
|
||||
"showdown": "^1.9.1",
|
||||
"tough-cookie": "^3.0.1",
|
||||
"tty-table": "^2.7.0",
|
||||
"tty-table": "^2.8.3",
|
||||
"url-pattern": "^1.0.3",
|
||||
"v-tooltip": "^2.0.2",
|
||||
"vue": "^2.6.10",
|
||||
"vue-router": "^3.0.6",
|
||||
"vuex": "^3.1.1",
|
||||
"yargs": "^13.2.2"
|
||||
"vue-router": "^3.1.3",
|
||||
"vuex": "^3.1.2",
|
||||
"yargs": "^13.3.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,8 +42,10 @@
|
|||
/* $primary: #ff886c; */
|
||||
.filter-bar[data-v-6db17c96] {
|
||||
background: #fff;
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
-webkit-box-pack: justify;
|
||||
justify-content: space-between;
|
||||
padding: .5rem 1rem;
|
||||
z-index: 1;
|
||||
font-size: 0;
|
||||
|
@ -93,8 +95,11 @@
|
|||
/* $primary: #ff886c; */
|
||||
.tile[data-v-3abcf101] {
|
||||
background: #fff;
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
padding: 0 0 .5rem 0;
|
||||
overflow: hidden;
|
||||
|
@ -108,9 +113,12 @@
|
|||
.thumbnail[data-v-3abcf101] {
|
||||
width: 100%;
|
||||
height: 12rem;
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: center;
|
||||
justify-content: center;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
-o-object-fit: cover;
|
||||
object-fit: cover;
|
||||
background-position: center;
|
||||
|
@ -120,18 +128,24 @@
|
|||
text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
.row[data-v-3abcf101] {
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
-webkit-box-pack: justify;
|
||||
justify-content: space-between;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
padding: 0 .5rem;
|
||||
margin: 0 0 .25rem 0;
|
||||
}
|
||||
.details[data-v-3abcf101] {
|
||||
width: 100%;
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: justify;
|
||||
justify-content: space-between;
|
||||
position: absolute;
|
||||
font-size: 0;
|
||||
}
|
||||
|
@ -162,9 +176,13 @@
|
|||
font-weight: bold;
|
||||
}
|
||||
.info[data-v-3abcf101] {
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
flex-direction: column;
|
||||
-webkit-box-flex: 1;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.link[data-v-3abcf101] {
|
||||
text-decoration: none;
|
||||
|
@ -281,9 +299,12 @@
|
|||
background: rgba(0, 0, 0, 0.1);
|
||||
height: 12rem;
|
||||
width: 100%;
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: center;
|
||||
justify-content: center;
|
||||
-o-object-fit: cover;
|
||||
object-fit: cover;
|
||||
-o-object-position: 50% 0;
|
||||
|
@ -324,8 +345,10 @@
|
|||
box-sizing: border-box;
|
||||
}
|
||||
.row[data-v-2bc41e74] {
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
margin: 0 0 1rem 0;
|
||||
}
|
||||
.row .icon[data-v-2bc41e74] {
|
||||
|
@ -341,8 +364,10 @@
|
|||
cursor: default;
|
||||
}
|
||||
.info .column[data-v-2bc41e74] {
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
.tidbit[data-v-2bc41e74] {
|
||||
|
@ -362,15 +387,21 @@
|
|||
margin: 0 1rem 0 0;
|
||||
}
|
||||
.site[data-v-2bc41e74] {
|
||||
display: -webkit-inline-box;
|
||||
display: inline-flex;
|
||||
flex-grow: 1;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
-webkit-box-flex: 1;
|
||||
flex-grow: 1;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: end;
|
||||
justify-content: flex-end;
|
||||
padding: .25rem 0;
|
||||
font-size: 0;
|
||||
}
|
||||
.logo[data-v-2bc41e74] {
|
||||
display: inline-block;
|
||||
-webkit-filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.5));
|
||||
filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.5));
|
||||
}
|
||||
.logo-site[data-v-2bc41e74] {
|
||||
height: 3rem;
|
||||
|
@ -379,6 +410,8 @@
|
|||
object-fit: contain;
|
||||
-o-object-position: 100% 50%;
|
||||
object-position: 100% 50%;
|
||||
-webkit-filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.5));
|
||||
filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.5));
|
||||
}
|
||||
.logo-network[data-v-2bc41e74] {
|
||||
height: 1.5rem;
|
||||
|
@ -407,6 +440,7 @@
|
|||
font-size: 1rem;
|
||||
}
|
||||
.actors[data-v-2bc41e74] {
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
@ -460,13 +494,17 @@
|
|||
|
||||
/* $primary: #ff886c; */
|
||||
.header[data-v-3e57cf44] {
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
-webkit-box-pack: justify;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.title[data-v-3e57cf44] {
|
||||
display: -webkit-inline-box;
|
||||
display: inline-flex;
|
||||
align-items: top;
|
||||
-webkit-box-align: top;
|
||||
align-items: top;
|
||||
margin: 0 1rem 0 0;
|
||||
}
|
||||
.title:hover .icon[data-v-3e57cf44] {
|
||||
|
@ -477,10 +515,14 @@
|
|||
margin: 0 0 1rem 0;
|
||||
}
|
||||
.link[data-v-3e57cf44] {
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
flex-direction: column;
|
||||
-webkit-box-align: end;
|
||||
align-items: flex-end;
|
||||
}
|
||||
.logo[data-v-3e57cf44] {
|
||||
width: 20rem;
|
||||
|
@ -490,8 +532,10 @@
|
|||
margin: 0 .5rem 1rem 0;
|
||||
}
|
||||
.networklogo-container[data-v-3e57cf44] {
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
}
|
||||
.networklogo[data-v-3e57cf44] {
|
||||
color: #222;
|
||||
|
@ -517,9 +561,13 @@
|
|||
/* $primary: #ff886c; */
|
||||
.tile[data-v-f4958086] {
|
||||
background: #fff;
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
flex-direction: column;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
padding: .5rem 1rem;
|
||||
border-radius: .25rem;
|
||||
|
@ -534,9 +582,12 @@
|
|||
width: 100%;
|
||||
height: 5rem;
|
||||
color: #222;
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: center;
|
||||
justify-content: center;
|
||||
-o-object-fit: contain;
|
||||
object-fit: contain;
|
||||
font-size: 1rem;
|
||||
|
@ -547,22 +598,29 @@
|
|||
.title[data-v-f4958086] {
|
||||
color: #222;
|
||||
height: 100%;
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* $primary: #ff886c; */
|
||||
.header[data-v-e2e12602] {
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: top;
|
||||
-webkit-box-pack: justify;
|
||||
justify-content: space-between;
|
||||
-webkit-box-align: top;
|
||||
align-items: top;
|
||||
margin: 0 0 2rem 0;
|
||||
}
|
||||
.title[data-v-e2e12602] {
|
||||
display: -webkit-inline-box;
|
||||
display: inline-flex;
|
||||
align-items: top;
|
||||
-webkit-box-align: top;
|
||||
align-items: top;
|
||||
margin: 0 1rem 0 0;
|
||||
}
|
||||
.title:hover .icon[data-v-e2e12602] {
|
||||
|
@ -592,9 +650,13 @@
|
|||
/* $primary: #ff886c; */
|
||||
.tile[data-v-8b4c90b0] {
|
||||
background: #fff;
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
flex-direction: column;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
padding: .5rem 1rem;
|
||||
border-radius: .25rem;
|
||||
|
@ -609,9 +671,12 @@
|
|||
width: 100%;
|
||||
height: 5rem;
|
||||
color: #222;
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: center;
|
||||
justify-content: center;
|
||||
-o-object-fit: contain;
|
||||
object-fit: contain;
|
||||
font-size: 1rem;
|
||||
|
@ -622,8 +687,10 @@
|
|||
.title[data-v-8b4c90b0] {
|
||||
color: #222;
|
||||
height: 100%;
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
@ -660,6 +727,7 @@
|
|||
.photos[data-v-0a0430c7] {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
overflow-x: scroll;
|
||||
scrollbar-width: none;
|
||||
|
@ -680,8 +748,11 @@
|
|||
/* $primary: #ff886c; */
|
||||
.actor-inner[data-v-ea0483c2] {
|
||||
height: 100%;
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
@ -689,8 +760,11 @@
|
|||
background: #222;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
width: 100%;
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-box-direction: normal;
|
||||
flex-direction: row;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.profile .avatar-link[data-v-ea0483c2] {
|
||||
|
@ -708,21 +782,27 @@
|
|||
object-position: 50% 0;
|
||||
}
|
||||
.bio[data-v-ea0483c2] {
|
||||
flex-grow: 1;
|
||||
-webkit-box-flex: 1;
|
||||
flex-grow: 1;
|
||||
min-width: 20rem;
|
||||
box-sizing: border-box;
|
||||
padding: 1rem;
|
||||
margin: 0 2rem 0 0;
|
||||
}
|
||||
.bio-header[data-v-ea0483c2] {
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
-webkit-box-pack: justify;
|
||||
justify-content: space-between;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
margin: 0 0 1rem 0;
|
||||
}
|
||||
.bio-item[data-v-ea0483c2] {
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
-webkit-box-pack: justify;
|
||||
justify-content: space-between;
|
||||
padding: 0 0 .25rem 0;
|
||||
margin: 0 0 .25rem 0;
|
||||
line-height: 1.75;
|
||||
|
@ -801,7 +881,8 @@
|
|||
font-size: .8rem;
|
||||
}
|
||||
.extra[data-v-ea0483c2] {
|
||||
flex-grow: 1;
|
||||
-webkit-box-flex: 1;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.description[data-v-ea0483c2] {
|
||||
max-height: 12rem;
|
||||
|
@ -837,9 +918,13 @@
|
|||
fill: #ff6c88;
|
||||
}
|
||||
.actor-content[data-v-ea0483c2] {
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
flex-direction: row;
|
||||
-webkit-box-flex: 1;
|
||||
flex-grow: 1;
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-box-direction: normal;
|
||||
flex-direction: row;
|
||||
}
|
||||
.heading[data-v-ea0483c2] {
|
||||
padding: 0;
|
||||
|
@ -856,7 +941,8 @@
|
|||
display: none;
|
||||
}
|
||||
.releases[data-v-ea0483c2] {
|
||||
flex-grow: 1;
|
||||
-webkit-box-flex: 1;
|
||||
flex-grow: 1;
|
||||
padding: 1rem;
|
||||
}
|
||||
@media (max-width: 1500px) {
|
||||
|
@ -870,7 +956,9 @@
|
|||
display: none;
|
||||
}
|
||||
.actor-content[data-v-ea0483c2] {
|
||||
flex-direction: column;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
flex-direction: column;
|
||||
}
|
||||
.photos-container[data-v-ea0483c2] {
|
||||
border: none;
|
||||
|
@ -882,12 +970,15 @@
|
|||
display: none;
|
||||
}
|
||||
.photos.compact[data-v-ea0483c2] {
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
@media (max-width: 720px) {
|
||||
.profile[data-v-ea0483c2] {
|
||||
flex-direction: column;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
flex-direction: column;
|
||||
padding: 0 0 .5rem 0;
|
||||
}
|
||||
.bio[data-v-ea0483c2] {
|
||||
|
@ -931,10 +1022,15 @@
|
|||
|
||||
/* $primary: #ff886c; */
|
||||
.tag[data-v-7f130e7f] {
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-grow: 1;
|
||||
justify-content: stretch;
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-box-direction: normal;
|
||||
flex-direction: row;
|
||||
-webkit-box-flex: 1;
|
||||
flex-grow: 1;
|
||||
-webkit-box-pack: stretch;
|
||||
justify-content: stretch;
|
||||
}
|
||||
.sidebar[data-v-7f130e7f] {
|
||||
background: #222;
|
||||
|
@ -970,11 +1066,15 @@
|
|||
|
||||
/* $primary: #ff886c; */
|
||||
.tile[data-v-602c6fd8] {
|
||||
color: #fff;
|
||||
background: #222;
|
||||
color: #222;
|
||||
background: #fff;
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
flex-direction: column;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 0 3px rgba(0, 0, 0, 0.25);
|
||||
text-align: center;
|
||||
|
@ -988,15 +1088,19 @@
|
|||
}
|
||||
.title[data-v-602c6fd8] {
|
||||
height: 100%;
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: center;
|
||||
justify-content: center;
|
||||
font-size: 1rem;
|
||||
padding: .5rem 1rem;
|
||||
font-weight: bold;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
/* $primary: #ff886c; */
|
||||
.tags[data-v-66fa6284] {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
@ -1011,10 +1115,15 @@
|
|||
background: #fff;
|
||||
color: #ff6c88;
|
||||
height: 100%;
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
flex-direction: column;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: center;
|
||||
justify-content: center;
|
||||
font-size: 2rem;
|
||||
}
|
||||
.error[data-v-29109daf] {
|
||||
|
@ -1149,8 +1258,10 @@ body {
|
|||
|
||||
/* $primary: #ff886c; */
|
||||
.header[data-v-10b7ec04] {
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
color: #ff6c88;
|
||||
border-bottom: solid 1px rgba(0, 0, 0, 0.1);
|
||||
|
@ -1171,9 +1282,12 @@ body {
|
|||
display: inline-block;
|
||||
}
|
||||
.nav-link[data-v-10b7ec04] {
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: center;
|
||||
justify-content: center;
|
||||
padding: 1rem;
|
||||
border-bottom: solid 5px transparent;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
|
@ -1203,11 +1317,13 @@ body {
|
|||
display: none;
|
||||
}
|
||||
.nav .nolist[data-v-10b7ec04] {
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
}
|
||||
.nav[data-v-10b7ec04],
|
||||
.nav-item[data-v-10b7ec04] {
|
||||
flex-grow: 1;
|
||||
-webkit-box-flex: 1;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1215,18 +1331,26 @@ body {
|
|||
.container {
|
||||
background: #fafafa;
|
||||
height: 100%;
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
.content {
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
flex-direction: column;
|
||||
-webkit-box-flex: 1;
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.content-inner {
|
||||
flex-grow: 1;
|
||||
-webkit-box-flex: 1;
|
||||
flex-grow: 1;
|
||||
padding: 1rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
|
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 106 KiB |
After Width: | Height: | Size: 22 KiB |
|
@ -21,23 +21,17 @@ function getSites(networksMap) {
|
|||
{
|
||||
slug: 'buttplays',
|
||||
name: 'Butt Plays',
|
||||
url: 'https://www.buttplays.com',
|
||||
network_id: networksMap['21sextury'],
|
||||
parameters: JSON.stringify({ filter: true }),
|
||||
},
|
||||
{
|
||||
slug: 'clubsandy',
|
||||
name: 'Club Sandy',
|
||||
url: 'https://www.clubsandy.com',
|
||||
network_id: networksMap['21sextury'],
|
||||
parameters: JSON.stringify({ filter: true }),
|
||||
},
|
||||
{
|
||||
slug: 'deepthroatfrenzy',
|
||||
name: 'Deepthroat Frenzy',
|
||||
url: 'https://www.deepthroatfrenzy.com',
|
||||
network_id: networksMap['21sextury'],
|
||||
parameters: JSON.stringify({ filter: true }),
|
||||
},
|
||||
{
|
||||
slug: 'dpfanatics',
|
||||
|
@ -56,9 +50,7 @@ function getSites(networksMap) {
|
|||
{
|
||||
slug: 'gapeland',
|
||||
name: 'Gapeland',
|
||||
url: 'https://www.gapeland.com',
|
||||
network_id: networksMap['21sextury'],
|
||||
parameters: JSON.stringify({ filter: true }),
|
||||
},
|
||||
{
|
||||
slug: 'lezcuties',
|
||||
|
@ -67,12 +59,65 @@ function getSites(networksMap) {
|
|||
description: 'LezCuties brings you the cutest lesbian coeds and tiny teen lesbians in HD lesbian porn. Watch as European teens explore themselves and lick each other\'s tight lesbian pussy while their parents aren\'t home.',
|
||||
network_id: networksMap['21sextury'],
|
||||
},
|
||||
{
|
||||
slug: 'onlyswallows',
|
||||
name: 'Only Swallows',
|
||||
network_id: networksMap['21sextury'],
|
||||
},
|
||||
{
|
||||
slug: 'alettaoceanempire',
|
||||
name: 'Aletta Ocean Empire',
|
||||
network_id: networksMap['21sextury'],
|
||||
},
|
||||
{
|
||||
slug: 'analqueenalysa',
|
||||
name: 'Anal Queen Alysa',
|
||||
network_id: networksMap['21sextury'],
|
||||
},
|
||||
{
|
||||
slug: 'blueangellive',
|
||||
name: 'Blue Angel Live',
|
||||
network_id: networksMap['21sextury'],
|
||||
},
|
||||
{
|
||||
slug: 'pixandvideo',
|
||||
name: 'Pix and Video',
|
||||
url: 'https://www.pixandvideo.com',
|
||||
network_id: networksMap['21sextury'],
|
||||
parameters: JSON.stringify({ filter: true }),
|
||||
},
|
||||
{
|
||||
slug: 'cheatingwhorewives',
|
||||
name: 'Cheating Whore Wives',
|
||||
network_id: networksMap['21sextury'],
|
||||
},
|
||||
{
|
||||
slug: 'cutiesgalore',
|
||||
name: 'Cuties Galore',
|
||||
network_id: networksMap['21sextury'],
|
||||
},
|
||||
{
|
||||
slug: 'hotmilfclub',
|
||||
name: 'Hot MILF Club',
|
||||
network_id: networksMap['21sextury'],
|
||||
},
|
||||
{
|
||||
slug: 'letsplaylez',
|
||||
name: 'Lets Play Lez',
|
||||
network_id: networksMap['21sextury'],
|
||||
},
|
||||
{
|
||||
slug: 'nudefightclub',
|
||||
name: 'Nude Fight Club',
|
||||
network_id: networksMap['21sextury'],
|
||||
},
|
||||
{
|
||||
slug: 'sexwithkathianobili',
|
||||
name: 'Sex With Kathia Nobili',
|
||||
network_id: networksMap['21sextury'],
|
||||
},
|
||||
{
|
||||
slug: 'sweetsophiemoone',
|
||||
name: 'Sweet Sophie Moone',
|
||||
network_id: networksMap['21sextury'],
|
||||
},
|
||||
// BANGBROS
|
||||
{
|
||||
|
|
|
@ -72,6 +72,12 @@ function getMedia(tagsMap) {
|
|||
role: 'poster',
|
||||
comment: 'Juelz Ventura in "Gangbanged 5" for Elegant Angel',
|
||||
},
|
||||
{
|
||||
path: 'tags/tattoo.jpeg',
|
||||
target_id: tagsMap.tattoo,
|
||||
role: 'poster',
|
||||
comment: 'Kali Roses in "Goes All In For Anal" for Hussie Pass',
|
||||
},
|
||||
{
|
||||
path: 'tags/triple-anal.jpeg',
|
||||
target_id: tagsMap['triple-anal'],
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
{
|
||||
"extends": "airbnb-base",
|
||||
"parserOptions": {
|
||||
"parser": "babel-eslint",
|
||||
"sourceType": "script"
|
||||
},
|
||||
"rules": {
|
||||
"strict": 0,
|
||||
"no-unused-vars": ["error", {"argsIgnorePattern": "^_"}],
|
||||
"no-console": 0,
|
||||
"indent": ["error", 4],
|
||||
"max-len": [2, {"code": 300, "tabWidth": 4, "ignoreUrls": true}]
|
||||
"indent": "off",
|
||||
"max-len": [2, {"code": 300, "tabWidth": 4, "ignoreUrls": true}],
|
||||
"template-curly-spacing": "off"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -271,6 +271,8 @@ async function updateActor(actor, scraped = false, scrapeSuccess = false) {
|
|||
}
|
||||
|
||||
async function mergeProfiles(profiles, actor) {
|
||||
console.log(profiles);
|
||||
|
||||
const mergedProfile = profiles.reduce((prevProfile, profile) => {
|
||||
if (profile === null) {
|
||||
return prevProfile;
|
||||
|
|
|
@ -46,4 +46,4 @@ async function init() {
|
|||
await initServer();
|
||||
}
|
||||
|
||||
init();
|
||||
module.exports = init;
|
||||
|
|
|
@ -63,6 +63,7 @@ const { argv } = yargs
|
|||
.option('debug', {
|
||||
describe: 'Show error stack traces',
|
||||
type: 'boolean',
|
||||
default: process.env.NODE_ENV === 'development',
|
||||
});
|
||||
|
||||
module.exports = argv;
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
require('babel-polyfill');
|
||||
const init = require('./app');
|
||||
|
||||
init();
|
13
src/media.js
|
@ -10,6 +10,7 @@ const sharp = require('sharp');
|
|||
const blake2 = require('blake2');
|
||||
|
||||
const knex = require('./knex');
|
||||
const pluckPhotos = require('./utils/pluck-photos');
|
||||
|
||||
function getHash(buffer) {
|
||||
const hash = blake2.createHash('blake2b', { digestLength: 24 });
|
||||
|
@ -94,10 +95,10 @@ async function filterHashDuplicates(files, domains = ['releases'], roles = ['pho
|
|||
}
|
||||
|
||||
async function fetchPhoto(photoUrl, index, identifier) {
|
||||
const { pathname } = new URL(photoUrl);
|
||||
const mimetype = mime.getType(pathname);
|
||||
|
||||
try {
|
||||
const { pathname } = new URL(photoUrl);
|
||||
const mimetype = mime.getType(pathname);
|
||||
|
||||
const res = await bhttp.get(photoUrl);
|
||||
|
||||
if (res.statusCode === 200) {
|
||||
|
@ -176,7 +177,11 @@ async function storePhotos(release, releaseId) {
|
|||
return;
|
||||
}
|
||||
|
||||
const newPhotos = await filterSourceDuplicates(release.photos, 'releases', 'photo', `(${release.site.name}, ${releaseId}) "${release.title}"`);
|
||||
const pluckedPhotos = pluckPhotos(release.photos, release);
|
||||
|
||||
console.log(release.photos, pluckedPhotos);
|
||||
|
||||
const newPhotos = await filterSourceDuplicates(pluckedPhotos, 'releases', 'photo', `(${release.site.name}, ${releaseId}) "${release.title}"`);
|
||||
|
||||
if (newPhotos.length === 0) return;
|
||||
|
||||
|
|
|
@ -125,7 +125,7 @@ async function curateScrapedRelease(release) {
|
|||
likes: release.rating && release.rating.likes,
|
||||
dislikes: release.rating && release.rating.dislikes,
|
||||
rating: release.rating && release.rating.stars && Math.floor(release.rating.stars),
|
||||
deep: Boolean(argv.deep && release.url && !release.upcoming),
|
||||
deep: typeof release.deep === 'boolean' ? release.deep : false,
|
||||
};
|
||||
|
||||
if (release.site.isFallback && release.channel) {
|
||||
|
@ -275,6 +275,12 @@ async function storeRelease(release) {
|
|||
|
||||
async function storeReleases(releases) {
|
||||
const storedReleases = await Promise.map(releases, async (release) => {
|
||||
if (release.site.isFallback && !release.channel) {
|
||||
console.error(`Unable to derive channel site from generic URL: ${release.url}.`);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const releaseId = await storeRelease(release);
|
||||
|
||||
|
@ -289,7 +295,7 @@ async function storeReleases(releases) {
|
|||
}
|
||||
}, {
|
||||
concurrency: 10,
|
||||
});
|
||||
}).filter(release => release);
|
||||
|
||||
const actors = storedReleases.reduce((acc, release) => {
|
||||
if (!release.actors) return acc;
|
||||
|
|
|
@ -51,7 +51,9 @@ async function scrapeRelease(url, release, deep = false) {
|
|||
// don't store release when called by site scraper
|
||||
const [storedRelease] = await storeReleases([scene]);
|
||||
|
||||
console.log(`http://${config.web.host}:${config.web.port}/scene/${storedRelease.id}`);
|
||||
if (storedRelease) {
|
||||
console.log(`http://${config.web.host}:${config.web.port}/scene/${storedRelease.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
return scene;
|
||||
|
|
|
@ -69,12 +69,20 @@ async function scrapeUpcomingReleases(scraper, site) {
|
|||
async function deepFetchReleases(baseReleases) {
|
||||
return Promise.map(baseReleases, async (release) => {
|
||||
if (release.url) {
|
||||
const fullRelease = await scrapeRelease(release.url, release, true);
|
||||
try {
|
||||
const fullRelease = await scrapeRelease(release.url, release, true);
|
||||
|
||||
return {
|
||||
...release,
|
||||
...fullRelease,
|
||||
};
|
||||
return {
|
||||
...release,
|
||||
...fullRelease,
|
||||
deep: true,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
...release,
|
||||
deep: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return release;
|
||||
|
@ -116,7 +124,7 @@ async function scrapeReleases() {
|
|||
return await scrapeSiteReleases(scraper, site);
|
||||
} catch (error) {
|
||||
if (argv.debug) {
|
||||
console.error(`${site.id}: Failed to scrape releases`, error);
|
||||
console.error(`${site.name}: Failed to scrape releases`, error);
|
||||
}
|
||||
|
||||
console.warn(`${site.id}: Failed to scrape releases`);
|
||||
|
|
|
@ -1,11 +1,51 @@
|
|||
'use strict';
|
||||
|
||||
const Promise = require('bluebird');
|
||||
const bhttp = require('bhttp');
|
||||
const cheerio = require('cheerio');
|
||||
const moment = require('moment');
|
||||
|
||||
const knex = require('../knex');
|
||||
const { matchTags } = require('../tags');
|
||||
async function fetchPhotos(photoPath) {
|
||||
const res = await bhttp.get(`https://21sextury.com${photoPath}`);
|
||||
|
||||
return res.body.toString();
|
||||
}
|
||||
|
||||
function scrapePhotos(html) {
|
||||
const $ = cheerio.load(html, { normalizeWhitespace: true });
|
||||
|
||||
const unlockedPhotos = $('.preview .imgLink.pgUnlocked')
|
||||
.map((photoIndex, photoElement) => $(photoElement).attr('href')).toArray();
|
||||
|
||||
const lockedThumbnails = $('.preview .imgLink.lockedPicture img')
|
||||
.map((photoIndex, photoElement) => $(photoElement)
|
||||
.attr('src'))
|
||||
// .replace('_tb.jpg', '.jpg')) does not always work
|
||||
.toArray();
|
||||
|
||||
return unlockedPhotos.concat(lockedThumbnails);
|
||||
}
|
||||
|
||||
async function getPhotos(photoPath) {
|
||||
if (!photoPath || photoPath.match('join')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const html = await fetchPhotos(photoPath);
|
||||
const $ = cheerio.load(html, { normalizeWhitespace: true });
|
||||
const photos = scrapePhotos(html);
|
||||
const pages = $('.paginatorPages a').map((pageIndex, pageElement) => $(pageElement).attr('href')).toArray();
|
||||
|
||||
const otherPhotos = await Promise.map(pages, async (pagePath) => {
|
||||
const pageHtml = await fetchPhotos(pagePath);
|
||||
|
||||
return scrapePhotos(pageHtml);
|
||||
}, {
|
||||
concurrency: 2,
|
||||
});
|
||||
|
||||
return photos.concat(otherPhotos.flat());
|
||||
}
|
||||
|
||||
function scrape(html, site) {
|
||||
const $ = cheerio.load(html, { normalizeWhitespace: true });
|
||||
|
@ -14,12 +54,13 @@ function scrape(html, site) {
|
|||
return scenesElements.reduce((accReleases, element) => {
|
||||
const siteName = $(element).find('.studioName a').attr('title');
|
||||
|
||||
if (site.parameters && site.parameters.filter && siteName.toLowerCase() !== site.name.toLowerCase()) {
|
||||
if (!site.url && siteName.toLowerCase() !== site.name.toLowerCase()) {
|
||||
// using generic overview as fallback, scene from different site
|
||||
return accReleases;
|
||||
}
|
||||
|
||||
const sceneLinkElement = $(element).find('.sceneTitle a');
|
||||
const url = `${site.url}${sceneLinkElement.attr('href')}`;
|
||||
const url = `${site.url || 'https://www.21sextury.com'}${sceneLinkElement.attr('href')}`;
|
||||
const title = sceneLinkElement.attr('title').trim();
|
||||
|
||||
const entryId = $(element).attr('data-itemid');
|
||||
|
@ -32,6 +73,9 @@ function scrape(html, site) {
|
|||
.map((actorIndex, actorElement) => $(actorElement).attr('title'))
|
||||
.toArray();
|
||||
|
||||
const poster = $(element).find('.imgLink img').attr('data-original');
|
||||
const trailer = `https://videothumb.gammacdn.com/307x224/${entryId}.mp4`;
|
||||
|
||||
const [likes, dislikes] = $(element).find('.value')
|
||||
.toArray()
|
||||
.map(value => Number($(value).text()));
|
||||
|
@ -44,6 +88,10 @@ function scrape(html, site) {
|
|||
title,
|
||||
actors,
|
||||
date,
|
||||
poster,
|
||||
trailer: {
|
||||
src: trailer,
|
||||
},
|
||||
rating: {
|
||||
likes,
|
||||
dislikes,
|
||||
|
@ -58,25 +106,21 @@ async function scrapeScene(html, url, site) {
|
|||
const $ = cheerio.load(html, { normalizeWhitespace: true });
|
||||
const sceneElement = $('#videoWrapper');
|
||||
const json = $('script[type="application/ld+json"]').html();
|
||||
const videoJson = $('script:contains("ScenePlayerOptions")').html();
|
||||
const videoDataString = videoJson.slice(videoJson.indexOf('= {') + 2, videoJson.indexOf('};') + 1);
|
||||
|
||||
const data = JSON.parse(json)[0];
|
||||
const videoData = JSON.parse(videoDataString);
|
||||
const entryId = new URL(url).pathname.split('/').slice(-1)[0];
|
||||
|
||||
const title = data.isPartOf ? data.isPartOf.name : data.name;
|
||||
const dataDate = moment.utc(data.dateCreated, 'YYYY-MM-DD');
|
||||
const title = videoData?.playerOptions?.sceneInfos?.sceneTitle || (data.isPartOf && data.isPartOf !== 'TBD' ? data.isPartOf.name : data.name);
|
||||
const dataDate = moment.utc(videoData?.playerOptions?.sceneInfos?.sceneReleaseDate, 'YYYY-MM-DD');
|
||||
|
||||
const date = dataDate.isValid()
|
||||
? dataDate.toDate()
|
||||
: moment.utc(sceneElement.find('.updatedDate').text().trim(), 'MM-DD-YYYY').toDate();
|
||||
|
||||
const actors = data.actor
|
||||
.sort(({ gender: genderA }, { gender: genderB }) => {
|
||||
if (genderA === 'female' && genderB === 'male') return -1;
|
||||
if (genderA === 'male' && genderB === 'female') return 1;
|
||||
|
||||
return 0;
|
||||
})
|
||||
.map(actor => actor.name);
|
||||
const actors = data.actor.map(actor => actor.name);
|
||||
|
||||
const description = data.description || null; // prevent empty string
|
||||
const likes = Number(sceneElement.find('.rating .state_1 .value').text());
|
||||
|
@ -84,27 +128,18 @@ async function scrapeScene(html, url, site) {
|
|||
|
||||
const duration = moment.duration(data.duration.slice(2).split(':')).asSeconds();
|
||||
|
||||
const rawTags = data.keywords.split(', ');
|
||||
const poster = videoData.picPreview;
|
||||
const trailer = `${videoData.playerOptions.host}${videoData.url}`;
|
||||
|
||||
const photoPath = $('.picturesItem a').attr('href');
|
||||
const photos = await getPhotos(photoPath, site);
|
||||
|
||||
const tags = data.keywords.split(', ');
|
||||
const siteName = data.productionCompany ? data.productionCompany.name : $('#logoLink a').attr('title');
|
||||
const siteId = siteName && siteName.replace(/\s+/g, '').toLowerCase();
|
||||
|
||||
const [channelSite, tags] = await Promise.all([
|
||||
site.isFallback
|
||||
? knex('sites')
|
||||
.where({ slug: siteId })
|
||||
.orWhereRaw('name = ? collate NOCASE', [siteName])
|
||||
.first()
|
||||
: site,
|
||||
matchTags(rawTags),
|
||||
]);
|
||||
|
||||
// only replace generic URL with site URL if site is not marked to fetch scenes from generic site
|
||||
const originalUrl = channelSite && !(channelSite.parameters && JSON.parse(channelSite.parameters).filter)
|
||||
? `${channelSite.url}/en/video/${new URL(url).pathname.split('/').slice(-2).join('/')}`
|
||||
: url;
|
||||
const channel = siteName && siteName.replace(/\s+/g, '').toLowerCase();
|
||||
|
||||
return {
|
||||
url: originalUrl,
|
||||
url,
|
||||
entryId,
|
||||
title,
|
||||
date,
|
||||
|
@ -112,22 +147,30 @@ async function scrapeScene(html, url, site) {
|
|||
description,
|
||||
duration,
|
||||
tags,
|
||||
poster,
|
||||
photos,
|
||||
trailer: {
|
||||
src: trailer,
|
||||
},
|
||||
rating: {
|
||||
likes,
|
||||
dislikes,
|
||||
},
|
||||
site: channelSite || site,
|
||||
site,
|
||||
channel,
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchLatest(site, page = 1) {
|
||||
const res = await bhttp.get(`${site.parameters && site.parameters.filter ? 'https://21sextury.com' : site.url}/en/videos/All-Categories/0/All-Pornstars/0/latest/${page}`);
|
||||
const url = `${site.url || 'https://21sextury.com'}/en/videos/All-Categories/0/All-Pornstars/0/latest/${page}`;
|
||||
const res = await bhttp.get(url);
|
||||
|
||||
return scrape(res.body.toString(), site);
|
||||
}
|
||||
|
||||
async function fetchUpcoming(site) {
|
||||
const res = await bhttp.get(`${site.parameters && site.parameters.filter ? 'https://21sextury.com' : site.url}/en/videos/All-Categories/0/All-Pornstars/0/upcoming`);
|
||||
const url = `${site.url || 'https://21sextury.com'}/en/videos/All-Categories/0/All-Pornstars/0/upcoming`;
|
||||
const res = await bhttp.get(url);
|
||||
|
||||
return scrape(res.body.toString(), site);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ const { JSDOM } = require('jsdom');
|
|||
const moment = require('moment');
|
||||
|
||||
const knex = require('../knex');
|
||||
const { matchTags } = require('../tags');
|
||||
|
||||
/* eslint-disable newline-per-chained-call */
|
||||
function scrapeLatest(html, site) {
|
||||
|
@ -49,13 +48,16 @@ async function scrapeScene(html, url, site) {
|
|||
const title = $('meta[itemprop="name"]').attr('content');
|
||||
const description = $('.descr-box p').text(); // meta tags don't contain full description
|
||||
|
||||
const date = moment.utc($('meta[itemprop="uploadDate"]').attr('content'), 'YYYY-MM-DD').toDate();
|
||||
const dateProp = $('meta[itemprop="uploadDate"]').attr('content');
|
||||
const date = dateProp
|
||||
? moment.utc($('meta[itemprop="uploadDate"]').attr('content'), 'YYYY-MM-DD').toDate()
|
||||
: moment.utc($('.title-border:nth-child(2) p').text(), 'MM.DD.YYYY').toDate();
|
||||
const actors = $('.pornstar-card > a').map((actorIndex, actorElement) => $(actorElement).attr('title')).toArray();
|
||||
|
||||
const likes = Number($('.info-panel.likes .likes').text());
|
||||
const duration = Number($('.info-panel.duration .duration').text().slice(0, -4)) * 60;
|
||||
|
||||
const rawTags = $('.tags-tab .tags a').map((tagIndex, tagElement) => $(tagElement).text()).toArray();
|
||||
const tags = $('.tags-tab .tags a').map((tagIndex, tagElement) => $(tagElement).text()).toArray();
|
||||
|
||||
const poster = $('#video').attr('poster');
|
||||
const photos = $('.photo-slider-guest .card a').map((photoIndex, photoElement) => $(photoElement).attr('href')).toArray();
|
||||
|
@ -63,21 +65,7 @@ async function scrapeScene(html, url, site) {
|
|||
const trailer540 = $('source[res="540"]').attr('src');
|
||||
const trailer720 = $('source[res="720"]').attr('src');
|
||||
|
||||
/*
|
||||
* broken as of nov 2019
|
||||
const { origin } = new URL($('.pornstar-card meta[itemprop="url"]').first().attr('content'));
|
||||
|
||||
const [channelSite, tags] = await Promise.all([
|
||||
// don't find site if original is already specific
|
||||
site.isFallback ? knex('sites').where({ url: origin }).first() : site,
|
||||
matchTags(rawTags),
|
||||
]);
|
||||
*/
|
||||
|
||||
const tags = await matchTags(rawTags);
|
||||
|
||||
return {
|
||||
// url: channelSite ? `${channelSite.url}${new URL(url).pathname}` : url,
|
||||
url,
|
||||
entryId,
|
||||
title,
|
||||
|
@ -88,20 +76,19 @@ async function scrapeScene(html, url, site) {
|
|||
tags,
|
||||
poster,
|
||||
photos,
|
||||
trailer: trailer540
|
||||
? {
|
||||
src: trailer540,
|
||||
quality: 540,
|
||||
}
|
||||
: {
|
||||
// backup
|
||||
trailer: [
|
||||
{
|
||||
src: trailer720,
|
||||
quality: 720,
|
||||
},
|
||||
{
|
||||
src: trailer540,
|
||||
quality: 540,
|
||||
},
|
||||
],
|
||||
rating: {
|
||||
likes,
|
||||
},
|
||||
// site: channelSite || site,
|
||||
site,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ const moment = require('moment');
|
|||
|
||||
const knex = require('../knex');
|
||||
const { matchTags } = require('../tags');
|
||||
const pluckPhotos = require('../utils/pluck-photos');
|
||||
|
||||
async function getPhoto(url) {
|
||||
const res = await bhttp.get(url);
|
||||
|
@ -20,7 +19,7 @@ async function getPhoto(url) {
|
|||
return photoUrl;
|
||||
}
|
||||
|
||||
async function getPhotos(albumUrl, site, siteUrl) {
|
||||
async function getPhotos(albumUrl) {
|
||||
const res = await bhttp.get(albumUrl);
|
||||
const html = res.body.toString();
|
||||
const { document } = new JSDOM(html).window;
|
||||
|
@ -28,15 +27,7 @@ async function getPhotos(albumUrl, site, siteUrl) {
|
|||
const lastPhotoPage = Array.from(document.querySelectorAll('.preview-image-container a')).slice(-1)[0].href;
|
||||
const lastPhotoIndex = parseInt(lastPhotoPage.match(/\d+.jpg/)[0], 10);
|
||||
|
||||
// dogfart has massive albums, pick 25 or specified number of photos: first, last and evenly inbetween
|
||||
const photoLimit = (site.network.parameters && site.network.parameters.photoLimit) || 25;
|
||||
const photoIndexes = pluckPhotos(lastPhotoIndex, photoLimit);
|
||||
|
||||
if (photoLimit > 25) {
|
||||
console.log(`${site.name}: Scraping ${photoLimit} album photos from ${siteUrl}, this may take some time...`);
|
||||
}
|
||||
|
||||
const photoUrls = await Promise.map(photoIndexes, async (index) => {
|
||||
const photoUrls = await Promise.map(Array.from({ length: lastPhotoIndex }), async (index) => {
|
||||
const pageUrl = `https://blacksonblondes.com${lastPhotoPage.replace(/\d+.jpg/, `${index.toString().padStart(3, '0')}.jpg`)}`;
|
||||
|
||||
return getPhoto(pageUrl);
|
||||
|
|
|
@ -9,8 +9,6 @@ const moment = require('moment');
|
|||
const { heightToCm } = require('../utils/convert');
|
||||
const { matchTags } = require('../tags');
|
||||
|
||||
const pluckPhotos = require('../utils/pluck-photos');
|
||||
|
||||
async function fetchPhotos(url) {
|
||||
const res = await bhttp.get(url);
|
||||
|
||||
|
@ -58,14 +56,7 @@ async function getPhotos(entryId, site, page = 1) {
|
|||
})
|
||||
: [];
|
||||
|
||||
const allPhotos = photos.concat(otherPhotos.flat());
|
||||
|
||||
const photoLimit = (site.network.parameters && site.network.parameters.photoLimit) || 25;
|
||||
const photoIndexes = pluckPhotos(allPhotos.length - 1, photoLimit);
|
||||
|
||||
const pluckedPhotos = photoIndexes.map(photoIndex => allPhotos[photoIndex]);
|
||||
|
||||
return pluckedPhotos;
|
||||
return photos.concat(otherPhotos.flat());
|
||||
}
|
||||
|
||||
function scrapeLatest(html, site) {
|
||||
|
|
|
@ -8,7 +8,6 @@ const moment = require('moment');
|
|||
|
||||
const { fetchSites } = require('../sites');
|
||||
const { matchTags } = require('../tags');
|
||||
const pluckPhotos = require('../utils/pluck-photos');
|
||||
|
||||
const defaultTags = {
|
||||
hardx: [],
|
||||
|
@ -38,7 +37,7 @@ function scrapePhotos(html) {
|
|||
return unlockedPhotos.concat(lockedThumbnails);
|
||||
}
|
||||
|
||||
async function getPhotos(albumPath, siteDomain, site) {
|
||||
async function getPhotos(albumPath, siteDomain) {
|
||||
const albumUrl = `https://${siteDomain}${albumPath}`;
|
||||
|
||||
const html = await fetchPhotos(albumUrl);
|
||||
|
@ -56,14 +55,7 @@ async function getPhotos(albumPath, siteDomain, site) {
|
|||
concurrency: 2,
|
||||
});
|
||||
|
||||
const allPhotos = photos.concat(otherPhotos.flat());
|
||||
|
||||
const photoLimit = (site.network.parameters && site.network.parameters.photoLimit) || 25;
|
||||
const photoIndexes = pluckPhotos(allPhotos.length - 1, photoLimit);
|
||||
|
||||
const pluckedPhotos = photoIndexes.map(photoIndex => allPhotos[photoIndex]);
|
||||
|
||||
return pluckedPhotos;
|
||||
return photos.concat(otherPhotos.flat());
|
||||
}
|
||||
|
||||
function scrape(html, site) {
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
'use strict';
|
||||
|
||||
const config = require('config');
|
||||
|
||||
// pick {photoLimit} photos evenly distributed photos from a set with {photoTotal} photos, return array of indexes starting at 1
|
||||
function pluckPhotos(photoTotal, photoLimit) {
|
||||
function pluckPhotos(photos, release, specifiedLimit) {
|
||||
const limit = specifiedLimit || config.media.limit;
|
||||
console.log(limit);
|
||||
|
||||
const plucked = [1]
|
||||
.concat(
|
||||
Array.from({ length: photoLimit - 1 }, (value, index) => Math.round((index + 1) * (photoTotal / (photoLimit - 1)))),
|
||||
Array.from({ length: limit - 1 }, (value, index) => Math.round((index + 1) * (photos.length / (limit)))),
|
||||
);
|
||||
|
||||
return Array.from(new Set(plucked)); // remove duplicates, may happen when photo total and photo limit are close
|
||||
return Array.from(new Set(plucked)).map(photoIndex => photos[photoIndex]); // remove duplicates, may happen when photo total and photo limit are close
|
||||
}
|
||||
|
||||
module.exports = pluckPhotos;
|
||||
|
|