From 213f8f56ab8a48aabb08e7996eb30bbb687925cf Mon Sep 17 00:00:00 2001 From: Jonathan Barrow Date: Mon, 25 Sep 2023 18:50:56 -0400 Subject: [PATCH] Added source testing script --- .gitignore | 1 + package-lock.json | 385 ++++++++++++++++++++++++++++++------------- package.json | 8 +- run-source.ts | 405 ++++++++++++++++++++++++++++++++++++++++++++++ tsconfig.json | 2 +- 5 files changed, 685 insertions(+), 116 deletions(-) create mode 100644 run-source.ts diff --git a/.gitignore b/.gitignore index 7525992..7780010 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules/ /lib coverage +.env diff --git a/package-lock.json b/package-lock.json index 626dcc4..26d6d86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,24 +1,29 @@ { "name": "@movie-web/providers", - "version": "0.0.11", + "version": "0.0.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@movie-web/providers", - "version": "0.0.11", + "version": "0.0.12", "license": "MIT", "dependencies": { "cheerio": "^1.0.0-rc.12", "crypto-js": "^4.1.1", "form-data": "^4.0.0", - "node-fetch": "^3.3.2" + "node-fetch": "^2.7.0" }, "devDependencies": { "@types/crypto-js": "^4.1.1", + "@types/node-fetch": "^2.6.6", + "@types/spinnies": "^0.5.1", "@typescript-eslint/eslint-plugin": "^5.60.0", "@typescript-eslint/parser": "^5.60.0", "@vitest/coverage-v8": "^0.34.3", + "commander": "^11.0.0", + "dotenv": "^16.3.1", + "enquirer": "^2.4.1", "eslint": "^8.30.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-prettier": "^8.5.0", @@ -26,6 +31,7 @@ "eslint-plugin-import": "^2.27.5", "eslint-plugin-prettier": "^4.2.1", "prettier": "^2.6.2", + "spinnies": "^0.5.1", "tsc-alias": "^1.6.7", "typescript": "^4.6.3", "vite": "^4.0.0", @@ -877,12 +883,28 @@ "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==", "dev": true }, + "node_modules/@types/node-fetch": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.6.tgz", + "integrity": "sha512-95X8guJYhfqiuVVhRFxVQcf4hW/2bCuoPwDasMf/531STFoNoWTT7YDnWdXHEZKqAGUigmpG31r2FE70LwnzJw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, "node_modules/@types/semver": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, + "node_modules/@types/spinnies": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@types/spinnies/-/spinnies-0.5.1.tgz", + "integrity": "sha512-ka85pZ8mW3QPeKxDqrpNMNzoa/oEdlOXXvckCneDcKddj52zM5fHpiAPVQIpYZUMIw66nZsc1CK1dcB0h1a37g==", + "dev": true + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.60.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.0.tgz", @@ -1555,6 +1577,15 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1953,6 +1984,18 @@ "node": ">= 6" } }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1992,12 +2035,12 @@ } }, "node_modules/commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", "dev": true, "engines": { - "node": "^12.20.0 || >=14" + "node": ">=16" } }, "node_modules/concat-map": { @@ -2127,14 +2170,6 @@ "optional": true, "peer": true }, - "node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "engines": { - "node": ">= 12" - } - }, "node_modules/data-urls": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", @@ -2492,6 +2527,18 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, "node_modules/enhanced-resolve": { "version": "5.15.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", @@ -2505,6 +2552,19 @@ "node": ">=10.13.0" } }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -2976,6 +3036,30 @@ "node": ">=0.10.0" } }, + "node_modules/eslint-plugin-import/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/eslint-plugin-import/node_modules/tsconfig-paths": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", + "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, "node_modules/eslint-plugin-prettier": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", @@ -3189,28 +3273,6 @@ "reusify": "^1.0.4" } }, - "node_modules/fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, - "engines": { - "node": "^12.20 || >= 14.13" - } - }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -3292,17 +3354,6 @@ "node": ">= 6" } }, - "node_modules/formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "dependencies": { - "fetch-blob": "^3.1.2" - }, - "engines": { - "node": ">=12.20.0" - } - }, "node_modules/fs-extra": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", @@ -4589,39 +4640,42 @@ "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", "dev": true }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, "engines": { - "node": ">=10.5.0" + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, - "node_modules/node-fetch": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" } }, "node_modules/normalize-path": { @@ -5177,6 +5231,19 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -5375,6 +5442,109 @@ "node": ">=0.10.0" } }, + "node_modules/spinnies": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/spinnies/-/spinnies-0.5.1.tgz", + "integrity": "sha512-WpjSXv9NQz0nU3yCT9TFEOfpFrXADY9C5fG6eAJqixLhvTX1jP3w92Y8IE5oafIe42nlF9otjhllnXN/QCaB3A==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "cli-cursor": "^3.0.0", + "strip-ansi": "^5.2.0" + } + }, + "node_modules/spinnies/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/spinnies/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/spinnies/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/spinnies/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/spinnies/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/spinnies/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/spinnies/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/spinnies/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/spinnies/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -5459,6 +5629,15 @@ "node": ">=8" } }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -5680,37 +5859,13 @@ "tsc-alias": "dist/bin/index.js" } }, - "node_modules/tsconfig-paths": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", - "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/tsconfig-paths/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "node_modules/tsc-alias/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", "dev": true, "engines": { - "node": ">=4" + "node": "^12.20.0 || >=14" } }, "node_modules/tslib": { @@ -6180,14 +6335,6 @@ "node": ">=10" } }, - "node_modules/web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", - "engines": { - "node": ">= 8" - } - }, "node_modules/webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", @@ -6401,6 +6548,16 @@ "optionalDependencies": { "commander": "^9.4.1" } + }, + "node_modules/z-schema/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "optional": true, + "engines": { + "node": "^12.20.0 || >=14" + } } } } diff --git a/package.json b/package.json index 95b1b79..8ce3f6d 100644 --- a/package.json +++ b/package.json @@ -46,9 +46,14 @@ }, "devDependencies": { "@types/crypto-js": "^4.1.1", + "@types/node-fetch": "^2.6.6", + "@types/spinnies": "^0.5.1", "@typescript-eslint/eslint-plugin": "^5.60.0", "@typescript-eslint/parser": "^5.60.0", "@vitest/coverage-v8": "^0.34.3", + "commander": "^11.0.0", + "dotenv": "^16.3.1", + "enquirer": "^2.4.1", "eslint": "^8.30.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-prettier": "^8.5.0", @@ -56,6 +61,7 @@ "eslint-plugin-import": "^2.27.5", "eslint-plugin-prettier": "^4.2.1", "prettier": "^2.6.2", + "spinnies": "^0.5.1", "tsc-alias": "^1.6.7", "typescript": "^4.6.3", "vite": "^4.0.0", @@ -67,6 +73,6 @@ "cheerio": "^1.0.0-rc.12", "crypto-js": "^4.1.1", "form-data": "^4.0.0", - "node-fetch": "^3.3.2" + "node-fetch": "^2.7.0" } } diff --git a/run-source.ts b/run-source.ts new file mode 100644 index 0000000..0c86da0 --- /dev/null +++ b/run-source.ts @@ -0,0 +1,405 @@ +import nodeFetch from 'node-fetch'; +import { prompt } from 'enquirer'; +import Spinnies from 'spinnies'; +import { program } from 'commander'; +import dotenv from 'dotenv'; +import { makeProviders, targets, makeStandardFetcher, MovieMedia, ShowMedia, ProviderControls, MetaOutput } from '.'; + +dotenv.config(); + +type ProviderSourceAnswers = { + id: string; + type: string; +}; + +type EmbedSourceAnswers = { + url: string; +}; + +type CommonAnswers = { + fetcher: string; + source: string; +}; + +type ShowAnswers = { + season: string; + episode: string; +}; + +type CommandLineArguments = { + fetcher: string; + sourceId: string; + tmdbId: string; + type: string; + season: string; + episode: string; + url: string; +}; + +const TMDB_API_KEY = process.env.MOVIE_WEB_TMDB_API_KEY; + +const sources = getAllSources(); + +function getAllSources() { + // * The only way to get a list of all sources is to + // * create all these things. Maybe this should change + const nativeSources = makeProviders({ + fetcher: makeStandardFetcher(nodeFetch), + target: targets.NATIVE + }).listSources(); + + const browserSources = makeProviders({ + fetcher: makeStandardFetcher(nodeFetch), + target: targets.BROWSER + }).listSources(); + + const nativeEmbeds = makeProviders({ + fetcher: makeStandardFetcher(nodeFetch), + target: targets.NATIVE + }).listEmbeds(); + + const browserEmbeds = makeProviders({ + fetcher: makeStandardFetcher(nodeFetch), + target: targets.BROWSER + }).listEmbeds(); + + const combined = [ + ...nativeSources, ...browserSources, + ...nativeEmbeds, ...browserEmbeds + ]; + + // * Remove dupes + const map = new Map(combined.map(source => [source.id, source])); + + return [...map.values()] +} + +async function getMovieMediaDetails(id: string): Promise { + const response = await fetch(`https://api.themoviedb.org/3/movie/${id}?api_key=${TMDB_API_KEY}`, { + method: 'GET', + headers: { + accept: 'application/json' + } + }); + const movie = await response.json(); + + if (movie.success === false) { + throw new Error(movie.status_message); + } + + return { + type: 'movie', + title: movie.title, + releaseYear: Number(movie.release_date.split('-')[0]), + tmdbId: id + }; +} + +async function getShowMediaDetails(id: string, seasonNumber: string, episodeNumber: string): Promise { + // * TV shows require the TMDB ID for the series, season, and episode + // * and the name of the series. Needs multiple requests + let response = await fetch(`https://api.themoviedb.org/3/tv/${id}?api_key=${TMDB_API_KEY}`, { + method: 'GET', + headers: { + accept: 'application/json' + } + }); + const series = await response.json(); + + if (series.success === false) { + throw new Error(series.status_message); + } + + response = await fetch(`https://api.themoviedb.org/3/tv/${id}/season/${seasonNumber}?api_key=${TMDB_API_KEY}`, { + method: 'GET', + headers: { + accept: 'application/json' + } + }); + const season = await response.json(); + + if (season.success === false) { + throw new Error(season.status_message); + } + + response = await fetch(`https://api.themoviedb.org/3/tv/${id}/season/${seasonNumber}/episode/${episodeNumber}?api_key=${TMDB_API_KEY}`, { + method: 'GET', + headers: { + accept: 'application/json' + } + }); + const episode = await response.json(); + + if (episode.success === false) { + throw new Error(episode.status_message); + } + + return { + type: 'show', + title: series.name, + releaseYear: Number(series.first_air_date.split('-')[0]), // * Is this really what should go here? + tmdbId: id, + episode: { + number: episode.episode_number, + tmdbId: episode.id + }, + season: { + number: season.season_number, + tmdbId: season.id + } + }; +} + +function joinMediaTypes(mediaTypes: string[] | undefined) { + if (mediaTypes) { + const formatted = mediaTypes.map((type: string) => { + type = type[0].toUpperCase() + type.substring(1).toLowerCase(); + return `${type}s`; + }).join(' / '); + + return `(${formatted})`; + } else { + return ''; // * Embed sources pass through here too + } +} + +async function runQuestions() { + const options = { + fetcher: 'node-fetch', + sourceId: '', + tmdbId: '', + type: 'movie', + season: '0', + episode: '0', + url: '' + }; + + const answers = await prompt([ + { + type: 'select', + name: 'fetcher', + message: 'Select a fetcher', + choices: [ + { + + message: 'Native', + name: 'native' + }, + { + message: 'Node fetch', + name: 'node-fetch' + } + ] + }, + { + type: 'select', + name: 'source', + message: 'Select a source', + choices: sources.map(source => ({ + message: `[${source.type.toLocaleUpperCase()}] ${source.name} ${joinMediaTypes(source.mediaTypes)}`.trim(), + name: source.id + })) + } + ]); + + options.fetcher = answers.fetcher; + options.sourceId = answers.source; + + const source = sources.find(source => source.id === answers.source)!; + + if (source.type === 'embed') { + const sourceAnswers = await prompt([ + { + type: 'input', + name: 'url', + message: 'Embed URL' + } + ]); + + options.url = sourceAnswers.url; + } else { + const sourceAnswers = await prompt([ + { + type: 'input', + name: 'id', + message: 'TMDB ID' + }, + { + type: 'select', + name: 'type', + message: 'Media type', + choices: [ + { + message: 'Movie', + name: 'movie' + }, + { + message: 'TV Show', + name: 'show' + } + ] + } + ]); + + options.tmdbId = sourceAnswers.id; + options.type = sourceAnswers.type; + + if (sourceAnswers.type === 'show') { + const seriesAnswers = await prompt([ + { + type: 'input', + name: 'season', + message: 'Season' + }, + { + type: 'input', + name: 'episode', + message: 'Episode' + } + ]); + + options.season = seriesAnswers.season; + options.episode = seriesAnswers.episode; + } + } + + await processOptions(options); +} + +async function runCommandLine() { + program + .option('-f, --fetcher ', 'Fetcher to use. Currently only \'node-fetch\' is suspported', 'node-fetch') + .option('-sid, --source-id ', 'ID for the source to use. Either an embed or provider', '') + .option('-tid, --tmdb-id ', 'TMDB ID for the media to scrape. Only used if source is a provider', '') + .option('-t, --type ', 'Media type. Either \'movie\' or \'show\'. Only used if source is a provider', 'movie') + .option('-s, --season ', 'Season number. Only used if type is \'show\'', '0') + .option('-e, --episode ', 'Episode number. Only used if type is \'show\'', '0') + .option('-u, --url Promise' is not assignable to parameter of type 'typeof fetch'. + // * Property 'isRedirect' is missing in type '(input: RequestInfo | URL, init?: RequestInit | undefined) => Promise' but required in type 'typeof fetch'. ts(2345) + // * index.d.ts(221, 14): 'isRedirect' is declared here. + throw new Error('Fetcher must be node-fetch'); + } + + if (!options.sourceId.trim()) { + throw new Error('Source ID must be provided'); + } + + const source = sources.find(source => source.id === options.sourceId); + + if (!source) { + throw new Error('Invalid source ID. No source found'); + } + + if (source.type === 'embed' && !options.url.trim()) { + throw new Error('Must provide an embed URL for embed sources'); + } + + if (source.type === 'source') { + if (!options.tmdbId.trim()) { + throw new Error('Must provide a TMDB ID for provider sources'); + } + + if (isNaN(Number(options.tmdbId)) || Number(options.tmdbId) < 0) { + throw new Error('TMDB ID must be a number greater than 0'); + } + + if (!options.type.trim()) { + throw new Error('Must provide a type for provider sources'); + } + + if (options.type !== 'movie' && options.type !== 'show') { + throw new Error('Invalid media type. Must be either \'movie\' or \'show\''); + } + + if (options.type === 'show') { + if (!options.season.trim()) { + throw new Error('Must provide a season number for TV shows'); + } + + if (!options.episode.trim()) { + throw new Error('Must provide an episode number for TV shows'); + } + + if (isNaN(Number(options.season)) || Number(options.season) <= 0) { + throw new Error('Season number must be a number greater than 0'); + } + + if (isNaN(Number(options.episode)) || Number(options.episode) <= 0) { + throw new Error('Episode number must be a number greater than 0'); + } + } + } + + const fetcher = makeStandardFetcher(nodeFetch); + const providers = makeProviders({ + fetcher: fetcher, + target: targets.NATIVE + }); + + await runScraper(providers, source, options); +} + +async function runScraper(providers: ProviderControls, source: MetaOutput, options: CommandLineArguments) { + const spinnies = new Spinnies(); + + if (source.type === 'embed') { + spinnies.add('scrape', { text: `Running ${source.name} scraper on ${options.url}` }); + try { + const result = await providers.runEmbedScraper({ + url: options.url, + id: source.id + }); + spinnies.succeed('scrape', { text: 'Done!' }); + console.log(result); + } catch (error) { + let message = 'Unknown error'; + if (error instanceof Error) { + message = error.message; + } + + spinnies.fail('scrape', { text: `ERROR: ${message}` }); + } + } else { + let media; + + if (options.type === 'movie') { + media = await getMovieMediaDetails(options.tmdbId); + } else { + media = await getShowMediaDetails(options.tmdbId, options.season, options.episode); + } + + spinnies.add('scrape', { text: `Running ${source.name} scraper on ${media.title}` }); + try { + const result = await providers.runSourceScraper({ + media: media, + id: source.id + }); + spinnies.succeed('scrape', { text: 'Done!' }); + console.log(result); + } catch (error) { + let message = 'Unknown error'; + if (error instanceof Error) { + message = error.message; + } + + spinnies.fail('scrape', { text: `ERROR: ${message}` }); + } + } +} + +if (process.argv.length === 2) { + runQuestions(); +} else { + runCommandLine(); +} diff --git a/tsconfig.json b/tsconfig.json index 4ea9f0b..a476bbc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "es2021", - "lib": ["es2021"], + "lib": ["es2021", "dom"], "module": "CommonJS", "esModuleInterop": true, "declaration": true,