diff --git a/assets/components/releases/release.vue b/assets/components/releases/release.vue index 14da7b86..f3fa7db4 100644 --- a/assets/components/releases/release.vue +++ b/assets/components/releases/release.vue @@ -231,7 +231,7 @@ v-if="release.productionDate" class="row-tidbit" > - Production date + Shoot date {{ formatDate(release.productionDate, 'MMMM D, YYYY') }} diff --git a/assets/img/favicon/border/android-chrome-192x192.png b/assets/img/favicon/border/android-chrome-192x192.png new file mode 100644 index 00000000..7d75fa0f Binary files /dev/null and b/assets/img/favicon/border/android-chrome-192x192.png differ diff --git a/assets/img/favicon/border/android-chrome-512x512.png b/assets/img/favicon/border/android-chrome-512x512.png new file mode 100644 index 00000000..7092374b Binary files /dev/null and b/assets/img/favicon/border/android-chrome-512x512.png differ diff --git a/assets/img/favicon/border/apple-touch-icon.png b/assets/img/favicon/border/apple-touch-icon.png new file mode 100644 index 00000000..26bcf554 Binary files /dev/null and b/assets/img/favicon/border/apple-touch-icon.png differ diff --git a/assets/img/favicon/border/browserconfig.xml b/assets/img/favicon/border/browserconfig.xml new file mode 100644 index 00000000..50c09863 --- /dev/null +++ b/assets/img/favicon/border/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #ff6c88 + + + diff --git a/assets/img/favicon/border/favicon-16x16.png b/assets/img/favicon/border/favicon-16x16.png new file mode 100644 index 00000000..57206671 Binary files /dev/null and b/assets/img/favicon/border/favicon-16x16.png differ diff --git a/assets/img/favicon/border/favicon-32x32.png b/assets/img/favicon/border/favicon-32x32.png new file mode 100644 index 00000000..71dfc35c Binary files /dev/null and b/assets/img/favicon/border/favicon-32x32.png differ diff --git a/assets/img/favicon/border/favicon.ico b/assets/img/favicon/border/favicon.ico new file mode 100644 index 00000000..afc5cac9 Binary files /dev/null and b/assets/img/favicon/border/favicon.ico differ diff --git a/assets/img/favicon/border/mstile-150x150.png b/assets/img/favicon/border/mstile-150x150.png new file mode 100644 index 00000000..4626b985 Binary files /dev/null and b/assets/img/favicon/border/mstile-150x150.png differ diff --git a/assets/img/favicon/border/safari-pinned-tab.svg b/assets/img/favicon/border/safari-pinned-tab.svg new file mode 100644 index 00000000..3cff570e --- /dev/null +++ b/assets/img/favicon/border/safari-pinned-tab.svg @@ -0,0 +1,28 @@ + + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + + diff --git a/assets/img/favicon/border/site.webmanifest b/assets/img/favicon/border/site.webmanifest new file mode 100644 index 00000000..fb7aee98 --- /dev/null +++ b/assets/img/favicon/border/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/img/favicon/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/img/favicon/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ff6c88", + "background_color": "#ff6c88", + "display": "standalone" +} diff --git a/assets/img/favicon/favicon.png b/assets/img/favicon/favicon.png new file mode 100644 index 00000000..9cb66580 Binary files /dev/null and b/assets/img/favicon/favicon.png differ diff --git a/assets/img/favicon/favicon.svg b/assets/img/favicon/favicon.svg new file mode 100644 index 00000000..76cf9c77 --- /dev/null +++ b/assets/img/favicon/favicon.svg @@ -0,0 +1,75 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/assets/img/favicon/favicon_border.png b/assets/img/favicon/favicon_border.png new file mode 100644 index 00000000..4d91f9b7 Binary files /dev/null and b/assets/img/favicon/favicon_border.png differ diff --git a/public/img/favicon/favicon-16x16.ico b/assets/img/favicon/old/favicon-16x16.ico similarity index 100% rename from public/img/favicon/favicon-16x16.ico rename to assets/img/favicon/old/favicon-16x16.ico diff --git a/public/img/favicon/favicon-32x32.ico b/assets/img/favicon/old/favicon-32x32.ico similarity index 100% rename from public/img/favicon/favicon-32x32.ico rename to assets/img/favicon/old/favicon-32x32.ico diff --git a/public/img/favicon/favicon-64x64.ico b/assets/img/favicon/old/favicon-64x64.ico similarity index 100% rename from public/img/favicon/favicon-64x64.ico rename to assets/img/favicon/old/favicon-64x64.ico diff --git a/public/img/favicon/favicon-64x64.png b/assets/img/favicon/old/favicon-64x64.png similarity index 100% rename from public/img/favicon/favicon-64x64.png rename to assets/img/favicon/old/favicon-64x64.png diff --git a/public/img/favicon/favicon-dark-16x16.ico b/assets/img/favicon/old/favicon-dark-16x16.ico similarity index 100% rename from public/img/favicon/favicon-dark-16x16.ico rename to assets/img/favicon/old/favicon-dark-16x16.ico diff --git a/public/img/favicon/favicon-dark-32x32.ico b/assets/img/favicon/old/favicon-dark-32x32.ico similarity index 100% rename from public/img/favicon/favicon-dark-32x32.ico rename to assets/img/favicon/old/favicon-dark-32x32.ico diff --git a/public/img/favicon/favicon-dark-64x64.ico b/assets/img/favicon/old/favicon-dark-64x64.ico similarity index 100% rename from public/img/favicon/favicon-dark-64x64.ico rename to assets/img/favicon/old/favicon-dark-64x64.ico diff --git a/public/img/favicon/favicon.png b/assets/img/favicon/old/favicon.png similarity index 100% rename from public/img/favicon/favicon.png rename to assets/img/favicon/old/favicon.png diff --git a/assets/img/logo.svg b/assets/img/logo.svg index 0def5f49..2384a4e1 100644 --- a/assets/img/logo.svg +++ b/assets/img/logo.svg @@ -1,12 +1,95 @@ - - - - - - - - - + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + - diff --git a/assets/img/logo_old.svg b/assets/img/logo_old.svg new file mode 100644 index 00000000..0def5f49 --- /dev/null +++ b/assets/img/logo_old.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/assets/index.ejs b/assets/index.ejs index 94a2d421..df4dd18f 100644 --- a/assets/index.ejs +++ b/assets/index.ejs @@ -3,15 +3,20 @@ - + traxxx - - - + + + + + + + + diff --git a/assets/js/actors/actions.js b/assets/js/actors/actions.js index 1be909ab..0a26b4bb 100644 --- a/assets/js/actors/actions.js +++ b/assets/js/actors/actions.js @@ -80,7 +80,7 @@ function initActorActions(store, router) { lazy hash comment - copyright + credit sfw: sfwMedia { id thumbnail @@ -111,7 +111,7 @@ function initActorActions(store, router) { lazy hash comment - copyright + credit sfw: sfwMedia { id thumbnail @@ -314,7 +314,7 @@ function initActorActions(store, router) { thumbnail lazy comment - copyright + credit sfw: sfwMedia { id thumbnail diff --git a/migrations/20190325001339_releases.js b/migrations/20190325001339_releases.js index 33fe19f1..86478470 100644 --- a/migrations/20190325001339_releases.js +++ b/migrations/20190325001339_releases.js @@ -621,14 +621,14 @@ exports.up = knex => Promise.resolve() table.text('title'); table.text('slug'); - table.date('date'); + table.timestamp('date'); table.index('date'); + table.date('production_date'); + table.enum('date_precision', ['year', 'month', 'day', 'hour', 'minute', 'second']) .defaultTo('day'); - table.date('production_date'); - table.text('description'); table.integer('duration') diff --git a/package-lock.json b/package-lock.json index 2f9e70b5..0c4cb4c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1490,21 +1490,22 @@ "acorn": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz", - "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==" + "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==", + "dev": true }, "acorn-globals": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", - "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", "requires": { - "acorn": "^6.0.1", - "acorn-walk": "^6.0.1" + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" }, "dependencies": { "acorn": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz", - "integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==" + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", + "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==" } } }, @@ -1515,9 +1516,9 @@ "dev": true }, "acorn-walk": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", - "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==" + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==" }, "adm-zip": { "version": "0.4.14", @@ -1654,11 +1655,6 @@ "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=" }, - "array-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", - "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=" - }, "array-find-index": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", @@ -2211,9 +2207,9 @@ "dev": true }, "browser-process-hrtime": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", - "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" }, "browserify-aes": { "version": "1.2.0", @@ -3111,9 +3107,9 @@ "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" }, "cssstyle": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.2.0.tgz", - "integrity": "sha512-sEb3XFPx3jNnCAMtqrXPDeSgQr+jojtCeNf8cvMNMh1cG970+lljssvQDzPq6lmmJu2Vhqood/gtEomBiHOGnA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", "requires": { "cssom": "~0.3.6" }, @@ -3181,13 +3177,13 @@ } }, "data-urls": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", - "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", "requires": { - "abab": "^2.0.0", - "whatwg-mimetype": "^2.2.0", - "whatwg-url": "^7.0.0" + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" } }, "dayjs": { @@ -3214,6 +3210,11 @@ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, + "decimal.js": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz", + "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==" + }, "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", @@ -3386,11 +3387,18 @@ "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" }, "domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", "requires": { - "webidl-conversions": "^4.0.2" + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==" + } } }, "domhandler": { @@ -3635,9 +3643,9 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "escodegen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", - "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==", + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", "requires": { "esprima": "^4.0.1", "estraverse": "^4.2.0", @@ -5761,11 +5769,11 @@ "dev": true }, "html-encoding-sniffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", - "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", "requires": { - "whatwg-encoding": "^1.0.1" + "whatwg-encoding": "^1.0.5" } }, "htmlparser2": { @@ -6273,6 +6281,11 @@ "isobject": "^3.0.1" } }, + "is-potential-custom-element-name": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz", + "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=" + }, "is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", @@ -6408,42 +6421,47 @@ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, "jsdom": { - "version": "15.2.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-15.2.1.tgz", - "integrity": "sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.3.0.tgz", + "integrity": "sha512-zggeX5UuEknpdZzv15+MS1dPYG0J/TftiiNunOeNxSl3qr8Z6cIlQpN0IdJa44z9aFxZRIVqRncvEhQ7X5DtZg==", "requires": { - "abab": "^2.0.0", - "acorn": "^7.1.0", - "acorn-globals": "^4.3.2", - "array-equal": "^1.0.0", - "cssom": "^0.4.1", - "cssstyle": "^2.0.0", - "data-urls": "^1.1.0", - "domexception": "^1.0.1", - "escodegen": "^1.11.1", - "html-encoding-sniffer": "^1.0.2", + "abab": "^2.0.3", + "acorn": "^7.1.1", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.2.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.0", + "domexception": "^2.0.1", + "escodegen": "^1.14.1", + "html-encoding-sniffer": "^2.0.1", + "is-potential-custom-element-name": "^1.0.0", "nwsapi": "^2.2.0", - "parse5": "5.1.0", - "pn": "^1.1.0", - "request": "^2.88.0", - "request-promise-native": "^1.0.7", - "saxes": "^3.1.9", - "symbol-tree": "^3.2.2", + "parse5": "5.1.1", + "request": "^2.88.2", + "request-promise-native": "^1.0.8", + "saxes": "^5.0.0", + "symbol-tree": "^3.2.4", "tough-cookie": "^3.0.1", - "w3c-hr-time": "^1.0.1", - "w3c-xmlserializer": "^1.1.2", - "webidl-conversions": "^4.0.2", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", "whatwg-encoding": "^1.0.5", "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^7.0.0", - "ws": "^7.0.0", + "whatwg-url": "^8.0.0", + "ws": "^7.2.3", "xml-name-validator": "^3.0.0" }, "dependencies": { + "acorn": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", + "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==" + }, "parse5": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", - "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==" + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" } } }, @@ -8331,11 +8349,6 @@ "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==" }, - "pn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", - "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==" - }, "popper.js": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", @@ -9561,11 +9574,11 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "saxes": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz", - "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", "requires": { - "xmlchars": "^2.1.1" + "xmlchars": "^2.2.0" } }, "scheduler": { @@ -10946,11 +10959,11 @@ } }, "tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", + "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==", "requires": { - "punycode": "^2.1.0" + "punycode": "^2.1.1" } }, "traverse": { @@ -11560,20 +11573,18 @@ "integrity": "sha512-ha3jNLJqNhhrAemDXcmMJMKf1Zu4sybMPr9KxJIuOpVcsDQlTBYLLladav2U+g1AvdYDG5Gs0xBTb0M5pXXYFQ==" }, "w3c-hr-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", - "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", "requires": { - "browser-process-hrtime": "^0.1.2" + "browser-process-hrtime": "^1.0.0" } }, "w3c-xmlserializer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz", - "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", "requires": { - "domexception": "^1.0.1", - "webidl-conversions": "^4.0.2", "xml-name-validator": "^3.0.0" } }, @@ -11597,9 +11608,9 @@ } }, "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==" }, "webpack": { "version": "4.41.6", @@ -11847,13 +11858,20 @@ "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" }, "whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.1.0.tgz", + "integrity": "sha512-vEIkwNi9Hqt4TV9RdnaBPNt+E2Sgmo3gePebCRgZ1R7g6d23+53zCTnuB0amKI4AXq6VM8jj2DUAa0S1vjJxkw==", "requires": { "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" + "tr46": "^2.0.2", + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==" + } } }, "which": { @@ -11999,9 +12017,9 @@ } }, "ws": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.1.tgz", - "integrity": "sha512-sucePNSafamSKoOqoNfBd8V0StlkzJKL2ZAhGQinCfNQ+oacw+Pk7lcdAElecBF2VkLNZRiIb5Oi1Q5lVUVt2A==" + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", + "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" }, "xml-name-validator": { "version": "3.0.0", diff --git a/package.json b/package.json index eb322380..5831cfca 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "fs-extra": "^7.0.1", "graphile-utils": "^4.5.6", "iconv-lite": "^0.5.1", - "jsdom": "^15.2.1", + "jsdom": "^16.3.0", "knex": "^0.20.10", "knex-migrate": "^1.7.4", "longjohn": "^0.2.12", diff --git a/public/img/favicon/android-chrome-192x192.png b/public/img/favicon/android-chrome-192x192.png new file mode 100644 index 00000000..21cfdffb Binary files /dev/null and b/public/img/favicon/android-chrome-192x192.png differ diff --git a/public/img/favicon/android-chrome-512x512.png b/public/img/favicon/android-chrome-512x512.png new file mode 100644 index 00000000..314d1bda Binary files /dev/null and b/public/img/favicon/android-chrome-512x512.png differ diff --git a/public/img/favicon/apple-touch-icon.png b/public/img/favicon/apple-touch-icon.png new file mode 100644 index 00000000..5cea8b44 Binary files /dev/null and b/public/img/favicon/apple-touch-icon.png differ diff --git a/public/img/favicon/browserconfig.xml b/public/img/favicon/browserconfig.xml new file mode 100644 index 00000000..50c09863 --- /dev/null +++ b/public/img/favicon/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #ff6c88 + + + diff --git a/public/img/favicon/favicon-16x16.png b/public/img/favicon/favicon-16x16.png new file mode 100644 index 00000000..cb9a2ad2 Binary files /dev/null and b/public/img/favicon/favicon-16x16.png differ diff --git a/public/img/favicon/favicon-32x32.png b/public/img/favicon/favicon-32x32.png new file mode 100644 index 00000000..73f82de6 Binary files /dev/null and b/public/img/favicon/favicon-32x32.png differ diff --git a/public/img/favicon/favicon.ico b/public/img/favicon/favicon.ico new file mode 100644 index 00000000..7fbb90b7 Binary files /dev/null and b/public/img/favicon/favicon.ico differ diff --git a/public/img/favicon/mstile-150x150.png b/public/img/favicon/mstile-150x150.png new file mode 100644 index 00000000..08ae5704 Binary files /dev/null and b/public/img/favicon/mstile-150x150.png differ diff --git a/public/img/favicon/safari-pinned-tab.svg b/public/img/favicon/safari-pinned-tab.svg new file mode 100644 index 00000000..5f5a3689 --- /dev/null +++ b/public/img/favicon/safari-pinned-tab.svg @@ -0,0 +1,48 @@ + + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + + diff --git a/public/img/favicon/site.webmanifest b/public/img/favicon/site.webmanifest new file mode 100644 index 00000000..ed370de3 --- /dev/null +++ b/public/img/favicon/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "traxxx", + "short_name": "traxxx", + "icons": [ + { + "src": "/img/favicon/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/img/favicon/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/public/img/logos/elegantangel/elegantangel.png b/public/img/logos/elegantangel/elegantangel.png new file mode 100644 index 00000000..46e594d1 Binary files /dev/null and b/public/img/logos/elegantangel/elegantangel.png differ diff --git a/public/img/logos/elegantangel/network.png b/public/img/logos/elegantangel/network.png new file mode 100644 index 00000000..46e594d1 Binary files /dev/null and b/public/img/logos/elegantangel/network.png differ diff --git a/public/img/logos/mikeadriano/lazy/allanal.png b/public/img/logos/mikeadriano/lazy/allanal.png index 9bfc7ee2..c3363527 100644 Binary files a/public/img/logos/mikeadriano/lazy/allanal.png and b/public/img/logos/mikeadriano/lazy/allanal.png differ diff --git a/public/img/logos/mikeadriano/lazy/favicon.png b/public/img/logos/mikeadriano/lazy/favicon.png index 2e58f81f..1958930d 100644 Binary files a/public/img/logos/mikeadriano/lazy/favicon.png and b/public/img/logos/mikeadriano/lazy/favicon.png differ diff --git a/public/img/logos/mikeadriano/lazy/network.png b/public/img/logos/mikeadriano/lazy/network.png index 9eecf599..1ee18ec9 100644 Binary files a/public/img/logos/mikeadriano/lazy/network.png and b/public/img/logos/mikeadriano/lazy/network.png differ diff --git a/public/img/logos/mikeadriano/lazy/nympho.png b/public/img/logos/mikeadriano/lazy/nympho.png index 343b226a..aa8bd79c 100644 Binary files a/public/img/logos/mikeadriano/lazy/nympho.png and b/public/img/logos/mikeadriano/lazy/nympho.png differ diff --git a/public/img/logos/mikeadriano/lazy/swallowed.png b/public/img/logos/mikeadriano/lazy/swallowed.png index b503bf05..84bbf963 100644 Binary files a/public/img/logos/mikeadriano/lazy/swallowed.png and b/public/img/logos/mikeadriano/lazy/swallowed.png differ diff --git a/public/img/logos/mikeadriano/lazy/trueanal.png b/public/img/logos/mikeadriano/lazy/trueanal.png index 7aa57757..933f2e07 100644 Binary files a/public/img/logos/mikeadriano/lazy/trueanal.png and b/public/img/logos/mikeadriano/lazy/trueanal.png differ diff --git a/public/img/logos/mikeadriano/misc/mike-adriano_2.png b/public/img/logos/mikeadriano/misc/mike-adriano_2.png new file mode 100644 index 00000000..f806e8f6 Binary files /dev/null and b/public/img/logos/mikeadriano/misc/mike-adriano_2.png differ diff --git a/public/img/logos/mikeadriano/network.png b/public/img/logos/mikeadriano/network.png index f806e8f6..89f89b9c 100644 Binary files a/public/img/logos/mikeadriano/network.png and b/public/img/logos/mikeadriano/network.png differ diff --git a/public/img/logos/mikeadriano/thumbs/allanal.png b/public/img/logos/mikeadriano/thumbs/allanal.png index a5b31fba..3d513cb1 100644 Binary files a/public/img/logos/mikeadriano/thumbs/allanal.png and b/public/img/logos/mikeadriano/thumbs/allanal.png differ diff --git a/public/img/logos/mikeadriano/thumbs/favicon.png b/public/img/logos/mikeadriano/thumbs/favicon.png index 2e58f81f..1958930d 100644 Binary files a/public/img/logos/mikeadriano/thumbs/favicon.png and b/public/img/logos/mikeadriano/thumbs/favicon.png differ diff --git a/public/img/logos/mikeadriano/thumbs/network.png b/public/img/logos/mikeadriano/thumbs/network.png index d0c32e62..4589a111 100644 Binary files a/public/img/logos/mikeadriano/thumbs/network.png and b/public/img/logos/mikeadriano/thumbs/network.png differ diff --git a/public/img/logos/mikeadriano/thumbs/nympho.png b/public/img/logos/mikeadriano/thumbs/nympho.png index e7393fe1..2884107a 100644 Binary files a/public/img/logos/mikeadriano/thumbs/nympho.png and b/public/img/logos/mikeadriano/thumbs/nympho.png differ diff --git a/public/img/logos/mikeadriano/thumbs/swallowed.png b/public/img/logos/mikeadriano/thumbs/swallowed.png index a0bce4d2..f8ec87b0 100644 Binary files a/public/img/logos/mikeadriano/thumbs/swallowed.png and b/public/img/logos/mikeadriano/thumbs/swallowed.png differ diff --git a/public/img/logos/mikeadriano/thumbs/trueanal.png b/public/img/logos/mikeadriano/thumbs/trueanal.png index 0a8a97c0..05735bd7 100644 Binary files a/public/img/logos/mikeadriano/thumbs/trueanal.png and b/public/img/logos/mikeadriano/thumbs/trueanal.png differ diff --git a/seeds/01_networks.js b/seeds/01_networks.js index 708df9e3..e26f5d7d 100644 --- a/seeds/01_networks.js +++ b/seeds/01_networks.js @@ -305,8 +305,7 @@ const networks = [ { slug: 'mikeadriano', name: 'Mike Adriano', - url: null, - description: null, + url: 'https://www.mikeadriano.com', }, { slug: 'milehighmedia', diff --git a/seeds/02_sites.js b/seeds/02_sites.js index 8e229c8a..cff532f7 100644 --- a/seeds/02_sites.js +++ b/seeds/02_sites.js @@ -1821,6 +1821,12 @@ const sites = [ description: '', parent: 'dogfartnetwork', }, + // ELEGANT ANGEL + { + slug: 'elegantangel', + name: 'Elegant Angel', + url: 'https://www.elegantangel.com', + }, // EVIL ANGEL { slug: 'evilangel', diff --git a/src/scrapers/killergram.js b/src/scrapers/killergram.js index ff4b3f82..66cd60ff 100644 --- a/src/scrapers/killergram.js +++ b/src/scrapers/killergram.js @@ -68,7 +68,7 @@ async function fetchActorReleases({ query }, url, remainingPages, actorName, acc return accReleases.concat(releases); } -async function scrapeProfile({ query, window }, actorName, url, include) { +async function scrapeProfile({ query }, actorName, url, include) { const profile = {}; profile.avatar = { @@ -85,7 +85,7 @@ async function scrapeProfile({ query, window }, actorName, url, include) { if (include.releases) { const availablePages = query.all('.pageboxdropdown option', 'value'); - profile.releases = await fetchActorReleases(qu.init(query.q('#episodes > table'), window), url, availablePages.slice(1), actorName); + profile.releases = await fetchActorReleases(qu.init(query.q('#episodes > table')), url, availablePages.slice(1), actorName); } return profile; @@ -105,7 +105,9 @@ async function fetchScene(url, channel) { async function fetchProfile(actorName, entity, include) { const url = `http://killergram.com/episodes.asp?page=episodes&model=${encodeURI(actorName)}&ct=model`; - const res = await qu.get(url, '#content'); + const res = await qu.get(url, '#content', null, { + followRedirects: false, + }); return res.ok ? scrapeProfile(res.item, actorName, url, include) : res.status; } diff --git a/src/scrapers/mikeadriano.js b/src/scrapers/mikeadriano.js index cc2d586f..ab201835 100644 --- a/src/scrapers/mikeadriano.js +++ b/src/scrapers/mikeadriano.js @@ -1,237 +1,88 @@ 'use strict'; -/* eslint-disable newline-per-chained-call */ -const { JSDOM } = require('jsdom'); -const cheerio = require('cheerio'); -const moment = require('moment'); -// const bhttp = require('bhttp'); +const qu = require('../utils/qu'); -const { get } = require('../utils/http'); - -const descriptionTags = { - 'anal cream pie': 'anal creampie', - 'ass to mouth': 'ass to mouth', - 'cream pie in her ass': 'anal creampie', - 'eats ass': 'ass eating', - facial: 'facial', - gaped: 'gaping', - gapes: 'gaping', - gape: 'gaping', - 'rectal cream pie': 'anal creampie', - rimming: 'ass eating', -}; - -function deriveTagsFromDescription(description) { - const matches = (description || '').toLowerCase().match(new RegExp(Object.keys(descriptionTags).join('|'), 'g')); - - return matches - ? matches.map(match => descriptionTags[match]) - : []; -} - -async function scrapeLatestA(html, site) { - const { document } = new JSDOM(html).window; - const sceneElements = document.querySelectorAll('.content-item-large, .content-item'); - - return Promise.all(Array.from(sceneElements, async (element) => { - const $ = cheerio.load(element.innerHTML, { normalizeWhitespace: true }); - - const titleElement = element.querySelector('h3.title a'); - const title = titleElement.textContent; - const url = titleElement.href; - const entryId = url.split('/').slice(-2)[0]; - - const descriptionElement = element.querySelector('.desc'); - const description = descriptionElement && descriptionElement.textContent.trim(); - const date = moment(element.querySelector('.date, time').textContent, 'Do MMM YYYY').toDate(); - - const actors = Array.from(element.querySelectorAll('h4.models a'), actorElement => actorElement.textContent); - - const durationString = element.querySelector('.total-time').textContent.trim(); - // timestamp is sometimes 00:00, sometimes 0:00:00 - const duration = durationString.split(':').length === 3 - ? moment.duration(durationString).asSeconds() - : moment.duration(`00:${durationString}`).asSeconds(); - - const ratingElement = element.querySelector('.rating'); - const stars = ratingElement && ratingElement.dataset.rating; - - const [poster, ...primaryPhotos] = Array.from(element.querySelectorAll('img'), imageElement => imageElement.src); - const secondaryPhotos = $('.thumb-top, .thumb-bottom') - .map((photoIndex, photoElement) => $(photoElement).css()['background-image']) - .toArray() - .map(photoUrl => photoUrl.slice(photoUrl.indexOf('http'), photoUrl.indexOf('.jpg') + 4)); - - const photos = [...primaryPhotos, ...secondaryPhotos]; - const tags = deriveTagsFromDescription(description); - - const scene = { - url, - entryId, - title, - description, - actors, - director: 'Mike Adriano', - date, - duration, - tags, - poster, - photos, - rating: { - stars, - }, - site, - }; - - return scene; - })); -} - -async function scrapeLatestB(html) { - const { document } = new JSDOM(html).window; - const sceneElements = document.querySelectorAll('.content-border'); - - return Promise.all(Array.from(sceneElements, async (element) => { - const $ = cheerio.load(element.innerHTML, { normalizeWhitespace: true }); +async function scrapeAll(scenes) { + return scenes.map(({ query }) => { const release = { director: 'Mike Adriano', }; - const titleElement = element.querySelector('.content-title-wrap a'); - release.title = titleElement.title || titleElement.textContent.trim(); - release.url = titleElement.href; - release.entryId = release.url.split('/').slice(-2)[0]; + release.title = query.cnt('h3.title a, .content-title-wrap a'); + release.url = query.url('h3.title a, .content-title-wrap a'); + release.entryId = new URL(release.url).pathname.match(/\/view\/(\d+)/)[1]; - release.description = element.querySelector('.content-description').textContent.trim(); - release.date = (moment(element.querySelector('.mobile-date').textContent, 'MM/DD/YYYY') - || moment(element.querySelector('.date').textContent, 'Do MMM YYYY')).toDate(); - release.actors = Array.from(element.querySelectorAll('.content-models a'), actorElement => actorElement.textContent); + release.description = query.cnt('.desc, .content-description'); + release.date = query.date('.date, time, .hide', 'Do MMM YYYY'); - const durationString = element.querySelector('.total-time').textContent.trim(); - // timestamp is somethines 00:00, sometimes 0:00:00 - release.duration = durationString.split(':').length === 3 - ? moment.duration(durationString).asSeconds() - : moment.duration(`00:${durationString}`).asSeconds(); + release.actors = query.cnts('h4.models a, .content-models a'); + release.duration = query.dur('.total-time'); - const [poster, ...primaryPhotos] = Array.from(element.querySelectorAll('a img'), imageElement => imageElement.src); - const secondaryPhotos = $('.thumb-mouseover') - .map((photoIndex, photoElement) => $(photoElement).css()['background-image']) - .toArray() - .map(photoUrl => photoUrl.slice(photoUrl.indexOf('http'), photoUrl.indexOf('.jpg') + 4)); + const [poster, ...primaryPhotos] = query.imgs('a img'); + const secondaryPhotos = query.styles('.thumb-top, .thumb-bottom, .thumb-mouseover', 'background-image').map(style => style.match(/url\((.*)\)/)[1]); release.poster = poster; - release.photos = [...primaryPhotos, ...secondaryPhotos]; + release.photos = primaryPhotos.concat(secondaryPhotos); - release.tags = deriveTagsFromDescription(release.description); return release; - })); + }); } -async function scrapeSceneA(html, url) { - const { document } = new JSDOM(html).window; - const element = document.querySelector('.content-page-info'); - const release = { - url, - director: 'Mike Adriano', - }; +function scrapeScene({ query }, url) { + const release = { director: 'Mike Adriano' }; - release.entryId = url.split('/').slice(-2)[0]; - release.title = element.querySelector('.title').textContent.trim(); - release.description = element.querySelector('.desc').textContent.trim(); - release.date = moment(element.querySelector('.post-date').textContent.trim(), 'Do MMM YYYY').toDate(); + if (query.exists('a[href*="stackpath.com"]')) { + throw new Error('URL blocked by StackPath'); + } - release.actors = Array.from(element.querySelectorAll('.models a'), actorElement => actorElement.textContent); + release.entryId = new URL(url).pathname.match(/\/view\/(\d+)/)[1]; - const durationString = element.querySelector('.total-time').textContent.trim(); - // timestamp is sometimes 00:00, sometimes 0:00:00 - release.duration = durationString.split(':').length === 3 - ? moment.duration(durationString).asSeconds() - : moment.duration(`00:${durationString}`).asSeconds(); + release.title = query.cnt('.content-page-info .title'); + release.description = query.cnt('.content-page-info .desc'); + release.date = query.date('.content-page-info .date, .content-page-info .hide', 'Do MMM YYYY'); - const { poster } = document.querySelector('.content-page-header video'); - const { src, type } = document.querySelector('.content-page-header source'); + release.actors = query.cnts('.content-page-info .models a'); + release.duration = query.dur('.content-page-info .total-time:last-child'); - release.poster = poster; - release.trailer = { src, type }; + release.poster = query.poster('.content-page-header video, .content-page-header-inner video'); - release.tags = deriveTagsFromDescription(release.description); + const trailerEl = query.q('.content-page-header source, .content-page-header-inner source'); + + if (trailerEl) { + release.trailer = { + src: trailerEl.src, + type: trailerEl.type, + }; + } return release; } -async function scrapeSceneB(html, url, site) { - const { document } = new JSDOM(html).window; - const element = document.querySelector('.content-page-info'); - - const entryId = url.split('/').slice(-2)[0]; - const title = element.querySelector('.title').textContent.trim(); - const description = element.querySelector('.desc').textContent.trim(); - const date = moment(element.querySelector('.date').textContent.trim(), 'Do MMM YYYY').toDate(); - - const actors = Array.from(element.querySelectorAll('.models a'), actorElement => actorElement.textContent); - - const durationString = element.querySelector('.total-time').textContent.trim(); - // timestamp is somethines 00:00, sometimes 0:00:00 - const duration = durationString.split(':').length === 3 - ? moment.duration(durationString).asSeconds() - : moment.duration(`00:${durationString}`).asSeconds(); - - const { poster } = document.querySelector('.content-page-header-inner video'); - const { src, type } = document.querySelector('.content-page-header-inner source'); - - const tags = deriveTagsFromDescription(description); - - const scene = { - url, - entryId, - title, - description, - actors, - director: 'Mike Adriano', - date, - duration, - tags, - poster, - trailer: { - src, - type, - }, - site, - }; - - return scene; -} - -async function fetchLatest(site, page = 1) { - const { host } = new URL(site.url); +async function fetchLatest(channel, page = 1) { + const { host } = new URL(channel.url); const url = `https://tour.${host}/videos?page=${page}`; - const res = await get(url); + const res = await qu.get(url); - if (res.code === 200) { - if (host === 'trueanal.com' || host === 'swallowed.com') { - return scrapeLatestA(res.html, site); + if (res.ok) { + if (res.item.query.exists('a[href*="stackpath.com"]')) { + throw new Error('URL blocked by StackPath'); } - return scrapeLatestB(res.html, site); + return scrapeAll(qu.initAll(res.item.el, '.content-item-large, .content-item, .content-border'), channel); } - return res.code; + return res.status; } -async function fetchScene(url, site) { - const { host } = new URL(site.url); - const res = await get(url); +async function fetchScene(url, channel) { + const res = await qu.get(url); - if (res.code === 200) { - if (host === 'trueanal.com' || host === 'swallowed.com') { - return scrapeSceneA(res.body.toString(), url, site); - } - - return scrapeSceneB(res.body.toString(), url, site); + if (res.ok) { + return scrapeScene(res.item, url, channel); } - return res.code; + return res.status; } /* API protected diff --git a/src/scrapers/vixen.js b/src/scrapers/vixen.js index 71df674c..f9d2a736 100644 --- a/src/scrapers/vixen.js +++ b/src/scrapers/vixen.js @@ -78,7 +78,7 @@ function scrapeAll(scenes, site, origin) { release.url = `${site?.url || origin}${scene.targetUrl}`; release.date = moment.utc(scene.releaseDate).toDate(); - release.shootDate = moment.utc(scene.shootDate).toDate(); + release.datePrecision = 'minute'; release.actors = scene.models; release.stars = Number(scene.textRating) / 2; @@ -104,7 +104,7 @@ function scrapeUpcoming(scene, site) { release.url = `${site.url}${scene.targetUrl}`; release.date = moment.utc(scene.releaseDate).toDate(); - release.shootDate = moment.utc(scene.shootDate).toDate(); + release.datePrecision = 'minute'; release.actors = scene.models; @@ -133,7 +133,8 @@ async function scrapeScene(data, url, site, baseRelease) { release.entryId = scene.newId; release.date = moment.utc(scene.releaseDate).toDate(); - release.shootDate = moment.utc(scene.shootDate).toDate(); + release.productionDate = moment.utc(scene.shootDate).toDate(); + release.datePrecision = 'minute'; release.actors = baseRelease?.actors || scene.models; diff --git a/src/store-releases.js b/src/store-releases.js index dcba2b00..cbc08192 100644 --- a/src/store-releases.js +++ b/src/store-releases.js @@ -31,6 +31,7 @@ function curateReleaseEntry(release, batchId, existingRelease) { shoot_id: release.shootId || null, url: release.url, date: Number(release.date) ? release.date : null, + production_date: Number(release.productionDate) ? release.productionDate : null, date_precision: release.datePrecision, slug, description: release.description, diff --git a/src/utils/jsdom-url.js b/src/utils/jsdom-url.js new file mode 100644 index 00000000..85b05108 --- /dev/null +++ b/src/utils/jsdom-url.js @@ -0,0 +1,17 @@ +'use strict'; + +const { JSDOM } = require('jsdom'); + +const el = new JSDOM(` + + + + + +`).window.document; + +console.log(el.querySelector('#url').style.backgroundImage); +console.log(el.querySelector('#urlQuotes').style.backgroundImage); +console.log(el.querySelector('#color').style.color); +console.log(el.querySelector('#urlSpaces').style.backgroundImage); +console.log(el.querySelector('#colorSpaces').style.color); diff --git a/src/utils/qu.js b/src/utils/qu.js index f25a0944..5d8058bd 100644 --- a/src/utils/qu.js +++ b/src/utils/qu.js @@ -4,9 +4,11 @@ const { JSDOM } = require('jsdom'); const moment = require('moment'); const http = require('./http'); +const { window: globalWindow } = new JSDOM(''); + function trim(str) { if (typeof str !== 'string') { - return null; + return str; } return str.trim().replace(/\s+/g, ' '); @@ -55,9 +57,9 @@ function q(context, selector, attrArg, applyTrim = true) { if (attr) { const value = selector ? context.querySelector(selector)?.[attr] || context.querySelector(selector)?.attributes[attr]?.value - : context[attr] || context.attributes[attr]?.value; + : context[attr] || context.getAttribute(attr); - return applyTrim && value ? trim(value) : value; + return applyTrim && typeof value === 'string' ? trim(value) : value; } return selector ? context.querySelector(selector) : context; @@ -77,6 +79,14 @@ function exists(context, selector) { return !!q(context, selector); } +function content(context, selector, applyTrim = true) { + return q(context, selector, 'textContent', applyTrim); +} + +function contents(context, selector, applyTrim) { + return all(context, selector, 'textContent', applyTrim); +} + function html(context, selector) { const el = q(context, selector, null, true); @@ -103,6 +113,33 @@ function text(context, selector, applyTrim = true) { return applyTrim ? trim(textValue) : textValue; } +function removeStyleFunctionSpaces(el) { + // jsdom appears to have a bug where it ignores inline CSS attributes set to a function() containing spaces, e.g. url( image.png ) + el.setAttribute('style', el.getAttribute('style').replace(/\(\s+(.*)\s+\)/g, (match, cssArgs) => `(${cssArgs})`)); +} + +function style(context, selector, styleAttr) { + const el = q(context, selector); + + if (el) { + removeStyleFunctionSpaces(el); + + return styleAttr ? el.style[styleAttr] : el.style; + } + + return null; +} + +function styles(context, selector, styleAttr) { + const elStyles = Array.from(context.querySelectorAll(selector), (el) => { + removeStyleFunctionSpaces(el); + + return styleAttr ? el.style[styleAttr] : el.style; + }); + + return elStyles; +} + function number(context, selector, attr = true) { const value = q(context, selector, attr); @@ -236,6 +273,10 @@ const legacyFuncs = { const quFuncs = { all, html, + content, + contents, + cnt: content, + cnts: contents, date, dur: duration, duration, @@ -250,6 +291,8 @@ const quFuncs = { num: number, poster, q, + style, + styles, text, texts, trailer: video, @@ -265,7 +308,7 @@ function init(element, window) { const legacyContextFuncs = Object.entries(legacyFuncs) // dynamically attach methods with context .reduce((acc, [key, func]) => ({ ...acc, - [key]: (...args) => (window && args[0] instanceof window.HTMLElement // allow for different context + [key]: (...args) => (args[0] instanceof globalWindow.HTMLElement // allow for different context ? func(...args) : func(element, ...args)), }), {}); @@ -273,7 +316,7 @@ function init(element, window) { const quContextFuncs = Object.entries(quFuncs) // dynamically attach methods with context .reduce((acc, [key, func]) => ({ ...acc, - [key]: (...args) => (window && args[0] instanceof window.HTMLElement // allow for different context + [key]: (...args) => (args[0] instanceof globalWindow.HTMLElement // allow for different context ? func(...args) : func(element, ...args)), }), {}); @@ -319,7 +362,7 @@ function extractAll(htmlValue, selector) { } async function get(urlValue, selector, headers, options, queryAll = false) { - const res = await http.get(urlValue, headers); + const res = await http.get(urlValue, headers, options); if (res.statusCode === 200) { const item = queryAll