├── .dockerignore ├── .eslintrc.json ├── .gitignore ├── Dockerfile ├── README.md ├── dist ├── HttpClient.js ├── HttpClient.js.map ├── PornClient.js ├── PornClient.js.map ├── adapters │ ├── BaseAdapter.js │ ├── BaseAdapter.js.map │ ├── Chaturbate.js │ ├── Chaturbate.js.map │ ├── EPorner.js │ ├── EPorner.js.map │ ├── HubTrafficAdapter.js │ ├── HubTrafficAdapter.js.map │ ├── PornCom.js │ ├── PornCom.js.map │ ├── PornHub.js │ ├── PornHub.js.map │ ├── RedTube.js │ ├── RedTube.js.map │ ├── SpankWire.js │ ├── SpankWire.js.map │ ├── YouPorn.js │ └── YouPorn.js.map ├── index.js └── index.js.map ├── package.json ├── src ├── HttpClient.js ├── PornClient.js ├── adapters │ ├── BaseAdapter.js │ ├── Chaturbate.js │ ├── EPorner.js │ ├── HubTrafficAdapter.js │ ├── PornCom.js │ ├── PornHub.js │ ├── RedTube.js │ ├── SpankWire.js │ └── YouPorn.js └── index.js ├── static ├── bg.jpg ├── logo.png └── screenshot_discover.jpg ├── tests ├── adapters │ ├── Chaturbate │ │ ├── Chaturbate.test.js │ │ ├── itemPage.html │ │ └── listPage.html │ ├── EPorner │ │ ├── EPorner.test.js │ │ ├── apiResponse.xml │ │ └── moviePage.html │ ├── PornCom │ │ ├── PornCom.test.js │ │ ├── apiResponse.xml │ │ └── embedPage.xml │ ├── PornHub │ │ ├── PornHub.test.js │ │ └── embeddedMoviePage.html │ ├── RedTube │ │ ├── RedTube.test.js │ │ └── embeddedVideoPage.html │ ├── SpankWire │ │ ├── SpankWire.test.js │ │ └── embeddedMoviePage.html │ ├── YouPorn │ │ ├── YouPorn.test.js │ │ └── embeddedMoviePage.html │ └── testAdapter.js └── index.test.js └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | ./** 2 | !dist/** 3 | !static/** 4 | !package.json 5 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint:recommended", 4 | "plugin:import/errors", 5 | "plugin:jest/recommended" 6 | ], 7 | "parser": "babel-eslint", 8 | "parserOptions": { 9 | "ecmaVersion": 7, 10 | "sourceType": "module", 11 | "codeFrame": false 12 | }, 13 | "env": { 14 | "es6": true, 15 | "node": true, 16 | "jest": true 17 | }, 18 | "rules": { 19 | "no-constant-condition": "off", 20 | "no-empty": ["error", { 21 | "allowEmptyCatch": true 22 | }], 23 | "array-callback-return": "error", 24 | "curly": "error", 25 | "dot-location": ["error", "property"], 26 | "eqeqeq": "error", 27 | "no-alert": "error", 28 | "no-caller": "error", 29 | "no-eval": "error", 30 | "no-extend-native": "error", 31 | "no-floating-decimal": "error", 32 | "no-implicit-globals": "error", 33 | "no-implicit-coercion": "error", 34 | "no-implied-eval": "error", 35 | "no-iterator": "error", 36 | "no-loop-func": "error", 37 | "no-multi-spaces": "error", 38 | "no-proto": "error", 39 | "no-return-assign": "error", 40 | "no-return-await": "error", 41 | "no-self-compare": "error", 42 | "no-sequences": "error", 43 | "no-throw-literal": "error", 44 | "no-useless-call": "error", 45 | "no-useless-concat": "error", 46 | "no-useless-escape": "error", 47 | "no-useless-return": "error", 48 | "no-void": "error", 49 | "no-warning-comments": "warn", 50 | "no-with": "error", 51 | "prefer-promise-reject-errors": "error", 52 | "wrap-iife": "error", 53 | "yoda": "error", 54 | "no-undef-init": "error", 55 | "no-use-before-define": [ 56 | "error", 57 | { 58 | "functions": false, 59 | "classes": false, 60 | "variables": true 61 | } 62 | ], 63 | "global-require": "error", 64 | "array-bracket-spacing": "error", 65 | "block-spacing": "error", 66 | "brace-style": "error", 67 | "camelcase": "error", 68 | "comma-dangle": ["error", { 69 | "arrays": "always-multiline", 70 | "objects": "always-multiline", 71 | "imports": "never", 72 | "exports": "never", 73 | "functions": "never" 74 | }], 75 | "comma-spacing": "error", 76 | "comma-style": "error", 77 | "computed-property-spacing": "error", 78 | "consistent-this": ["error", "self"], 79 | "eol-last": "error", 80 | "func-call-spacing": "error", 81 | "indent": ["error", 2, { 82 | "SwitchCase": 1 83 | }], 84 | "jsx-quotes": "error", 85 | "key-spacing": "error", 86 | "keyword-spacing": "error", 87 | "linebreak-style": "error", 88 | "lines-around-directive": "error", 89 | "max-depth": ["error", 4], 90 | "max-len": ["error", { 91 | "code": 80, 92 | "ignoreStrings": true 93 | }], 94 | "max-params": ["error", 5], 95 | "new-cap": "error", 96 | "new-parens": "error", 97 | "no-lonely-if": "error", 98 | "no-mixed-operators": "error", 99 | "no-multi-assign": "error", 100 | "no-multiple-empty-lines": ["error", { 101 | "max": 2, 102 | "maxEOF": 0 103 | }], 104 | "no-nested-ternary": "error", 105 | "no-tabs": "error", 106 | "no-trailing-spaces": "error", 107 | "no-unneeded-ternary": "error", 108 | "no-whitespace-before-property": "error", 109 | "object-curly-spacing": ["error", "always"], 110 | "one-var-declaration-per-line": "error", 111 | "operator-linebreak": ["error", "after"], 112 | "quote-props": ["error", "as-needed"], 113 | "quotes": ["error", "single"], 114 | "semi": ["error", "never"], 115 | "space-before-blocks": ["error", "always"], 116 | "space-before-function-paren": ["error", { 117 | "anonymous": "never", 118 | "named": "never", 119 | "asyncArrow": "always" 120 | }], 121 | "space-in-parens": "error", 122 | "space-infix-ops": "error", 123 | "space-unary-ops": ["error", { 124 | "words": true, 125 | "nonwords": false 126 | }], 127 | "spaced-comment": "error", 128 | "template-tag-spacing": "error", 129 | "unicode-bom": "error", 130 | "arrow-parens": "error", 131 | "arrow-spacing": "error", 132 | "generator-star-spacing": ["error", "after"], 133 | "no-class-assign": "error", 134 | "no-confusing-arrow": "error", 135 | "no-duplicate-imports": "error", 136 | "no-new-symbol": "error", 137 | "no-useless-computed-key": "error", 138 | "no-useless-constructor": "error", 139 | "no-var": "error", 140 | "prefer-arrow-callback": ["error", { 141 | "allowNamedFunctions": true 142 | }], 143 | "prefer-rest-params": "error", 144 | "prefer-template": "error", 145 | "rest-spread-spacing": "error", 146 | "symbol-description": "error", 147 | "template-curly-spacing": "error", 148 | "yield-star-spacing": "error", 149 | "import/newline-after-import": ["error", { 150 | "count": 2 151 | }], 152 | "import/order": ["error"] 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | wip 2 | node_modules 3 | .DS_Store 4 | Thumbs.db 5 | .idea 6 | .vs 7 | .vscode 8 | *.log 9 | .npmrc 10 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:alpine 2 | 3 | WORKDIR /var/stremio_addon 4 | # The exact files included are controlled by .dockerignore 5 | COPY . . 6 | RUN npm install --only=prod --no-package-lock 7 | 8 | CMD node dist/index.js 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 |
6 | Time to unsheathe your sword! 7 |
8 | 9 | This is a [Stremio](https://www.stremio.com/) addon that provides porn content from various websites: 10 | 11 | - __Videos__ _(Movies)_: PornHub, RedTube, YouPorn, SpankWire and Porn.com 12 | - __Webcam streams__ _(TV Channels)_: Chaturbate 13 | 14 | 15 | ## Features 16 | 17 | - Adds a dedicated tab in Discover for each website 18 | - Works in Stremio v4 and v3.6 19 | - Supports Docker out of the box 20 | - Caches results in memory or Redis 21 | - Limits the number of concurrent requests to avoid overloading the sites 22 | - Supports HTTPS proxy 23 | - Configurable via environment variables 24 | - Prints a nicely formatted status message when run 25 | - The logo is dope 🗡💖 26 | 27 | 28 | ## Running 29 | 30 | The addon is a web server that fetches video streams from the porn sites in response to requests from Stremio clients. It uses environment variables for configuration and includes a handful of npm scripts to run with or without Docker. 31 | 32 | To install and quickly start the addon, do: 33 | 34 | ```bash 35 | git clone https://github.com/naughty-doge/stremio-porn 36 | cd stremio-porn 37 | yarn # or `npm install` 38 | yarn start # or `npm start` 39 | ``` 40 | 41 | By default the server starts on `localhost:80` in development mode and doesn't announce itself to the Stremio addon tracker. To add the addon to Stremio, open its endpoint in the browser and click the Install button, or enter the URL in the app's Addons section. 42 | 43 | In order for the addon to work publicly, the following environment variables must be set: 44 | - `NODE_ENV` to `production` 45 | - `STREMIO_PORN_ENDPOINT` to a public URL of the server 46 | - `STREMIO_PORN_ID` to a non-default value 47 | 48 | Note: since this addon scrapes pages, it is recommended to run it behind a proxy and use Redis caching. 49 | 50 | 51 | ## Development 52 | 53 | The code is written in ES7 and then transpiled with Babel. It is covered by a suite of Jest tests, and the staged files are automatically linted with ESLint. The transpiled files are included in the repository: this makes for quicker start and eases deployment to different environments such as Docker and Heroku. 54 | 55 | 56 | ## npm scripts 57 | 58 | Each of these scripts can be used with `yarn