├── src ├── server │ ├── database.js │ ├── log.js │ ├── config.js │ ├── wsDispatcher.js │ ├── authenticate.js │ ├── index.js │ └── httpDispatcher.js ├── functions │ ├── getAliases.js │ ├── getActressProfile.js │ ├── getNewlyReleased.js │ ├── searchMagnet.js │ ├── getBrief.js │ ├── searchActressByImage.js │ ├── index.js │ ├── utils.js │ ├── searchByActress.js │ └── searchByCode.js ├── ds.js └── sources │ ├── javfull.js │ ├── index.js │ ├── javdisk.js │ ├── avgle.js │ ├── xslist.js │ ├── highporn.js │ ├── javbus.js │ ├── javhdporn.js │ ├── youav.js │ ├── indexav.js │ ├── javlibrary.js │ ├── utils.js │ ├── javdb.js │ ├── javmost.js │ └── warashiAsianPornstarsFr.js ├── Procfile ├── preview.png ├── deployment └── get_version.sh ├── .gitpod.yml ├── test ├── ws.js └── test.js ├── Dockerfile ├── .eslintrc.js ├── app.json ├── .github └── workflows │ ├── coverage.yml │ ├── test.yml │ └── release.yml ├── electron └── index.js ├── bin └── javpy.js ├── package.json ├── .gitignore ├── README.md └── LICENSE /src/server/database.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node ./bin/javpy.js --port $PORT --public=true -------------------------------------------------------------------------------- /preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheodoreKrypton/JavPy/HEAD/preview.png -------------------------------------------------------------------------------- /deployment/get_version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | npm version | grep -oP "javpy: '\K.+?(?=')" -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | image: 2 | wheatcarrier/javpy 3 | 4 | tasks: 5 | - command: /javpy-linux 6 | ports: 7 | - port: 8081 8 | onOpen: open-browser -------------------------------------------------------------------------------- /test/ws.js: -------------------------------------------------------------------------------- 1 | class WS { 2 | constructor() { 3 | this.responses = []; 4 | } 5 | 6 | send(msg) { 7 | this.responses.push(msg); 8 | } 9 | } 10 | 11 | module.exports = WS; 12 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:buster as build 2 | 3 | COPY . /javpy 4 | 5 | RUN cd /javpy && npm install --only=prod --unsafe-perm && npm install -g pkg && pkg -t node14-linux . 6 | 7 | FROM debian:buster-slim 8 | 9 | WORKDIR / 10 | 11 | COPY --from=build /javpy/javpy . 12 | 13 | CMD ["./javpy"] 14 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | node: true, 4 | commonjs: true, 5 | es2020: true, 6 | }, 7 | extends: [ 8 | 'airbnb-base', 9 | ], 10 | parserOptions: { 11 | ecmaVersion: 11, 12 | }, 13 | rules: { 14 | 'no-console': 'off', 15 | 'linebreak-style': 'off', 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "javpy", 3 | "description": "Enjoy driving on a Javascriptive (originally Pythonic) way to Japanese AV!", 4 | "repository": "https://github.com/TheodoreKrypton/JavPy.git", 5 | "logo": "https://i.imgur.com/knHmnhg.png", 6 | "keywords": [ 7 | "react", 8 | "javascript", 9 | "crawler", 10 | "material-ui", 11 | "jav", 12 | "porn", 13 | "japanese-av" 14 | ] 15 | } -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: coverage 2 | 3 | on: [push, pull_request] 4 | 5 | 6 | jobs: 7 | test: 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Set up Node 12 13 | uses: actions/setup-node@v1 14 | with: 15 | node-version: 12 16 | - name: Install dependencies 17 | run: npm install 18 | - name: Coverage 19 | run: npm run coverage 20 | env: 21 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 22 | -------------------------------------------------------------------------------- /src/server/log.js: -------------------------------------------------------------------------------- 1 | const { createLogger, format, transports } = require('winston'); 2 | 3 | const fmt = format.printf(({ 4 | level, 5 | message, 6 | label, 7 | timestamp, 8 | }) => `${timestamp} [${label}] ${level}: ${JSON.stringify(message)}`); 9 | 10 | const logger = createLogger({ 11 | format: format.combine( 12 | format.label({ label: 'javpy' }), 13 | format.timestamp(), 14 | fmt, 15 | ), 16 | transports: [new transports.Console()], 17 | }); 18 | 19 | module.exports = { 20 | logger, 21 | }; 22 | -------------------------------------------------------------------------------- /src/functions/getAliases.js: -------------------------------------------------------------------------------- 1 | const sources = require('../sources'); 2 | const utils = require('./utils'); 3 | 4 | const getAliases = async (ws, reqId, { actress }) => { 5 | try { 6 | const rsp = await sources.warashiAsianPornstarsFr.getAliases(actress); 7 | if (rsp) { 8 | ws.send(JSON.stringify({ response: rsp, reqId })); 9 | } else { 10 | utils.notFound(ws, reqId); 11 | } 12 | } catch (err) { 13 | utils.notFound(ws, reqId); 14 | } 15 | }; 16 | 17 | module.exports = { 18 | getAliases, 19 | }; 20 | -------------------------------------------------------------------------------- /src/functions/getActressProfile.js: -------------------------------------------------------------------------------- 1 | const sources = require('../sources'); 2 | const utils = require('./utils'); 3 | 4 | const getActressProfile = async (ws, reqId, { actress }) => { 5 | try { 6 | const rsp = await sources.warashiAsianPornstarsFr.getActressProfile(actress); 7 | if (rsp) { 8 | ws.send(JSON.stringify({ response: rsp, reqId })); 9 | } else { 10 | utils.notFound(ws, reqId); 11 | } 12 | } catch (err) { 13 | utils.notFound(ws, reqId); 14 | } 15 | }; 16 | 17 | module.exports = { 18 | getActressProfile, 19 | }; 20 | -------------------------------------------------------------------------------- /src/functions/getNewlyReleased.js: -------------------------------------------------------------------------------- 1 | const sources = require('../sources'); 2 | const utils = require('./utils'); 3 | 4 | const getNewlyReleased = async (ws, reqId, { page }) => { 5 | await Promise.allSettled([sources.javlibrary, sources.javmost, sources.javdb].map( 6 | (source) => source.getNewlyReleased(page).then((response) => { 7 | if (response) { 8 | ws.send(JSON.stringify({ response, reqId })); 9 | } 10 | }).catch((err) => { 11 | utils.notFound(ws, reqId); 12 | throw err; 13 | }), 14 | )); 15 | }; 16 | 17 | module.exports = { 18 | getNewlyReleased, 19 | }; 20 | -------------------------------------------------------------------------------- /src/functions/searchMagnet.js: -------------------------------------------------------------------------------- 1 | const sources = require('../sources'); 2 | const utils = require('./utils'); 3 | 4 | const searchMagnet = async (ws, reqId, { code }) => { 5 | let something = false; 6 | await Promise.allSettled([sources.javbus, sources.javdb].map( 7 | (source) => source.searchMagnet(code).then((response) => { 8 | if (!response) { 9 | return; 10 | } 11 | something = true; 12 | ws.send(JSON.stringify({ response, reqId })); 13 | }), 14 | )); 15 | if (!something) { 16 | utils.notFound(ws, reqId); 17 | } 18 | }; 19 | 20 | module.exports = { 21 | searchMagnet, 22 | }; 23 | -------------------------------------------------------------------------------- /src/functions/getBrief.js: -------------------------------------------------------------------------------- 1 | const sources = require('../sources'); 2 | const utils = require('./utils'); 3 | 4 | const getBrief = async (ws, reqId, { code }) => { 5 | let something = false; 6 | await Promise.allSettled([sources.indexav, sources.javlibrary, sources.javdb].map( 7 | (source) => source.getBrief(code).then((response) => { 8 | if (!response) { 9 | return; 10 | } 11 | something = true; 12 | ws.send(JSON.stringify({ response, reqId })); 13 | }), 14 | )); 15 | if (!something) { 16 | utils.notFound(ws, reqId); 17 | } 18 | }; 19 | 20 | module.exports = { 21 | getBrief, 22 | }; 23 | -------------------------------------------------------------------------------- /electron/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | const { app, BrowserWindow } = require('electron'); 3 | const server = require('../src/server'); 4 | 5 | const PORT = 8081; 6 | 7 | function createWindow() { 8 | // Create the browser window. 9 | const win = new BrowserWindow({ 10 | width: 800, 11 | height: 600, 12 | webPreferences: { 13 | nodeIntegration: true, 14 | }, 15 | }); 16 | win.setMenu(null); 17 | // and load the index.html of the app. 18 | win.loadURL(`http://localhost:${PORT}`); 19 | } 20 | 21 | app.whenReady().then(() => { 22 | server.run(8081, createWindow); 23 | }); 24 | -------------------------------------------------------------------------------- /src/functions/searchActressByImage.js: -------------------------------------------------------------------------------- 1 | const sources = require('../sources'); 2 | const utils = require('./utils'); 3 | 4 | const searchActressByImage = async (ws, reqId, { image }) => { 5 | let something = false; 6 | await Promise.allSettled([sources.xslist].map( 7 | (source) => source.searchActressByImage(image).then((response) => { 8 | if (!response) { 9 | return; 10 | } 11 | something = true; 12 | ws.send(JSON.stringify({ response, reqId })); 13 | }), 14 | )); 15 | if (!something) { 16 | utils.notFound(ws, reqId); 17 | } 18 | }; 19 | 20 | module.exports = { 21 | searchActressByImage, 22 | }; 23 | -------------------------------------------------------------------------------- /src/ds.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-classes-per-file */ 2 | class AV { 3 | constructor() { 4 | this.code = null; 5 | this.title = null; 6 | this.video_url = null; 7 | this.preview_img_url = null; 8 | this.actress = []; 9 | this.release_date = null; 10 | } 11 | } 12 | 13 | class Magnet { 14 | constructor() { 15 | this.magnet = null; 16 | this.description = null; 17 | this.peers = null; 18 | } 19 | } 20 | 21 | class Actress { 22 | constructor() { 23 | this.birth_date = null; 24 | this.img = null; 25 | this.height = null; 26 | this.weight = null; 27 | } 28 | } 29 | 30 | module.exports = { 31 | AV, Magnet, Actress, 32 | }; 33 | -------------------------------------------------------------------------------- /src/functions/index.js: -------------------------------------------------------------------------------- 1 | const { searchByActress } = require('./searchByActress'); 2 | const { getNewlyReleased } = require('./getNewlyReleased'); 3 | const { searchByCode } = require('./searchByCode'); 4 | const { searchMagnet } = require('./searchMagnet'); 5 | const { getActressProfile } = require('./getActressProfile'); 6 | const { getAliases } = require('./getAliases'); 7 | const { getBrief } = require('./getBrief'); 8 | const { searchActressByImage } = require('./searchActressByImage'); 9 | 10 | module.exports = { 11 | searchByActress, 12 | getNewlyReleased, 13 | searchByCode, 14 | searchMagnet, 15 | getActressProfile, 16 | getAliases, 17 | getBrief, 18 | searchActressByImage, 19 | }; 20 | -------------------------------------------------------------------------------- /src/sources/javfull.js: -------------------------------------------------------------------------------- 1 | const { JSDOM } = require('jsdom'); 2 | const utils = require('./utils'); 3 | const ds = require('../ds'); 4 | 5 | const requester = utils.requester('https://javfull.net'); 6 | 7 | const searchByCode = async (code) => { 8 | const rsp = await requester.get(`/${code}/`); 9 | const html = rsp.data; 10 | const dom = new JSDOM(html).window.document; 11 | const av = new ds.AV(); 12 | av.title = utils.noexcept(() => dom.querySelector('h1').textContent); 13 | av.code = code; 14 | av.preview_img_url = utils.noexcept(() => dom.querySelector('.iframeplayer').getAttribute('data-bg').match(/url\('(.+?)'\)/)[1]); 15 | av.video_url = `https://javfull.net/${code}/`; 16 | return av; 17 | }; 18 | 19 | module.exports = { 20 | searchByCode, 21 | }; 22 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | 7 | 8 | jobs: 9 | test: 10 | name: ${{ matrix.node }} @ ${{ matrix.platform }} 11 | strategy: 12 | max-parallel: 4 13 | fail-fast: false 14 | matrix: 15 | platform: [ubuntu-latest, macOS-latest, windows-latest] 16 | node: [12, 14] 17 | 18 | 19 | runs-on: ${{ matrix.platform }} 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Set up Node ${{ matrix.node }} on ${{ matrix.platform }} 24 | uses: actions/setup-node@v1 25 | with: 26 | node-version: ${{ matrix.node }} 27 | - name: Install dependencies 28 | run: npm install 29 | - name: Test 30 | run: npm run test 31 | 32 | -------------------------------------------------------------------------------- /src/sources/index.js: -------------------------------------------------------------------------------- 1 | const javmost = require('./javmost'); 2 | const avgle = require('./avgle'); 3 | const warashiAsianPornstarsFr = require('./warashiAsianPornstarsFr'); 4 | const indexav = require('./indexav'); 5 | const youav = require('./youav'); 6 | const highporn = require('./highporn'); 7 | const javhdporn = require('./javhdporn'); 8 | const javbus = require('./javbus'); 9 | const javlibrary = require('./javlibrary'); 10 | const javdb = require('./javdb'); 11 | const xslist = require('./xslist'); 12 | const javfull = require('./javfull'); 13 | 14 | module.exports = { 15 | javmost, 16 | avgle, 17 | warashiAsianPornstarsFr, 18 | indexav, 19 | youav, 20 | highporn, 21 | javhdporn, 22 | javbus, 23 | javlibrary, 24 | javdb, 25 | xslist, 26 | javfull, 27 | }; 28 | -------------------------------------------------------------------------------- /src/sources/javdisk.js: -------------------------------------------------------------------------------- 1 | const { JSDOM } = require('jsdom'); 2 | const utils = require('./utils'); 3 | const ds = require('../ds'); 4 | 5 | const requester = utils.requester('https://javdisk.com'); 6 | 7 | const searchByCode = async (code) => { 8 | const rsp = await requester.get(encodeURI(`/search.html?q=${code}`)); 9 | const dom = new JSDOM(rsp.data).window.document; 10 | const av = new ds.AV(); 11 | av.code = code; 12 | av.title = dom.querySelector('meta[property="og:title"]').content; 13 | if (!utils.titleIncludes(av.title, code)) { 14 | return null; 15 | } 16 | av.video_url = dom.querySelector('meta[property="og:url"]').content; 17 | av.preview_img_url = dom.querySelector('meta[property="og:image"]').content; 18 | return av; 19 | }; 20 | 21 | module.exports = { 22 | searchByCode, 23 | }; 24 | -------------------------------------------------------------------------------- /src/sources/avgle.js: -------------------------------------------------------------------------------- 1 | const Axios = require('axios').default; 2 | const utils = require('./utils'); 3 | const ds = require('../ds'); 4 | 5 | const requester = utils.requester('https://api.avgle.com'); 6 | 7 | const searchByCode = async (code) => { 8 | const rsp = await requester.get(`/v1/search/${code}/0?limit=1`); 9 | const video = rsp.data.response.videos[0]; 10 | 11 | const testVideo = await Axios.get(encodeURI(video.video_url), { maxRedirects: 0 }); 12 | if (testVideo.status === 301) { 13 | return null; 14 | } 15 | 16 | if (utils.titleIncludes(video.title, code)) { 17 | const av = new ds.AV(); 18 | av.title = video.title; 19 | av.video_url = video.video_url; 20 | av.code = code; 21 | av.preview_img_url = video.preview_url; 22 | return av; 23 | } 24 | return null; 25 | }; 26 | 27 | module.exports = { 28 | searchByCode, 29 | }; 30 | -------------------------------------------------------------------------------- /src/server/config.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const os = require('os'); 3 | const path = require('path'); 4 | const packageJson = require('../../package.json'); 5 | 6 | const configDir = `${os.homedir()}${path.sep}.JavPy`; 7 | const configPath = `${configDir}${path.sep}config.json`; 8 | 9 | if (!fs.existsSync(configDir)) { 10 | fs.mkdirSync(configDir, { recursive: true }); 11 | } 12 | 13 | if (!fs.existsSync(configPath)) { 14 | fs.writeFileSync(configPath, JSON.stringify({ 15 | 'ip-whitelist': ['127.0.0.1', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'], 16 | password: '', 17 | proxy: '', 18 | version: packageJson.version, 19 | })); 20 | } 21 | 22 | module.exports = { 23 | config: JSON.parse(fs.readFileSync(configPath)), 24 | set(newCfg) { 25 | fs.writeFileSync(configPath, JSON.stringify(newCfg)); 26 | this.config = JSON.parse(fs.readFileSync(configPath)); 27 | }, 28 | }; 29 | -------------------------------------------------------------------------------- /src/sources/xslist.js: -------------------------------------------------------------------------------- 1 | const FormData = require('form-data'); 2 | const { fs } = require('memfs'); 3 | const { JSDOM } = require('jsdom'); 4 | const utils = require('./utils'); 5 | 6 | const requester = utils.requester('https://xslist.org'); 7 | 8 | const searchActressByImage = async (image) => { 9 | const blob = Buffer.from(image.split(',')[1], 'base64'); 10 | const fileName = `/${Math.random()}`; 11 | fs.writeFileSync(fileName, blob); 12 | const formData = new FormData(); 13 | formData.append('pic', fs.createReadStream(fileName)); 14 | const rsp = await requester.post('/search/pic', formData, { headers: formData.getHeaders() }); 15 | const dom = new JSDOM(rsp.data).window.document; 16 | return [...dom.querySelectorAll('a')].map((a) => { 17 | const tokens = a.text.split('-'); 18 | return tokens.length === 3 ? tokens[1].trim() : tokens[0].trim(); 19 | }); 20 | }; 21 | 22 | module.exports = { 23 | searchActressByImage, 24 | }; 25 | -------------------------------------------------------------------------------- /src/sources/highporn.js: -------------------------------------------------------------------------------- 1 | const { JSDOM } = require('jsdom'); 2 | const utils = require('./utils'); 3 | const ds = require('../ds'); 4 | 5 | const requester = utils.requester('https://highporn.net'); 6 | 7 | const searchByCode = async (code) => { 8 | const rsp = await requester.get(`/search/videos?search_query=${encodeURI(code.toLowerCase())}`); 9 | if (rsp.data.includes('No Videos Found.')) { 10 | return null; 11 | } 12 | const dom = new JSDOM(rsp.data); 13 | const div = dom.window.document.querySelector('.well-sm'); 14 | if (!div) { 15 | return null; 16 | } 17 | const av = new ds.AV(); 18 | av.code = code; 19 | av.preview_img_url = utils.noexcept(() => div.querySelector('img').src); 20 | if (av.preview_img_url && !av.preview_img_url.startsWith('http')) { 21 | av.preview_img_url = `http:${av.preview_img_url}`; 22 | } 23 | av.title = utils.noexcept(() => div.querySelector('.video-title').textContent); 24 | av.video_url = utils.noexcept(() => div.querySelector('a').href); 25 | return av; 26 | }; 27 | 28 | module.exports = { 29 | searchByCode, 30 | }; 31 | -------------------------------------------------------------------------------- /src/functions/utils.js: -------------------------------------------------------------------------------- 1 | const guessLang = (actress) => { 2 | for (let i = 0; i < actress.length; i += 1) { 3 | const charCode = actress.charCodeAt(i); 4 | if (charCode >= 128) { 5 | return 'jp'; 6 | } 7 | } 8 | return 'en'; 9 | }; 10 | 11 | const notFound = (ws, reqId) => { 12 | ws.send(JSON.stringify({ response: 'not found', reqId })); 13 | }; 14 | 15 | const expandSearch = { 16 | regexes: [ 17 | { 18 | regex: new RegExp(/^\d+[-_]\d+$/), 19 | expander: (code) => [code.replace('-', '_'), code.replace('_', '-')], 20 | }, 21 | { 22 | regex: new RegExp(/^RETOMN-\d+$/), 23 | expander: (code) => [code, code.slice(2, code.length)], 24 | }, 25 | { 26 | regex: new RegExp(/\d+_ppv-.+/), 27 | expander: (code) => [code, code.replace(/\d+_ppv-/, '')], 28 | }, 29 | ], 30 | 31 | getExpander(code) { 32 | for (let i = 0; i < this.regexes.length; i += 1) { 33 | if (code.match(this.regexes[i].regex)) { 34 | return this.regexes[i].expander; 35 | } 36 | } 37 | return null; 38 | }, 39 | }; 40 | 41 | module.exports = { 42 | guessLang, 43 | notFound, 44 | expandSearch, 45 | }; 46 | -------------------------------------------------------------------------------- /src/server/wsDispatcher.js: -------------------------------------------------------------------------------- 1 | const functions = require('../functions'); 2 | const auth = require('./authenticate'); 3 | const { logger } = require('./log'); 4 | 5 | const dispatch = (ws, msg) => { 6 | if (!msg.message || !msg.message.args || !msg.message.args.image) { 7 | logger.info({ ws: msg }); 8 | } 9 | 10 | try { 11 | const { message, reqId, userpass } = msg; 12 | 13 | if (!auth.checkToken(userpass)) { 14 | return; 15 | } 16 | 17 | const { api, args } = message; 18 | 19 | const routes = { 20 | search_by_code: functions.searchByCode, 21 | get_newly_released: functions.getNewlyReleased, 22 | search_by_actress: functions.searchByActress, 23 | search_magnet_by_code: functions.searchMagnet, 24 | get_aliases: functions.getAliases, 25 | get_actress_profile: functions.getActressProfile, 26 | get_brief: functions.getBrief, 27 | search_actress_by_image: functions.searchActressByImage, 28 | }; 29 | 30 | if (routes[api]) { 31 | routes[api](ws, reqId, args); 32 | } 33 | } catch (err) { 34 | logger.error(err.message); 35 | } 36 | }; 37 | 38 | module.exports = { 39 | dispatch, 40 | }; 41 | -------------------------------------------------------------------------------- /bin/javpy.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const yargs = require('yargs'); 4 | const { exec } = require('child_process'); 5 | const os = require('os'); 6 | const auth = require('../src/server/authenticate'); 7 | const server = require('../src/server'); 8 | 9 | const { argv } = yargs 10 | .option('port', { 11 | alias: 'p', 12 | description: 'listen to port', 13 | default: 8081, 14 | type: 'int', 15 | }) 16 | .option('browser', { 17 | description: 'open browser automatically', 18 | default: true, 19 | type: 'bool', 20 | }) 21 | .option('public', { 22 | description: 'public mode does not require authentications', 23 | default: false, 24 | type: 'bool', 25 | }) 26 | .help() 27 | .alias('help', 'h'); 28 | 29 | const openBrowser = () => { 30 | const url = `http://localhost:${argv.port}`; 31 | const platform = os.platform(); 32 | if (platform.includes('win32')) { 33 | exec(`start "" "${url}"`); 34 | } else if (platform.includes('linux')) { 35 | exec(`xdg-open ${url}`); 36 | } else { 37 | exec(`open ${url}`); 38 | } 39 | }; 40 | 41 | if (argv.public === 'true') { 42 | auth.setPublic(); 43 | } 44 | 45 | server.run(argv.port, argv.browser === 'false' ? undefined : openBrowser); 46 | -------------------------------------------------------------------------------- /src/functions/searchByActress.js: -------------------------------------------------------------------------------- 1 | const sources = require('../sources'); 2 | const utils = require('./utils'); 3 | 4 | const searchByActress = async (ws, reqId, { actress }) => { 5 | const lang = utils.guessLang(actress); 6 | if (lang === 'en') { 7 | try { 8 | // eslint-disable-next-line no-param-reassign 9 | actress = await sources.warashiAsianPornstarsFr.translate2Jp(actress); 10 | } catch (err) { 11 | console.error(err.message); 12 | } 13 | if (!actress) { 14 | utils.notFound(ws, reqId); 15 | } 16 | } 17 | 18 | let something = false; 19 | await Promise.allSettled([ 20 | sources.indexav, 21 | sources.warashiAsianPornstarsFr, 22 | ].map((source) => source.searchByActress(actress).then(async (rsp) => { 23 | if (!rsp) { 24 | return; 25 | } 26 | if (rsp.length > 0 && rsp[0] instanceof Promise) { 27 | await Promise.allSettled(rsp.map((r) => r.then((response) => { 28 | if (response) { 29 | something = true; 30 | ws.send(JSON.stringify({ response, reqId })); 31 | } 32 | }))); 33 | } else { 34 | something = true; 35 | ws.send(JSON.stringify({ response: rsp, reqId })); 36 | } 37 | }))); 38 | if (!something) { 39 | utils.notFound(ws, reqId); 40 | } 41 | }; 42 | 43 | module.exports = { 44 | searchByActress, 45 | }; 46 | -------------------------------------------------------------------------------- /src/server/authenticate.js: -------------------------------------------------------------------------------- 1 | const ipRangeCheck = require('ip-range-check'); 2 | const sha256 = require('js-sha256'); 3 | const config = require('./config'); 4 | 5 | const [isPublic, setPublic] = (() => { 6 | let publicMode = false; 7 | return [() => publicMode, () => { publicMode = true; }]; 8 | })(); 9 | 10 | const [hasToken, addToken] = (() => { 11 | const tokens = new Set(); 12 | return [(token) => tokens.has(token), (token) => tokens.add(token)]; 13 | })(); 14 | 15 | const hashedPassword = (() => { 16 | if (config.config['hashed-password']) { 17 | return config.config['hashed-password']; 18 | } 19 | return sha256.sha256(config.config.password); 20 | })(); 21 | 22 | const genToken = (ip, password) => sha256.sha256(`${ip}/${password}/${new Date().time}`).slice(0, 24); 23 | const checkPassword = (password) => hashedPassword === password; 24 | const checkIP = (ip) => isPublic() || ipRangeCheck(ip, config.config['ip-whitelist']); 25 | const checkToken = (token) => isPublic() || hasToken(token); 26 | 27 | const authenticate = (ip, password) => { 28 | if ((isPublic() && password === null) || (checkIP(ip) && checkPassword(password))) { 29 | const token = genToken(ip, password); 30 | addToken(token); 31 | return token; 32 | } 33 | return null; 34 | }; 35 | 36 | module.exports = { 37 | authenticate, 38 | checkToken, 39 | setPublic, 40 | }; 41 | -------------------------------------------------------------------------------- /src/sources/javbus.js: -------------------------------------------------------------------------------- 1 | const { JSDOM } = require('jsdom'); 2 | const utils = require('./utils'); 3 | const ds = require('../ds'); 4 | 5 | const requester = utils.requester('https://www.javbus.com'); 6 | 7 | const regexes = { 8 | gid: new RegExp(/var gid = (\d+?);/), 9 | uc: new RegExp(/var uc = (\d+?);/), 10 | img: new RegExp(/var img = '(.+?)';/), 11 | }; 12 | 13 | const searchMagnet = async (code) => { 14 | const rsp = await requester.get(`/${code}`); 15 | const gid = encodeURI(rsp.data.match(regexes.gid)[1]); 16 | const uc = encodeURI(rsp.data.match(regexes.uc)[1]); 17 | const img = encodeURI(rsp.data.match(regexes.img)[1]); 18 | const queryString = `gid=${gid}&lang=zh&img=${img}&uc=${uc}`; 19 | const headers = { Referer: `https://www.javbus.com/${code}` }; 20 | const rsp2 = await requester.get(`/ajax/uncledatoolsbyajax.php?${queryString}`, { headers }); 21 | const dom = new JSDOM(`