├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ └── feature-request.yml └── workflows │ ├── pyinstaller.yml │ ├── test-basic-funcs.yml │ └── test-web-funcs.yml ├── .gitignore ├── JavSP.py ├── LICENSE ├── README.md ├── core ├── avid.py ├── baidu_aip.py ├── chromium.py ├── config.ini ├── config.py ├── datatype.py ├── file.py ├── func.py ├── image.py ├── lib.py ├── nfo.py └── print.py ├── data ├── genre_avsox.csv ├── genre_javbus.csv ├── genre_javdb.csv └── genre_javlib.csv ├── image ├── JavSP.ico ├── javsp_logo.png └── screenshot.png ├── make.bat ├── make ├── gen_ver_hook.py └── windows.spec ├── requirements.txt ├── tools ├── airav_search.py ├── call_crawler.py └── check_genre.py ├── unittest ├── conftest.py ├── data │ ├── 012717_472 (airav).json │ ├── 080719-976 (airav).json │ ├── 082713-417 (airav).json │ ├── 082713-417 (avsox).json │ ├── 082713-417 (javbus).json │ ├── 082713-417 (javdb).json │ ├── 130614-KEIKO (avsox).json │ ├── 130614-KEIKO (javbus).json │ ├── 130614-KEIKO (javdb).json │ ├── 145dmn000007 (fanza).json │ ├── 1stars931r (fanza).json │ ├── 259LUXU-593 (avwiki).json │ ├── 300MIUM-1001 (avwiki).json │ ├── 62knbm009 (fanza).json │ ├── ABC-000 (prestige).json │ ├── ABP-647 (airav).json │ ├── ABP-647 (mgstage).json │ ├── ABP-647 (prestige).json │ ├── AGEMIX-175 (javbus).json │ ├── AGEMIX-175 (javdb).json │ ├── AGEMIX-175 (javlib).json │ ├── AbW-001 (javlib).json │ ├── CND-037 (airav).json │ ├── DCV-137 (jav321).json │ ├── FC2-10000 (fc2).json │ ├── FC2-10000 (javdb).json │ ├── FC2-10000 (javmenu).json │ ├── FC2-1879420 (msin).json │ ├── FC2-1899973 (javmenu).json │ ├── FC2-238629 (avsox).json │ ├── FC2-238629 (msin).json │ ├── FC2-2764073 (javmenu).json │ ├── FC2-3189680 (javdb).json │ ├── FC2-626157 (msin).json │ ├── FC2-718323 (avsox).json │ ├── FC2-718323 (fc2).json │ ├── FC2-718323 (javmenu).json │ ├── FC2-718323 (msin).json │ ├── FC2-985469 (avsox).json │ ├── FC2-985469 (javdb).json │ ├── FC2-985469 (msin).json │ ├── GANA-2156 (javbus).json │ ├── GETCHU-4016932 (dl_getchu).json │ ├── GETCHU-4053720 (dl_getchu).json │ ├── HRV-045 (mgstage).json │ ├── ION-020 (javdb).json │ ├── IPX-177 (airav).json │ ├── IPX-177 (jav321).json │ ├── IPX-177 (javbus).json │ ├── IPX-177 (javdb).json │ ├── IPX-177 (javlib).json │ ├── IPX-177 (javmenu).json │ ├── IPZ-037 (javbus).json │ ├── KING-048 (javbus).json │ ├── KQBD-089 (msin).json │ ├── MTF-020 (javbus).json │ ├── NANP-030 (javbus).json │ ├── OPCYN-174 (jav321).json │ ├── RED-096 (airav).json │ ├── SCUTE-1177 (jav321).json │ ├── SIRO-3093 (mgstage).json │ ├── SIRO-4718 (mgstage).json │ ├── SKY-247 (jav321).json │ ├── SKYHD-012 (airav).json │ ├── SQTE-148 (airav).json │ ├── SQTE-148 (javbus).json │ ├── SSNI-589 (javlib).json │ ├── STAR-299 (airav).json │ ├── STAR-676 (javbus).json │ ├── STARS-213 (javlib).json │ ├── STARS-256 (javdb).json │ ├── d_aisoft3356 (fanza).json │ ├── gyutto-266923 (gyutto).json │ ├── hjmo00214 (fanza).json │ ├── hjmo00214 (msin).json │ ├── ipx-001 (javlib).json │ ├── midv-001 (javdb).json │ ├── midv-001 (javlib).json │ ├── parathd03639 (fanza).json │ └── sqte00300 (fanza).json ├── test_avid.py ├── test_crawlers.py ├── test_exe.py ├── test_file.py ├── test_func.py ├── test_lib.py ├── test_proxyfree.py └── testdata_avid.txt ├── web ├── airav.py ├── avsox.py ├── avwiki.py ├── base.py ├── dl_getchu.py ├── exceptions.py ├── fanza.py ├── fc2.py ├── fc2fan.py ├── gyutto.py ├── jav321.py ├── javbus.py ├── javdb.py ├── javlib.py ├── javmenu.py ├── mgstage.py ├── msin.py ├── prestige.py ├── proxyfree.py └── translate.py └── webui ├── Dockerfile ├── config.ini ├── docker.yml └── scraper_setting.py /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | name: 报告问题 (Bug) 2 | description: 报告你遇到的问题 (Bug) 3 | title: 用一句话描述这个问题 4 | labels: ["bug", "triage"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | 为了方便复现和排查问题,请按照下面的提示补充问题详情 10 | - type: textarea 11 | id: what-happened 12 | attributes: 13 | label: 问题详情 14 | description: 描述这个bug的出现场景、现象等 15 | placeholder: | 16 | - 如果这个问题只在使用特定的配置时出现,请附上你配置文件中的相关项。 17 | - 如果这个问题只在部分影片上出现,必须说明这些影片的完整文件名或者番号。 18 | (假设某些影片标题中的特殊字符导致整理失败,而你既不提供影片番号也不提供日志的话,我是无法排查和修复的) 19 | validations: 20 | required: true 21 | - type: dropdown 22 | id: execute-method 23 | attributes: 24 | label: 运行方式 25 | description: 你运行的是打包后的exe程序吗? 26 | options: 27 | - 我运行的是打包后的exe程序 28 | - 我是从源代码运行的 29 | validations: 30 | required: true 31 | - type: dropdown 32 | id: proxy 33 | attributes: 34 | label: 代理 35 | description: 是否启用了网络代理? 36 | options: 37 | - 是 38 | - 否 39 | validations: 40 | required: true 41 | - type: textarea 42 | id: log 43 | attributes: 44 | label: 日志 45 | description: 请将软件所在目录下的日志文件`JavSP.log`上传到这里 46 | placeholder: 激活此输入框后,你可以直接将文件拖动到此输入框或者点击下面的“Attach files by dragging & dropping, selecting or pasting them”上传文件 47 | - type: textarea 48 | id: screenshot 49 | attributes: 50 | label: 运行截图(可选) 51 | description: 添加截图有助于定位和排查问题 52 | placeholder: 激活此输入框后,你可以按“Ctrl+V”快速上传剪贴板中的截图,或者参考上传日志的方法上传图片文件 53 | - type: checkboxes 54 | id: promise 55 | attributes: 56 | label: 提交须知 57 | description: 提交问题前,请确认你使用的是[最新版本](https://github.com/Yuukiy/JavSP/releases/latest)并且已经阅读过[Wiki帮助文档](https://github.com/Yuukiy/JavSP/wiki) 58 | options: 59 | - label: 我确认使用的是最新版本并且阅读过Wiki帮助文档 60 | required: true 61 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: 功能建议和新功能开发请求 2 | description: 功能建议和新功能开发请求 3 | title: 用一句话描述这个问题 4 | labels: ["feature", "triage"] 5 | body: 6 | - type: textarea 7 | id: what-you-want 8 | attributes: 9 | label: 功能建议 10 | placeholder: | 11 | 请在这里描述你对现有功能的建议或者希望添加的新功能。 12 | 如果你对功能的实现方案已经有初步设想,也欢迎一并说明。 13 | validations: 14 | required: true 15 | - type: checkboxes 16 | id: promise 17 | attributes: 18 | label: 提交须知 19 | description: 提交问题前,请确认你使用的是[最新版本](https://github.com/Yuukiy/JavSP/releases/latest)并且已经阅读过[Wiki帮助文档](https://github.com/Yuukiy/JavSP/wiki) 20 | options: 21 | - label: 我确认使用的是最新版本并且已经阅读过Wiki帮助文档 22 | required: true 23 | -------------------------------------------------------------------------------- /.github/workflows/pyinstaller.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: PyInstaller 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events 8 | push: 9 | branches: 10 | - master 11 | - premerge 12 | pull_request: 13 | branches: 14 | - master 15 | - premerge 16 | 17 | # Allows you to run this workflow manually from the Actions tab 18 | workflow_dispatch: 19 | 20 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 21 | jobs: 22 | # This workflow contains a single job called "build" 23 | build: 24 | # The type of runner that the job will run on 25 | runs-on: windows-latest 26 | env: 27 | PYTEST_ADDOPTS: "-rA --color=yes --tb=long --showlocals" 28 | 29 | # Steps represent a sequence of tasks that will be executed as part of the job 30 | steps: 31 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 32 | - uses: actions/checkout@v2 33 | 34 | - name: Fetch tags 35 | run: git fetch --prune --unshallow --tags 36 | 37 | - name: Setup Python 3.8 38 | uses: actions/setup-python@v2 39 | with: 40 | python-version: 3.8 41 | 42 | - name: Install dependencies 43 | run: | 44 | python -m pip install --upgrade pip 45 | python -m pip install -r requirements.txt 46 | python -m pip install pyinstaller 47 | 48 | - name: Build with PyInstaller for windows 49 | run: cmd.exe /c 'make.bat' 50 | 51 | - name: Install pytest 52 | run: | 53 | python -m pip install pytest 54 | 55 | - name: Switch code page 56 | run: | 57 | chcp 65001 58 | 59 | - name: Test JavSP.exe 60 | run: pytest unittest/test_exe.py 61 | 62 | - name: Set VERSION variable for windows 63 | run: | 64 | echo "VERSION=$(python make\gen_ver_hook.py)" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append 65 | 66 | - name: Upload build artifact 67 | uses: actions/upload-artifact@v1 68 | with: 69 | name: JavSP-${{ env.VERSION }}-Windows-amd64 70 | path: dist/JavSP.exe 71 | -------------------------------------------------------------------------------- /.github/workflows/test-basic-funcs.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: "Unit Test: basic functions" 5 | 6 | on: 7 | push: 8 | branches: 9 | - master 10 | - premerge 11 | pull_request: 12 | branches: 13 | - master 14 | - premerge 15 | # Allows you to run this workflow manually from the Actions tab 16 | workflow_dispatch: 17 | 18 | jobs: 19 | test-basic-funcs: 20 | 21 | runs-on: windows-latest 22 | env: 23 | PYTHONIOENCODING: "utf-8" 24 | PYTEST_ADDOPTS: "-rA --color=yes --tb=long --showlocals" 25 | 26 | steps: 27 | - uses: actions/checkout@v2 28 | - name: Set up Python 3.8 29 | uses: actions/setup-python@v2 30 | with: 31 | python-version: 3.8 32 | - name: Install dependencies 33 | run: | 34 | python -m pip install --upgrade pip 35 | python -m pip install flake8 pytest 36 | python -m pip install -r requirements.txt 37 | - name: Lint with flake8 38 | run: | 39 | # stop the build if there are Python syntax errors or undefined names 40 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 41 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 42 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 43 | - name: Switch code page 44 | run: | 45 | chcp 65001 46 | - name: Test avid.py 47 | run: | 48 | pytest unittest/test_avid.py 49 | - name: Test file.py 50 | run: | 51 | pytest unittest/test_file.py 52 | - name: Test func.py 53 | run: | 54 | pytest unittest/test_func.py 55 | - name: Upload log as artifact 56 | uses: actions/upload-artifact@v1 57 | if: ${{ always() }} 58 | with: 59 | name: JavSP-basic-funcs.log 60 | path: JavSP.log 61 | -------------------------------------------------------------------------------- /.github/workflows/test-web-funcs.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: "Unit Test: web-based functions" 5 | 6 | on: 7 | push: 8 | branches: 9 | - master 10 | - premerge 11 | pull_request: 12 | branches: 13 | - master 14 | - premerge 15 | schedule: 16 | # Web crawlers might be outdated because of website updating, so run periodically to follow the changes 17 | - cron: '9 9 * * 3,6,0' 18 | # Allows you to run this workflow manually from the Actions tab 19 | workflow_dispatch: 20 | 21 | jobs: 22 | test-web-crawlers: 23 | 24 | runs-on: windows-latest 25 | env: 26 | PYTHONIOENCODING: "utf-8" 27 | PYTEST_ADDOPTS: "-rA --color=yes --tb=long --showlocals" 28 | 29 | steps: 30 | - uses: actions/checkout@v2 31 | - name: Set up Python 3.8 32 | uses: actions/setup-python@v2 33 | with: 34 | python-version: 3.8 35 | - name: Install dependencies 36 | run: | 37 | python -m pip install --upgrade pip 38 | python -m pip install pytest 39 | python -m pip install -r requirements.txt 40 | - name: Switch code page 41 | run: | 42 | chcp 65001 43 | - name: Test proxyfree.py 44 | run: | 45 | pytest unittest/test_proxyfree.py 46 | - name: Test web crawlers 47 | run: | 48 | pytest unittest/test_crawlers.py 49 | - name: Upload log as artifact 50 | uses: actions/upload-artifact@v1 51 | if: ${{ always() }} 52 | with: 53 | name: JavSP-web-funcs.log 54 | path: JavSP.log 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | temp 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # hook script generated for pyinstaller 132 | ver_hook.py 133 | # cached data 134 | *.cache.json 135 | 136 | .idea 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![JavSP](https://github.com/tetato/JavSP-Docker/blob/docker/image/javsp_logo.png) 2 | 3 | # JavSP Docker WebUI 4 | 5 | Demo如下: 6 | ![JavSP](https://github.com/tetato/JavSP-Docker/blob/docker/image/screenshot.png) 7 | 8 | 非专业程序员,只是自己用用,有bug可以反馈,不保证解决 9 | 10 | 11 | **汇总多站点数据的AV元数据刮削器** 12 | 13 | 提取影片文件名中的番号信息,自动抓取并汇总多个站点数据的 AV 元数据,按照指定的规则分类整理影片文件,并创建供 Emby、Jellyfin、Kodi 等软件使用的元数据文件 14 | 15 | **i18n**: This project currently supports only Chinese. However, if you're willing, you can [vote here](https://github.com/Yuukiy/JavSP/discussions/157) for the language you'd like to see added 16 | 17 | ![License](https://img.shields.io/github/license/Yuukiy/JavSP) 18 | [![LICENSE](https://img.shields.io/badge/license-Anti%20996-blue.svg)](https://github.com/996icu/996.ICU/blob/master/LICENSE) 19 | ![Python 3.8](https://img.shields.io/badge/python-3.8-green.svg) 20 | [![Crawlers test](https://img.shields.io/github/actions/workflow/status/Yuukiy/JavSP/test-web-funcs.yml?label=crawlers%20test)](https://github.com/Yuukiy/JavSP/actions/workflows/test-web-funcs.yml) 21 | [![Latest release](https://img.shields.io/github/v/release/Yuukiy/JavSP)](https://github.com/Yuukiy/JavSP/releases/latest) 22 | [![996.icu](https://img.shields.io/badge/link-996.icu-red.svg)](https://996.icu) 23 | 24 | ## 功能特点 25 | 26 | 下面这些是一些已实现或待实现的功能,在逐渐实现和完善,如果想到新的功能点也会加进来。 27 | 28 | - [x] 自动识别影片番号 29 | - [x] 支持处理影片分片 30 | - [x] 汇总多个站点的数据生成NFO数据文件 31 | - [x] 每天自动对站点抓取器进行测试 32 | - [x] 多线程并行抓取 33 | - [x] 下载高清封面 34 | - [x] 基于AI人体分析裁剪素人等非常规封面的海报 35 | - [x] 自动检查和更新新版本 36 | - [x] 翻译标题和剧情简介 37 | - [ ] 匹配本地字幕 38 | - [ ] 使用小缩略图创建文件夹封面 39 | - [ ] 保持不同站点间 genre 分类的统一 40 | - [ ] 不同的运行模式(抓取数据+整理,仅抓取数据) 41 | - [ ] 可选:所有站点均抓取失败时由人工介入 42 | 43 | ## 安装 44 | 45 | - 想要快速上手? 46 | 47 | 前往[软件发布页](https://github.com/Yuukiy/JavSP/releases/latest)下载最新版本的软件,无需安装额外工具,开箱即用 48 | 49 | - 更喜欢源代码? 50 | 51 | 请确保已安装 Python (此项目以 Python 3.8 开发) 52 | ``` 53 | git clone https://github.com/Yuukiy/JavSP.git 54 | cd JavSP 55 | pip install -r requirements.txt 56 | python JavSP.py 57 | ``` 58 | 59 | ## 使用 60 | 61 | 软件开箱即用,首次运行时会在软件目录下生成默认的配置文件 ```config.ini```。如果想让软件更符合你的使用需求,也许你需要更改配置文件: 62 | 63 | > 以任意文本编辑器打开 ```config.ini```,根据各个配置项的说明选择你需要的配置即可。 64 | 65 | 此外软件也支持从命令行指定运行参数(命令行参数的优先级高于配置文件)。运行 ```JavSP -h``` 查看支持的参数列表 66 | 67 | 更详细的使用说明请前往 [JavSP Wiki](https://github.com/Yuukiy/JavSP/wiki) 查看 68 | 69 | 如果使用的时候遇到问题也欢迎给我反馈😊 70 | 71 | ## 问题反馈 72 | 73 | 如果使用中遇到了 Bug,请[前往 Issue 区反馈](https://github.com/Yuukiy/JavSP/issues)(提问前请先搜索是否已有类似问题) 74 | 75 | 76 | ## 参与贡献 77 | 78 | 此项目不需要捐赠。如果你想要帮助改进这个项目,欢迎通过以下方式参与进来(并不仅局限于代码): 79 | 80 | - 帮助撰写和改进Wiki 81 | 82 | - 帮助完善单元测试数据(不必非要写代码,例如如果你发现有某系列的番号识别不准确,总结一下提issue也是很好的) 83 | 84 | - 帮助翻译 genre 85 | 86 | - Bugfix / 新功能?欢迎发 Pull Request 87 | 88 | - 要不考虑点个 Star ?(我会很开心的) 89 | 90 | 91 | ## 许可 92 | 93 | 此项目的所有权利与许可受 GPL-3.0 License 与 [Anti 996 License](https://github.com/996icu/996.ICU/blob/master/LICENSE_CN) 共同限制。此外,如果你使用此项目,表明你还额外接受以下条款: 94 | 95 | - 本软件仅供学习 Python 和技术交流使用 96 | 97 | - 请勿在微博、微信等墙内的公共社交平台上宣传此项目 98 | 99 | - 用户在使用本软件时,请遵守当地法律法规 100 | 101 | - 禁止将本软件用于商业用途 102 | 103 | 104 | --- 105 | 106 | ![Star History Chart](https://api.star-history.com/svg?repos=Yuukiy/JavSP&type=Date) 107 | -------------------------------------------------------------------------------- /core/image.py: -------------------------------------------------------------------------------- 1 | """处理本地图片的相关功能""" 2 | import os 3 | import logging 4 | from PIL import Image, ImageOps 5 | 6 | 7 | __all__ = ['valid_pic', 'crop_poster', 'get_pic_size'] 8 | 9 | logger = logging.getLogger(__name__) 10 | 11 | 12 | def valid_pic(pic_path): 13 | """检查图片文件是否完整""" 14 | try: 15 | img = ImageOps.exif_transpose(Image.open(pic_path)) 16 | img.load() 17 | return True 18 | except Exception as e: 19 | logger.debug(e, exc_info=True) 20 | return False 21 | 22 | 23 | def crop_poster(fanart_file, poster_file): 24 | """将给定的fanart图片文件裁剪为适合poster尺寸的图片""" 25 | # Kodi的宽高比为2:3,但是按照这个比例来裁剪会导致poster画面不完整, 26 | # 因此按照poster画面比例来裁剪,这样的话虽然在显示时可能有轻微变形,但是画面是完整的 27 | fanart = Image.open(fanart_file) 28 | fanart_w, fanart_h = fanart.size 29 | # 1.42 = 2535/1785(高清封面), 539/379(普通封面) 30 | poster_w = int(fanart_h / 1.42) 31 | if poster_w <= fanart_w: 32 | poster_h = fanart_h 33 | else: 34 | # 图片太“瘦”时以宽度来定裁剪高度 35 | poster_w, poster_h = fanart_w, int(fanart_w * 1.42) 36 | # (left, upper, right, lower) 37 | box = (fanart_w-poster_w, 0, fanart_w, poster_h) 38 | poster = fanart.crop(box) 39 | # quality: from doc, default is 75, values above 95 should be avoided 40 | poster.save(poster_file, quality=95) 41 | 42 | 43 | def get_pic_size(pic_path): 44 | """获取图片文件的分辨率""" 45 | pic = ImageOps.exif_transpose(Image.open(pic_path)) 46 | return pic.size 47 | 48 | 49 | if __name__ == "__main__": 50 | import os, sys 51 | import pretty_errors 52 | pretty_errors.configure(display_link=True) 53 | for file in sys.argv[1:]: 54 | if os.path.exists(file): 55 | base, ext = os.path.splitext(file) 56 | poster = base.replace('_fanart', '') + '_poster' + ext 57 | crop_poster(file, poster) 58 | -------------------------------------------------------------------------------- /core/lib.py: -------------------------------------------------------------------------------- 1 | """用来组织不需要依赖任何自定义类型的功能函数""" 2 | import os 3 | import re 4 | import sys 5 | 6 | 7 | __all__ = ['re_escape', 'mei_path', 'strftime_to_minutes', 'detect_special_attr'] 8 | 9 | 10 | _special_chars_map = {i: '\\' + chr(i) for i in b'()[]{}?*+|^$\\.'} 11 | def re_escape(s: str) -> str: 12 | """用来对字符串进行转义,以将转义后的字符串用于构造正则表达式""" 13 | pattern = s.translate(_special_chars_map) 14 | return pattern 15 | 16 | 17 | def mei_path(path): 18 | """获取一个随代码打包的文件在解压后的路径""" 19 | if getattr(sys, 'frozen', False): 20 | return os.path.join(sys._MEIPASS, path) 21 | else: 22 | return path 23 | 24 | 25 | def strftime_to_minutes(s: str) -> int: 26 | """将HH:MM:SS或MM:SS的时长转换为分钟数返回 27 | 28 | Args: 29 | s (str): HH:MM:SS or MM:SS 30 | 31 | Returns: 32 | [int]: 取整后的分钟数 33 | """ 34 | items = list(map(int, s.split(':'))) 35 | if len(items) == 2: 36 | minutes = items[0] + round(items[1]/60) 37 | elif len(items) == 3: 38 | minutes = items[0] * 60 + items[1] + round(items[2]/60) 39 | else: 40 | raise ValueError(f"无法将字符串'{s}'转换为分钟") 41 | return minutes 42 | 43 | 44 | _PATTERN = re.compile(r'(uncen(sor(ed)?)?([- _\s]*leak(ed)?)?|[无無][码碼](流出|破解))', flags=re.I) 45 | def detect_special_attr(filepath: str, avid: str = None) -> str: 46 | """通过文件名检测影片是否有特殊属性(内嵌字幕、无码流出/破解) 47 | 48 | Returns: 49 | [str]: '', 'U', 'C', 'UC' 50 | """ 51 | result = '' 52 | base = os.path.splitext(os.path.basename(filepath))[0].upper() 53 | # 尝试使用正则匹配 54 | match = _PATTERN.search(base) 55 | if match: 56 | result += 'U' 57 | # 尝试匹配-C/-U/-UC后缀的影片 58 | postfix = base.split('-')[-1] 59 | if postfix in ('U', 'C', 'UC'): 60 | result += postfix 61 | elif avid: 62 | pattern_str = re.sub(r'[_-]', '[_-]*', avid) + '(UC|U|C)' 63 | match = re.search(pattern_str, base, flags=re.I) 64 | if match: 65 | result += match.group(1) 66 | # 最终格式化 67 | result = ''.join(sorted(set(result), reverse=True)) 68 | return result 69 | 70 | 71 | if __name__ == "__main__": 72 | print(detect_special_attr('STARS225Uc.mp4', 'STARS-225')) 73 | -------------------------------------------------------------------------------- /core/nfo.py: -------------------------------------------------------------------------------- 1 | """与操作nfo文件相关的功能""" 2 | import os 3 | import sys 4 | from lxml.etree import tostring 5 | from lxml.builder import E 6 | 7 | 8 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 9 | from core.datatype import MovieInfo 10 | from core.config import conf, rel_path_from_exe 11 | cfg, args = conf() 12 | 13 | 14 | def write_nfo(info: MovieInfo, nfo_file): 15 | """将存储了影片信息的'info'写入到nfo文件中""" 16 | # NFO spec: https://kodi.wiki/view/NFO_files/Movies 17 | nfo = E.movie() 18 | if info.nfo_title: 19 | nfo.append(E.title(info.nfo_title)) 20 | else: 21 | nfo.append(E.title(info.title)) 22 | 23 | # 仅在标题被处理过时'ori_title'字段才会有值 24 | if info.ori_title: 25 | nfo.append(E.originaltitle(info.ori_title)) 26 | 27 | # Kodi的文档中评分支持多个来源,但经测试,添加了多个评分时Kodi也只显示了第一个评分 28 | if info.score: 29 | nfo.append(E.rating(info.score)) 30 | 31 | # 目前没有合适的字段用于outline(一行简短的介绍),力求不在nfo中写入冗余的信息,因此不添加outline标签 32 | # 而且无论是Kodi还是Jellyfin中都没有找到实际显示outline的位置;tagline倒是都有发现 33 | 34 | if info.plot: 35 | nfo.append(E.plot(info.plot)) 36 | 37 | # 目前没有合适的字段用于tagline(一行简短的介绍) 38 | 39 | # 并不是每个数据源都有影片的时长信息(例如airav) 40 | if info.duration: 41 | nfo.append(E.runtime(info.duration)) 42 | 43 | # thumb字段可以用来为不同的aspect强制指定图片文件名 44 | # 例如可以将'NoPoster.jpg'指定给'ABC-123.mp4',而不必按照poster文件名的常规命名规则来 45 | # 但是Emby不支持此特性,Jellyfin的文档和社区都比较弱,没找到相关说明,推测多半也不支持 46 | 47 | # fanart通常也是通过给fanart图片命名来匹配 48 | nfo.append(E.mpaa('NC-17')) # 分级 49 | 50 | # 将DVD ID和CID写入到uniqueid字段 51 | if info.dvdid: 52 | nfo.append(E.uniqueid(info.dvdid, type='num', defult='true')) 53 | if info.cid: 54 | nfo.append(E.uniqueid(info.cid, type='cid')) 55 | 56 | # 选择要写入的genre数据源字段:将[]作为后备结果,以确保genre结果为None时后续不会抛出异常 57 | for genre in (info.genre_norm, info.genre, []): 58 | if genre: 59 | break 60 | # 写入genre分类:优先使用genre_norm。在Jellyfin上,只有genre可以直接跳转,tag不可以 61 | # 也同时写入tag。TODO: 还没有研究tag和genre在Kodi上的区别 62 | for i in genre: 63 | nfo.append(E.genre(i)) 64 | if cfg.NFO.add_genre_to_tag: 65 | for i in genre: 66 | nfo.append(E.tag(i)) 67 | 68 | # Kodi上的country字段没说必须使用国家的代码(比如JP),所以目前暂定直接使用国家名 69 | nfo.append(E.country('日本')) 70 | 71 | if info.serial: 72 | # 部分影片有系列。set字段支持overview作为介绍,但是目前没发现有地方可以获取到系列的介绍 73 | nfo.append(E.set(E.name(info.serial))) 74 | 75 | if info.director: 76 | nfo.append(E.director(info.director)) 77 | 78 | # 发行日期。文档中关于'year'字段的说明: Do not use. Use instead 79 | if info.publish_date: 80 | nfo.append(E.premiered(info.publish_date)) 81 | 82 | # 原文是 Production studio: 因此这里写入的是影片制作商 83 | if info.producer: 84 | nfo.append(E.studio(info.producer)) 85 | 86 | # trailer 预告片 87 | if info.preview_video: 88 | nfo.append(E.trailer(info.preview_video)) 89 | 90 | # TODO: fileinfo 字段,看起来可以给定字幕语言和类型,留待开发 91 | 92 | # 写入演员名。Kodi支持用thumb显示演员头像,如果能获取到演员头像也一并写入 93 | if info.actress: 94 | for i in info.actress: 95 | if (info.actress_pics) and (i in info.actress_pics): 96 | nfo.append(E.actor(E.name(i), E.thumb(info.actress_pics[i]))) 97 | else: 98 | nfo.append(E.actor(E.name(i))) 99 | 100 | with open(nfo_file, 'wt', encoding='utf-8') as f: 101 | f.write(tostring(nfo, encoding='unicode', pretty_print=True, 102 | doctype='')) 103 | 104 | 105 | if __name__ == "__main__": 106 | import pretty_errors 107 | pretty_errors.configure(display_link=True) 108 | info = MovieInfo(from_file=R'unittest\data\IPX-177 (javbus).json') 109 | write_nfo(info) 110 | -------------------------------------------------------------------------------- /core/print.py: -------------------------------------------------------------------------------- 1 | """改写内置的print函数,将其输出重定向到tqdm""" 2 | import tqdm 3 | import inspect 4 | 5 | 6 | __all__ = ['TqdmOut'] 7 | 8 | 9 | # 普通输出和tqdm的输出混在一起会导致显示错乱,故在使用tqdm时要使用tqdm.write方法。 10 | # 但是不希望又每个模块都使用tqdm.write方法,这样会显得混乱而且会导致与tqdm强耦合。 11 | # 这个模块被设计来解决上面的问题:导入此模块后,全局覆盖内置的print,将输出都重定向到tqdm。 12 | # 导入只在项目入口执行,这将改写所有后续导入的模块中的print的行为; 13 | # 在单个模块内,不执行导入,这样的话在各个模块内仍然可以直接使用print 14 | 15 | builtin_print = print 16 | def flex_print(*args, **kwargs): 17 | try: 18 | tqdm.tqdm.write(*args, **kwargs) 19 | except: 20 | builtin_print(*args, ** kwargs) 21 | # 替换内置的print 22 | inspect.builtins.print = flex_print 23 | 24 | 25 | class TqdmOut: 26 | """用于将logging的stream输出重定向到tqdm""" 27 | @classmethod 28 | def write(cls, s, file=None, nolock=False): 29 | tqdm.tqdm.write(s, file=file, end='', nolock=nolock) 30 | -------------------------------------------------------------------------------- /image/JavSP.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tetato/JavSP-Docker/2bf01cde13db210f76377e4a89ed4c9eaecec113/image/JavSP.ico -------------------------------------------------------------------------------- /image/javsp_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tetato/JavSP-Docker/2bf01cde13db210f76377e4a89ed4c9eaecec113/image/javsp_logo.png -------------------------------------------------------------------------------- /image/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tetato/JavSP-Docker/2bf01cde13db210f76377e4a89ed4c9eaecec113/image/screenshot.png -------------------------------------------------------------------------------- /make.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM Create exe from script 3 | 4 | set activate=venv\Scripts\activate.bat 5 | set deactivate=venv\Scripts\deactivate.bat 6 | set ps_script=make\archive.ps1 7 | 8 | IF EXIST %activate% ( 9 | @echo Activate vitualenv... 10 | call %activate% 11 | ) 12 | 13 | echo JavSP version: 14 | python make\gen_ver_hook.py ver_hook.py 15 | pyinstaller --clean --noconfirm make\windows.spec 16 | 17 | IF EXIST %ps_script% ( 18 | powershell -executionPolicy bypass -file %ps_script% 19 | del %ps_script% 20 | ) 21 | del ver_hook.py 22 | del dist\config.ini 2>nul 23 | rmdir /s /q build 24 | -------------------------------------------------------------------------------- /make/gen_ver_hook.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import platform 3 | import subprocess 4 | 5 | run = subprocess.run(['git', 'describe', '--tags', '--long'], capture_output=True, encoding='utf-8') 6 | if run.returncode == 0: 7 | desc = run.stdout.strip() 8 | tag_name, minor, _ = desc.split('-') 9 | if int(minor) == 0: # means current commit exactly matches the tag 10 | auto_ver = tag_name 11 | else: 12 | if tag_name.count('.') == 1: 13 | auto_ver = tag_name + '.0.' + minor 14 | else: 15 | auto_ver = tag_name + '.' + minor 16 | else: 17 | auto_ver = "v0.unknown" 18 | 19 | print(auto_ver) 20 | 21 | # Generate powershell script to create zip file 22 | if platform.system() == 'Windows': 23 | with open('make\\archive.ps1', 'wt', encoding='utf-8') as f: 24 | zip_file = 'dist\\JavSP-' + auto_ver + '-Windows-amd64.zip' 25 | command = 'Compress-Archive -Path dist\\JavSP.exe -Force -DestinationPath ' + zip_file 26 | f.write(command) 27 | 28 | if len(sys.argv) > 1: 29 | hook_file = sys.argv[1] 30 | with open(hook_file, 'wt', encoding='utf-8') as f: 31 | f.write('import sys\n') 32 | f.write("setattr(sys, 'javsp_version', '" + auto_ver + "')\n") 33 | -------------------------------------------------------------------------------- /make/windows.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python ; coding: utf-8 -*- 2 | import os 3 | import inspect 4 | import cloudscraper 5 | 6 | block_cipher = None 7 | cloudscraper_dir = os.path.dirname(inspect.getfile(cloudscraper)) 8 | cloudscraper_json = cloudscraper_dir + '/user_agent/browsers.json' 9 | 10 | # generate crawlers list (exlcude list is not needed here) 11 | all_crawlers = [] 12 | for file in os.listdir('web'): 13 | name, ext = os.path.splitext(file) 14 | if ext == '.py': 15 | all_crawlers.append('web.' + name) 16 | 17 | # workaround for a bug of PyInstaller since 5.0: https://github.com/pyinstaller/pyinstaller/issues/6759 18 | ico_file = os.path.abspath(os.path.join(SPECPATH, "../image/JavSP.ico")) 19 | 20 | # pyinstaller locates path relative to the .spec file 21 | a = Analysis(['../JavSP.py'], 22 | pathex=['build'], 23 | binaries=[], 24 | datas=[ 25 | (cloudscraper_json, 'cloudscraper/user_agent'), 26 | ("../core/config.ini", "."), 27 | ("../data/*.*", "data"), 28 | (ico_file, "image") 29 | ], 30 | hiddenimports=all_crawlers, 31 | hookspath=[], 32 | runtime_hooks=['ver_hook.py'], 33 | excludes=[], 34 | win_no_prefer_redirects=False, 35 | win_private_assemblies=False, 36 | cipher=block_cipher, 37 | noarchive=False) 38 | pyz = PYZ(a.pure, a.zipped_data, 39 | cipher=block_cipher) 40 | exe = EXE(pyz, 41 | a.scripts, 42 | a.binaries, 43 | a.zipfiles, 44 | a.datas, 45 | [], 46 | name='JavSP', 47 | debug=False, 48 | bootloader_ignore_signals=False, 49 | strip=False, 50 | upx=True, 51 | upx_exclude=[], 52 | runtime_tmpdir=None, 53 | console=True, 54 | icon=ico_file) 55 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | altgraph==0.17 2 | baidu-aip==2.2.18.0 3 | certifi==2023.7.22 4 | chardet==4.0.0 5 | cloudscraper==1.2.71 6 | colorama==0.4.4 7 | future==0.18.3 8 | idna==2.10 9 | lxml==4.9.1 10 | packaging==20.9 11 | pefile==2019.4.18 12 | Pillow==10.2.0 13 | pretty-errors==1.2.19 14 | pycryptodome==3.19.1 15 | PySocks==1.7.1 16 | requests==2.31.0 17 | tqdm==4.59.0 18 | urllib3==1.25.11 19 | pywin32==303 ; sys_platform=='win32' 20 | pywin32-ctypes==0.2.0 ; sys_platform=='win32' 21 | streamlit == 1.30.0 -------------------------------------------------------------------------------- /tools/airav_search.py: -------------------------------------------------------------------------------- 1 | """获取airav指定关键词的所有搜索结果""" 2 | import os 3 | import sys 4 | import json 5 | 6 | 7 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 8 | from web.base import Request 9 | 10 | request = Request() 11 | request.headers['Accept-Language'] = 'zh-TW,zh;q=0.9' 12 | 13 | base_url = 'https://www.airav.wiki' 14 | 15 | 16 | def search(keyword): 17 | """搜索指定影片的所有结果""" 18 | all_results = [] 19 | page = 1 20 | data = {'offset': 0, 'count': 1, 'result': []} 21 | while (data['offset'] + len(data['result']) < data['count']): 22 | url = f'{base_url}/api/video/list?lang=zh-TW&lng=zh-TW&search={keyword}&page={page}' 23 | data = request.get(url).json() 24 | all_results.extend(data['result']) 25 | print(f"Get page {page}: {len(data['result'])} movie(s)") 26 | page += 1 27 | for i in all_results: 28 | if not i['url']: 29 | i['url'] = f"{base_url}/video/{i['barcode']}" 30 | return all_results 31 | 32 | 33 | if __name__ == "__main__": 34 | keyword = '版' 35 | results = search(keyword) 36 | with open(f'airav_search_{keyword}.json', 'wt', encoding='utf-8') as f: 37 | json.dump(results, f, indent=2, ensure_ascii=False) 38 | -------------------------------------------------------------------------------- /tools/call_crawler.py: -------------------------------------------------------------------------------- 1 | """调用抓取器抓取数据""" 2 | import os 3 | import sys 4 | 5 | 6 | import pretty_errors 7 | from tqdm import tqdm 8 | 9 | 10 | pretty_errors.configure(display_link=True) 11 | 12 | 13 | file_dir = os.path.dirname(__file__) 14 | data_dir = os.path.abspath(os.path.join(file_dir, '../unittest/data')) 15 | sys.path.insert(0, os.path.abspath(os.path.join(file_dir, '..'))) 16 | from core.datatype import MovieInfo 17 | 18 | 19 | # 搜索抓取器并导入它们 20 | all_crawler = {} 21 | exclude_files = ['fc2fan'] 22 | for file in os.listdir('web'): 23 | name, ext = os.path.splitext(file) 24 | if ext == '.py' and name not in exclude_files: 25 | modu = 'web.' + name 26 | __import__(modu) 27 | if hasattr(sys.modules[modu], 'parse_data'): 28 | parser = getattr(sys.modules[modu], 'parse_data') 29 | all_crawler[name] = parser 30 | 31 | 32 | # 生成本地的测试数据作为测试数据,以确保未来对抓取器进行修改时,不会影响到现有功能 33 | def call_crawlers(dvdid_list: list, used_crawlers=None): 34 | """抓取影片数据 35 | 36 | Args: 37 | dvdid_list (list): 影片番号的列表 38 | crawlers (list[str], optional): 要使用的抓取器,未指定时将使用全部抓取器 39 | """ 40 | if used_crawlers: 41 | crawlers = {i:all_crawler[i] for i in used_crawlers} 42 | else: 43 | crawlers = all_crawler 44 | outer_bar = tqdm(dvdid_list, desc='抓取影片数据', leave=False) 45 | for avid in outer_bar: 46 | success, fail = [], [] 47 | outer_bar.set_description(f'抓取影片数据: {avid}') 48 | inner_bar = tqdm(crawlers.items(), desc='抓取器', leave=False) 49 | for name, parser in inner_bar: 50 | inner_bar.set_description(f'正在抓取{name}'.rjust(10+len(avid))) 51 | # 每次都会创建一个全新的实例,所以不同抓取器的结果之间不会有影响 52 | if name != 'fanza': 53 | movie = MovieInfo(avid) 54 | else: 55 | movie = MovieInfo(cid=avid) 56 | try: 57 | parser(movie) 58 | path = f"{data_dir}{os.sep}{avid} ({name}).json" 59 | movie.dump(path) 60 | success.append(name) 61 | except: 62 | fail.append(name) 63 | out = "{} 抓取完成: 成功{}个 {}; 失败{}个 {}".format(avid, len(success), ' '.join(success), len(fail), ' '.join(fail)) 64 | tqdm.write(out) 65 | 66 | 67 | if __name__ == "__main__": 68 | if len(sys.argv) > 1: 69 | # 带参数调用时,将参数全部视作番号并调用所有抓取器抓取数据 70 | call_crawlers(sys.argv[1:]) 71 | else: 72 | user_in = input('请输入要抓取数据的影片番号: ') 73 | dvdid_list = user_in.split() 74 | # 提示选择要使用的抓取器 75 | names = list(all_crawler.keys()) 76 | for i in range(len(names)): 77 | print(f"{i+1}. {names[i]}", end=' ') 78 | user_in2 = input('\n请选择要使用的抓取器(回车表示全部使用): ') 79 | if user_in2: 80 | items = user_in2.split() 81 | indexes = [int(i)-1 for i in items if i.isdigit()] 82 | valid_indexes = [i for i in indexes if i < len(names)] 83 | used = [names[i] for i in valid_indexes] 84 | else: 85 | used = names 86 | # 开始抓取数据 87 | call_crawlers(dvdid_list, used) 88 | -------------------------------------------------------------------------------- /unittest/conftest.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import pytest 4 | from glob import glob 5 | 6 | 7 | data_dir = os.path.join(os.path.dirname(__file__), 'data') 8 | 9 | 10 | def pytest_addoption(parser): 11 | parser.addoption( 12 | "--only", action="store", default="", help="仅测试指定抓取器的数据" 13 | ) 14 | 15 | def pytest_runtest_logreport(report): 16 | """定制 short test summary info 显示格式""" 17 | # report 的部分属性形如 18 | # nodeid: unittest/test_crawlers.py::test_crawler[082713-417: avsox] 19 | # location: ('unittest\\test_crawlers.py', 27, 'test_crawler[082713-417: avsox]') 20 | # keywords: {'082713-417: avsox': 1, 'unittest/test_crawlers.py': 1, 'test_crawler[082713-417: avsox]': 1, 'JavSP': 1} 21 | 22 | # 为test_crawlers.py定制short test summary格式 23 | if 'test_crawlers.py::' in report.nodeid: 24 | report.nodeid = re.sub(r'^.*::test_crawler', '', report.nodeid) 25 | 26 | 27 | @pytest.fixture 28 | def crawler(request): 29 | return request.config.getoption("--only") 30 | 31 | 32 | def pytest_generate_tests(metafunc): 33 | if 'crawler_params' in metafunc.fixturenames: 34 | # 根据测试数据文件夹中的文件生成测试数据 35 | testcases = {} 36 | data_files = glob(data_dir + os.sep + '*.json') 37 | target_crawler = metafunc.config.getoption("--only") 38 | for file in data_files: 39 | basename = os.path.basename(file) 40 | match = re.match(r"([-\w]+) \((\w+)\)", basename, re.I) 41 | if match: 42 | avid, scraper = match.groups() 43 | name = f'{avid}: {scraper}' 44 | # 仅当未指定抓取器或者指定的抓取器与当前抓取器相同时,才实际执行抓取和比较 45 | if (not target_crawler) or scraper == target_crawler: 46 | testcases[name] = (avid, scraper, file) 47 | # 生成测试用例(testcases的键名将用作测试用例ID) 48 | metafunc.parametrize("crawler_params", testcases.values(), ids=testcases.keys()) 49 | -------------------------------------------------------------------------------- /unittest/data/012717_472 (airav).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "1pondo_012717_472", 3 | "cid": null, 4 | "url": "https://www.airav.wiki/video/1pondo_012717_472", 5 | "plot": "早上看到鄰居人妻江波涼出來倒垃圾沒穿胸罩真是欠幹啊…太太想說只是倒個垃圾嘛不穿應該沒差、但讓男人忍不住勃起被幹翻也沒話說啊!", 6 | "cover": "https://wiki-img.airav.wiki/storage/big_pic/99-27-00287.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "熟女", 10 | "潮吹", 11 | "中出", 12 | "癡女", 13 | "巨乳", 14 | "口交", 15 | "無碼片", 16 | "AV女優片", 17 | "窈窕", 18 | "不戴套", 19 | "美腿", 20 | "抬腿開鮑", 21 | "HD高畫質" 22 | ], 23 | "genre_id": null, 24 | "genre_norm": null, 25 | "score": null, 26 | "title": "早起鄰居人妻不穿奶罩倒垃圾真欠幹 江波涼", 27 | "ori_title": null, 28 | "magnet": null, 29 | "serial": null, 30 | "actress": [], 31 | "actress_pics": null, 32 | "director": null, 33 | "duration": null, 34 | "producer": "一本道", 35 | "publisher": null, 36 | "uncensored": null, 37 | "publish_date": "2017-01-27", 38 | "preview_pics": [], 39 | "preview_video": "http://freemsall.airav.cc/mv_download/D5F98B5916D18CC6BFC43C0F3908E8CC/623C397E/mv/17C46753BDFC0468E7AC7C1257981610/623C397E/0/36000/257186/high.mp4" 40 | } -------------------------------------------------------------------------------- /unittest/data/080719-976 (airav).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "caribbeancom080719-976", 3 | "cid": null, 4 | "url": "https://www.airav.wiki/video/caribbeancom080719-976", 5 | "plot": "這次讓我們再來公開更多在加勒比以往看不到的AV女優姬川優奈精選片段、讓她穿上絲襪來猛插、身體敏感到痙攣超爽、被大屌男優肏壞子宮、搖著美乳大昇天、愛姬川優奈的你絕對不要錯過啦!", 6 | "cover": "https://wiki-img.airav.wiki/storage/big_pic/99-27-01907.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "中出", 10 | "明星臉", 11 | "女僕", 12 | "無碼片", 13 | "巨乳", 14 | "舔鮑", 15 | "窈窕", 16 | "按摩棒", 17 | "裸體圍裙", 18 | "精選集", 19 | "HD高畫質" 20 | ], 21 | "genre_id": null, 22 | "genre_norm": null, 23 | "score": null, 24 | "title": "噴精快手 姬川優奈精選 2", 25 | "ori_title": null, 26 | "magnet": null, 27 | "serial": null, 28 | "actress": [ 29 | "姬川優奈" 30 | ], 31 | "actress_pics": null, 32 | "director": null, 33 | "duration": null, 34 | "producer": "加勒比", 35 | "publisher": null, 36 | "uncensored": null, 37 | "publish_date": "2019-08-07", 38 | "preview_pics": [], 39 | "preview_video": "http://freemsall.airav.cc/mv_download/8DB2A8AC4A82C9D2B704AD60D5DFE766/623C38B3/mv/A7FE7001CF8328EAC2218865FFBB95DA/623C38B3/0/36000/280191/high.mp4" 40 | } -------------------------------------------------------------------------------- /unittest/data/082713-417 (airav).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "caribbeancom_082713-417", 3 | "cid": null, 4 | "url": "https://www.airav.wiki/video/caribbeancom_082713-417", 5 | "plot": "想要成為AKB48,有著白皙透嫩的美白肌膚露的尾野真知子化身為超可愛的女僕前來伺候主人您!!用著豪華的肉體來進行秘密的枕邊營業!不僅自慰,口交,就連插入也不是問題喔!!", 6 | "cover": "https://wiki-img.airav.wiki/storage/big_pic/99-07-9390.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "角色扮演", 10 | "企劃片", 11 | "中出", 12 | "巨乳", 13 | "中文字幕", 14 | "女僕", 15 | "口交", 16 | "白虎", 17 | "打手槍", 18 | "自慰", 19 | "舔鮑", 20 | "美少女", 21 | "裸體圍裙", 22 | "噴精", 23 | "偶像明星", 24 | "按摩棒", 25 | "不戴套", 26 | "口內射精", 27 | "美尻" 28 | ], 29 | "genre_id": null, 30 | "genre_norm": null, 31 | "score": null, 32 | "title": "新人偶像伺候女僕 尾野真知子", 33 | "ori_title": null, 34 | "magnet": null, 35 | "serial": null, 36 | "actress": [ 37 | "尾野真知子" 38 | ], 39 | "actress_pics": null, 40 | "director": null, 41 | "duration": null, 42 | "producer": "加勒比", 43 | "publisher": null, 44 | "uncensored": null, 45 | "publish_date": "2013-08-27", 46 | "preview_pics": [], 47 | "preview_video": "http://freemsall.airav.cc/mv_download/E2C2DD60BEF59B84C26995BE9F73C0AC/623C38BD/mv/B4E60C9F8F2C677311DDD25C01DF66CA/623C38BD/0/36000/59344/normal.mp4" 48 | } -------------------------------------------------------------------------------- /unittest/data/082713-417 (avsox).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "082713-417", 3 | "cid": null, 4 | "url": "https://avsox.click/cn/movie/bfdd4a5b8187eac4", 5 | "plot": null, 6 | "cover": "https://file.netcdn.space/storage/caribbeancom/moviepages/082713-417/images/l_l.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "振动", 10 | "口交", 11 | "萝莉", 12 | "舔阴", 13 | "手淫", 14 | "自慰", 15 | "内射", 16 | "巨乳", 17 | "美乳", 18 | "口内射精", 19 | "女仆", 20 | "围裙", 21 | "美臀", 22 | "角色扮演", 23 | "偶像" 24 | ], 25 | "genre_id": null, 26 | "genre_norm": null, 27 | "score": null, 28 | "title": "新人アイドルご奉仕メイド", 29 | "ori_title": null, 30 | "magnet": null, 31 | "serial": "オリジナル動画", 32 | "actress": [ 33 | "尾野真知子" 34 | ], 35 | "actress_pics": null, 36 | "director": null, 37 | "duration": "70", 38 | "producer": "カリビアンコム( Caribbeancom )", 39 | "publisher": null, 40 | "uncensored": null, 41 | "publish_date": "2013-08-27", 42 | "preview_pics": null, 43 | "preview_video": null 44 | } -------------------------------------------------------------------------------- /unittest/data/082713-417 (javbus).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "082713-417", 3 | "cid": null, 4 | "url": "https://www.javbus.com/082713-417", 5 | "plot": null, 6 | "cover": "https://www.javbus.com/imgs/cover/bq2_b.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "振動", 10 | "無毛", 11 | "舔陰", 12 | "手淫", 13 | "自慰", 14 | "內射", 15 | "巨乳", 16 | "美乳", 17 | "口內射精", 18 | "女僕", 19 | "圍裙", 20 | "美臀", 21 | "角色扮演", 22 | "偶像", 23 | "企劃物", 24 | "口交", 25 | "獨佔動畫" 26 | ], 27 | "genre_id": null, 28 | "genre_norm": [ 29 | "振動", 30 | "無毛", 31 | "舔陰", 32 | "手淫", 33 | "自慰", 34 | "內射", 35 | "巨乳", 36 | "美乳", 37 | "口內射精", 38 | "女僕", 39 | "圍裙", 40 | "美臀", 41 | "角色扮演", 42 | "偶像", 43 | "企劃物", 44 | "口交", 45 | "獨佔動畫" 46 | ], 47 | "score": null, 48 | "title": "新人アイドルご奉仕メイド", 49 | "ori_title": null, 50 | "magnet": null, 51 | "serial": "カリビアンコム", 52 | "actress": [ 53 | "尾野真知子" 54 | ], 55 | "actress_pics": { 56 | "尾野真知子": "https://www.javbus.com/imgs/actress/46l.jpg" 57 | }, 58 | "director": null, 59 | "duration": "60", 60 | "producer": "カリビアンコム", 61 | "publisher": null, 62 | "uncensored": true, 63 | "publish_date": "2013-08-27", 64 | "preview_pics": [], 65 | "preview_video": null 66 | } -------------------------------------------------------------------------------- /unittest/data/130614-KEIKO (avsox).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "130614-KEIKO", 3 | "cid": null, 4 | "url": "https://avsox.click/cn/movie/f77287dd6908a94b", 5 | "plot": null, 6 | "cover": "https://file.netcdn.space/storage/1000giri/130614-KEIKO/19515.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "舔阴", 10 | "内射", 11 | "学生", 12 | "角色扮演", 13 | "企划物", 14 | "口交", 15 | "独占动画", 16 | "少女" 17 | ], 18 | "genre_id": null, 19 | "genre_norm": null, 20 | "score": null, 21 | "title": "めっちゃシタイ!!改#054〜スタイル抜群で!めっちゃ!かわいい!JKと!生ハメ!〜", 22 | "ori_title": null, 23 | "magnet": null, 24 | "serial": null, 25 | "actress": [ 26 | "ケイコ" 27 | ], 28 | "actress_pics": null, 29 | "director": null, 30 | "duration": "39", 31 | "producer": "1000人斬り( 1000giri )", 32 | "publisher": null, 33 | "uncensored": null, 34 | "publish_date": "2013-06-14", 35 | "preview_pics": null, 36 | "preview_video": null 37 | } -------------------------------------------------------------------------------- /unittest/data/130614-KEIKO (javbus).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "130614-KEIKO", 3 | "cid": null, 4 | "url": "https://www.javbus.com/130614-KEIKO", 5 | "plot": null, 6 | "cover": "https://www.javbus.com/imgs/cover/f23_b.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "舔陰", 10 | "內射", 11 | "學生", 12 | "角色扮演", 13 | "企劃物", 14 | "口交", 15 | "獨佔動畫", 16 | "少女" 17 | ], 18 | "genre_id": null, 19 | "genre_norm": [ 20 | "舔陰", 21 | "內射", 22 | "學生", 23 | "角色扮演", 24 | "企劃物", 25 | "口交", 26 | "獨佔動畫", 27 | "少女" 28 | ], 29 | "score": null, 30 | "title": "めっちゃシタイ!!改#054〜スタイル抜群で!めっちゃ!かわいい!JKと!生ハメ!〜", 31 | "ori_title": null, 32 | "magnet": null, 33 | "serial": null, 34 | "actress": [ 35 | "ケイコ" 36 | ], 37 | "actress_pics": {}, 38 | "director": null, 39 | "duration": "39", 40 | "producer": "1000giri", 41 | "publisher": null, 42 | "uncensored": true, 43 | "publish_date": "2013-06-14", 44 | "preview_pics": [], 45 | "preview_video": null 46 | } -------------------------------------------------------------------------------- /unittest/data/130614-KEIKO (javdb).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "130614-KEIKO", 3 | "cid": null, 4 | "url": "https://javdb.com/v/rz4WD", 5 | "plot": null, 6 | "cover": "https://c0.jdbstatic.com/covers/rz/rz4WD.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "口交", 10 | "中出", 11 | "女學生", 12 | "角色扮演", 13 | "美少女" 14 | ], 15 | "genre_id": null, 16 | "genre_norm": [ 17 | "口交", 18 | "中出", 19 | "女學生", 20 | "Cosplay", 21 | "美少女" 22 | ], 23 | "score": "7.00", 24 | "title": "めっちゃシタイ!!改#054〜スタイル抜群で!めっちゃ!かわいい!JKと!生ハメ!〜", 25 | "ori_title": null, 26 | "magnet": [ 27 | "magnet:?xt=urn:btih:0c7821eacf03ac7c2c50bbaa5fd53e1264604528&dn=【特务】【sex8】1000人斬130614-keiko 風格出眾的JK生+無毛白板PAI.3", 28 | "magnet:?xt=urn:btih:3262c02619b30749fadec30e115f5c86b306f541&dn=第一會所新片@SIS001@(1000人斬り)(130614-keiko)めっちゃシタイ!!改#054~スタイル抜群で!めっちゃ!かわいい!JKと!生ハメ!~ケイコ_ミイナ", 29 | "magnet:?xt=urn:btih:05797f3e15a73608cc978d0cfee1ae84462543b4&dn=1000-giri 130614-Keiko Miina", 30 | "magnet:?xt=urn:btih:82d4e276f9843947d32bea78effe4e27057f5981&dn=1000-Giri – 130614 – Keiko.mp4" 31 | ], 32 | "serial": null, 33 | "actress": [ 34 | "けいこ" 35 | ], 36 | "actress_pics": null, 37 | "director": null, 38 | "duration": "39", 39 | "producer": "1000giri", 40 | "publisher": null, 41 | "uncensored": true, 42 | "publish_date": "2013-06-14", 43 | "preview_pics": [], 44 | "preview_video": null 45 | } -------------------------------------------------------------------------------- /unittest/data/145dmn000007 (fanza).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": null, 3 | "cid": "145dmn000007", 4 | "url": "https://www.dmm.co.jp/digital/nikkatsu/-/detail/=/cid=145dmn000007/", 5 | "plot": "どんな厳格な男でも、性欲に我を失ってしまう時がある。ましてやそれが若くセクシーな女性達に触れる機会の多い職業ならば…。テレビ番組のアシスタント・ディレクター、予約が殺到する超人気のカリスマ美容師、そして聖職とも言える女子校教師。責任感に抑圧される彼らのストレスが限界を越えた時、おぞましきセックス犯罪が誘発される…!!", 6 | "cover": "https://pics.dmm.co.jp/digital/video/145dmn000007/145dmn000007pl.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "ドラマ", 10 | "Vシネマ", 11 | "縛り・緊縛", 12 | "ハメ撮り" 13 | ], 14 | "genre_id": [ 15 | "4114", 16 | "4110", 17 | "5021", 18 | "6002" 19 | ], 20 | "genre_norm": null, 21 | "score": "4.00", 22 | "title": "実録性犯罪ファイル 職業別ストレス症候群", 23 | "ori_title": null, 24 | "magnet": null, 25 | "serial": "実録 性犯罪ファイル", 26 | "actress": [ 27 | "河愛純", 28 | "相沢リリ", 29 | "野田めぐみ", 30 | "入江浩治", 31 | "麻央はじめ", 32 | "神戸顕一", 33 | "すわしんじ" 34 | ], 35 | "actress_pics": null, 36 | "director": "笠原唯央", 37 | "duration": "59", 38 | "producer": "TMC", 39 | "publisher": null, 40 | "uncensored": false, 41 | "publish_date": "2022-08-12", 42 | "preview_pics": [ 43 | "https://pics.dmm.co.jp/digital/video/145dmn000007/145dmn000007-1.jpg", 44 | "https://pics.dmm.co.jp/digital/video/145dmn000007/145dmn000007-2.jpg", 45 | "https://pics.dmm.co.jp/digital/video/145dmn000007/145dmn000007-3.jpg", 46 | "https://pics.dmm.co.jp/digital/video/145dmn000007/145dmn000007-4.jpg", 47 | "https://pics.dmm.co.jp/digital/video/145dmn000007/145dmn000007-5.jpg", 48 | "https://pics.dmm.co.jp/digital/video/145dmn000007/145dmn000007-6.jpg", 49 | "https://pics.dmm.co.jp/digital/video/145dmn000007/145dmn000007-7.jpg", 50 | "https://pics.dmm.co.jp/digital/video/145dmn000007/145dmn000007-8.jpg", 51 | "https://pics.dmm.co.jp/digital/video/145dmn000007/145dmn000007-9.jpg", 52 | "https://pics.dmm.co.jp/digital/video/145dmn000007/145dmn000007-10.jpg", 53 | "https://pics.dmm.co.jp/digital/video/145dmn000007/145dmn000007-11.jpg", 54 | "https://pics.dmm.co.jp/digital/video/145dmn000007/145dmn000007-12.jpg", 55 | "https://pics.dmm.co.jp/digital/video/145dmn000007/145dmn000007-13.jpg", 56 | "https://pics.dmm.co.jp/digital/video/145dmn000007/145dmn000007-14.jpg", 57 | "https://pics.dmm.co.jp/digital/video/145dmn000007/145dmn000007-15.jpg", 58 | "https://pics.dmm.co.jp/digital/video/145dmn000007/145dmn000007-16.jpg", 59 | "https://pics.dmm.co.jp/digital/video/145dmn000007/145dmn000007-17.jpg", 60 | "https://pics.dmm.co.jp/digital/video/145dmn000007/145dmn000007-18.jpg", 61 | "https://pics.dmm.co.jp/digital/video/145dmn000007/145dmn000007-19.jpg", 62 | "https://pics.dmm.co.jp/digital/video/145dmn000007/145dmn000007-20.jpg" 63 | ], 64 | "preview_video": "" 65 | } -------------------------------------------------------------------------------- /unittest/data/1stars931r (fanza).json: -------------------------------------------------------------------------------- 1 | { 2 | "作为此类影片的代表": "https://www.dmm.co.jp/rental/ppr/-/detail/=/cid=1stars931r/", 3 | "dvdid": null, 4 | "cid": "1stars931r", 5 | "url": "https://www.dmm.co.jp/rental/ppr/-/detail/=/cid=1stars931r/", 6 | "plot": "", 7 | "cover": "https://pics.dmm.co.jp/mono/movie/1stars931r/1stars931rpl.jpg", 8 | "big_cover": null, 9 | "genre": [ 10 | "巨乳", 11 | "ドキュメンタリー", 12 | "単体作品", 13 | "アイドル・芸能人", 14 | "デビュー作品", 15 | "サンプル動画", 16 | "軟体" 17 | ], 18 | "genre_id": [ 19 | "2001", 20 | "4023", 21 | "4025", 22 | "4118", 23 | "6006", 24 | "6102", 25 | "6935" 26 | ], 27 | "genre_norm": null, 28 | "score": 45, 29 | "title": "芸能界引退後、即AVデビュー 渚恋生", 30 | "ori_title": null, 31 | "magnet": null, 32 | "serial": "AV DEBUT(STAR)", 33 | "actress": [ 34 | "渚恋生" 35 | ], 36 | "actress_pics": null, 37 | "director": "星シュート", 38 | "duration": "210", 39 | "producer": "SODクリエイト", 40 | "publisher": null, 41 | "uncensored": false, 42 | "publish_date": null, 43 | "preview_pics": [ 44 | "https://pics.dmm.co.jp/digital/video/1stars00931/1stars00931-1.jpg", 45 | "https://pics.dmm.co.jp/digital/video/1stars00931/1stars00931-2.jpg", 46 | "https://pics.dmm.co.jp/digital/video/1stars00931/1stars00931-3.jpg", 47 | "https://pics.dmm.co.jp/digital/video/1stars00931/1stars00931-4.jpg", 48 | "https://pics.dmm.co.jp/digital/video/1stars00931/1stars00931-5.jpg", 49 | "https://pics.dmm.co.jp/digital/video/1stars00931/1stars00931-6.jpg", 50 | "https://pics.dmm.co.jp/digital/video/1stars00931/1stars00931-7.jpg", 51 | "https://pics.dmm.co.jp/digital/video/1stars00931/1stars00931-8.jpg" 52 | ], 53 | "preview_video": "https://cc3001.dmm.co.jp/litevideo/freepv/1/1st/1stars931/1stars931mhb.mp4" 54 | } -------------------------------------------------------------------------------- /unittest/data/259LUXU-593 (avwiki).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "259LUXU-593", 3 | "cid": null, 4 | "url": "https://av-wiki.net/259LUXU-593", 5 | "plot": null, 6 | "cover": "https://image.mgstage.com/images/luxutv/259luxu/593/pb_e_259luxu-593.jpg", 7 | "big_cover": null, 8 | "genre": null, 9 | "genre_id": null, 10 | "genre_norm": null, 11 | "score": null, 12 | "title": "ラグジュTV 608", 13 | "ori_title": null, 14 | "magnet": null, 15 | "serial": "ラグジュTV", 16 | "actress": "白咲りの", 17 | "actress_pics": null, 18 | "director": null, 19 | "duration": null, 20 | "producer": "ラグジュTV", 21 | "publisher": null, 22 | "uncensored": false, 23 | "publish_date": "2017-03-26", 24 | "preview_pics": null, 25 | "preview_video": null 26 | } -------------------------------------------------------------------------------- /unittest/data/300MIUM-1001 (avwiki).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "300MIUM-1001", 3 | "cid": null, 4 | "url": "https://av-wiki.net/300MIUM-1001", 5 | "plot": null, 6 | "cover": "https://image.mgstage.com/images/prestigepremium/300mium/1001/pb_e_300mium-1001.jpg", 7 | "big_cover": null, 8 | "genre": null, 9 | "genre_id": null, 10 | "genre_norm": null, 11 | "score": null, 12 | "title": "【癒しのGカップ陸マネ】デカ乳JDを彼女としてレンタル!口説き落として本来禁止のエロ行為までヤリまくった一部始終を完全REC!!純朴ピュア娘のくせに、弾力最高の最終兵器Gカップを隠し持つ超逸材!!挟まれたい乳・オブザイヤー2023堂々受賞!!【レンタル彼女】", 13 | "ori_title": null, 14 | "magnet": null, 15 | "serial": "レンタル彼女", 16 | "actress": "沙優七羽", 17 | "actress_pics": null, 18 | "director": null, 19 | "duration": null, 20 | "producer": "プレステージプレミアム(PRESTIGE PREMIUM)", 21 | "publisher": null, 22 | "uncensored": false, 23 | "publish_date": "2023-11-19", 24 | "preview_pics": null, 25 | "preview_video": null 26 | } -------------------------------------------------------------------------------- /unittest/data/62knbm009 (fanza).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": null, 3 | "cid": "62knbm009", 4 | "url": "https://www.dmm.co.jp/mono/anime/-/detail/=/cid=62knbm009/", 5 | "plot": "原作「同居する粘液」(出版:キルタイムコミュニケーション)OVA化第1弾!\n\n正体不明な粘液生物との奇妙な同居生活\n\n【STORY】\n藤原ユージはツイていない人生を送っていた。\n子供のころは割ってもいない花瓶のことで責められたり、\n同僚のミスで会社を首になったりもしていた。\n\nバイト先のコンビニでは他人が起こしたミスを\n年下の女の子である高宮から責められるのであった。\n気分が落ち込んだまま帰宅すると見知らぬ女の子が出迎えてくれる。\n戸惑うユージを前に女の子は正体を明かす。\nそれは3ヵ月前から一緒に暮らしている粘液生命体であった。\n人の言葉を話し姿を変えられる生物はユージに好意を抱いている。\n粘液生命体の好意に対してユージはそっけない。\n粘液生命体はユージの気を引く為バイト先の同僚である高宮に姿を変え誘惑してくるのであった。\n\n原作「同居する粘液」より「第1話」収録\n(C)2023 DATE/キルタイムコミュニケーション/King Bee/ダグダグ\n\n──────────────────\nタイトル:同居する粘液 第1話\nサブタイトル:日常の中の非日常\nレーベル名:King Bee\n原作:「同居する粘液」(出版:キルタイムコミュニケーション)\n販売元:メディアバンク\n年齢制限:18歳未満購入不可\n倫理規定:日本コンテンツ審査センター\n仕様:DVD片面1層/トール黒/20分。", 6 | "cover": "https://pics.dmm.co.jp/mono/movie/adult/62knbm009/62knbm009pl.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "女子校生", 10 | "美少女", 11 | "巨乳", 12 | "サンプル動画", 13 | "ファンタジー" 14 | ], 15 | "genre_id": [ 16 | "1049", 17 | "1051", 18 | "2017", 19 | "6102", 20 | "6123" 21 | ], 22 | "genre_norm": null, 23 | "score": "4.00", 24 | "title": "同居する粘液 第1話日常の中の非日常", 25 | "ori_title": null, 26 | "magnet": null, 27 | "serial": "同居する粘液", 28 | "actress": null, 29 | "actress_pics": null, 30 | "director": null, 31 | "duration": "20", 32 | "producer": "メディアバンク", 33 | "publisher": null, 34 | "uncensored": false, 35 | "publish_date": "2023-11-10", 36 | "preview_pics": [ 37 | "https://pics.dmm.co.jp/digital/video/62knbm009/62knbm009-1.jpg", 38 | "https://pics.dmm.co.jp/digital/video/62knbm009/62knbm009-2.jpg", 39 | "https://pics.dmm.co.jp/digital/video/62knbm009/62knbm009-3.jpg", 40 | "https://pics.dmm.co.jp/digital/video/62knbm009/62knbm009-4.jpg", 41 | "https://pics.dmm.co.jp/digital/video/62knbm009/62knbm009-5.jpg", 42 | "https://pics.dmm.co.jp/digital/video/62knbm009/62knbm009-6.jpg", 43 | "https://pics.dmm.co.jp/digital/video/62knbm009/62knbm009-7.jpg", 44 | "https://pics.dmm.co.jp/digital/video/62knbm009/62knbm009-8.jpg", 45 | "https://pics.dmm.co.jp/digital/video/62knbm009/62knbm009-9.jpg", 46 | "https://pics.dmm.co.jp/digital/video/62knbm009/62knbm009-10.jpg", 47 | "https://pics.dmm.co.jp/digital/video/62knbm009/62knbm009-11.jpg", 48 | "https://pics.dmm.co.jp/digital/video/62knbm009/62knbm009-12.jpg", 49 | "https://pics.dmm.co.jp/digital/video/62knbm009/62knbm009-13.jpg", 50 | "https://pics.dmm.co.jp/digital/video/62knbm009/62knbm009-14.jpg", 51 | "https://pics.dmm.co.jp/digital/video/62knbm009/62knbm009-15.jpg" 52 | ], 53 | "preview_video": null 54 | } -------------------------------------------------------------------------------- /unittest/data/ABC-000 (prestige).json: -------------------------------------------------------------------------------- 1 | { 2 | "注": "此文件用来测试找不到番号时的处理,故各字段故意为空", 3 | "dvdid": "ABC-000", 4 | "cid": null, 5 | "url": null, 6 | "plot": null, 7 | "cover": null, 8 | "big_cover": null, 9 | "genre": null, 10 | "genre_id": null, 11 | "genre_norm": null, 12 | "score": null, 13 | "title": null, 14 | "ori_title": null, 15 | "magnet": null, 16 | "serial": null, 17 | "actress": null, 18 | "actress_pics": null, 19 | "director": null, 20 | "duration": null, 21 | "producer": null, 22 | "publisher": null, 23 | "uncensored": null, 24 | "publish_date": null, 25 | "preview_pics": null, 26 | "preview_video": null 27 | } -------------------------------------------------------------------------------- /unittest/data/ABP-647 (airav).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "ABP-647", 3 | "cid": null, 4 | "url": "https://www.airav.wiki/video/ABP-647", 5 | "plot": "プレステージ専屬女優『瀬名 きらり』があなたの妄想を実現させます!日常生活の中でふとした瞬間に頭をよぎるエッチな妄想の數々。ちょっぴりドジなレストラン店員さんがボクの股間に飲み物をこぼしたなら、責任を感じる彼女にバックヤードで汚れたパンツを脫がしてもらいフェラでご奉仕させるのに!もしも彼女と訪れた喋り聲ひとつしないマンガ喫茶で欲情してしまったら、聲が漏れるのを必至に我慢する姿に興奮してびちょ濡れマ○コを突きまくるのに!男なら誰もが一度は妄想し、股間を膨らませたことのあるエッチな鉄板シチュエーションを完全主観でお屆けする4シチュエーション160分!!", 6 | "cover": "https://wiki-img.airav.wiki/storage/big_pic/99-90-05441.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "顏射", 10 | "口交", 11 | "妄想", 12 | "AV女優片", 13 | "強迫口交" 14 | ], 15 | "genre_id": null, 16 | "genre_norm": null, 17 | "score": null, 18 | "title": "絕對經典場景幹砲 6 瀨名光莉", 19 | "ori_title": null, 20 | "magnet": null, 21 | "serial": null, 22 | "actress": [ 23 | "瀨名光莉" 24 | ], 25 | "actress_pics": null, 26 | "director": null, 27 | "duration": null, 28 | "producer": "PRESTIGE", 29 | "publisher": null, 30 | "uncensored": null, 31 | "publish_date": "2017-10-06", 32 | "preview_pics": [], 33 | "preview_video": "http://freemsall.tnfvbblwhr.work/mp4vod_path/NXTweMhwxTm4_px_IE3qMg/1663999444/0/3600/229090/normal.mp4" 34 | } -------------------------------------------------------------------------------- /unittest/data/ABP-647 (mgstage).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "ABP-647", 3 | "cid": null, 4 | "url": "https://www.mgstage.com/product/product_detail/ABP-647/", 5 | "plot": "【MGSだけのおまけ映像付き(+25分)】 プレステージ専属女優『瀬名きらり』があなたの妄想を実現させます!日常生活の中でふとした瞬間に頭をよぎるエッチな妄想の数々。ちょっぴりドジなレストラン店員さんがボクの股間に飲み物をこぼしたなら、責任を感じる彼女にバックヤードで汚れたパンツを脱がしてもらいフェラでご奉仕させるのに!もしも彼女と訪れた喋り声ひとつしないマンガ喫茶で欲情してしまったら、声が漏れるのを必至に我慢する姿に興奮してびちょ濡れマ○コを突きまくるのに!男なら誰もが一度は妄想し、股間を膨らませたことのあるエッチな鉄板シチュエーションを完全主観でお届けする4シチュエーション160分!!", 6 | "cover": "https://image.mgstage.com/images/prestige/abp/647/pb_e_abp-647.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "企画", 10 | "単体作品", 11 | "顔射", 12 | "イラマチオ", 13 | "コスプレ", 14 | "美尻", 15 | "MGSだけのおまけ映像付き" 16 | ], 17 | "genre_id": null, 18 | "genre_norm": null, 19 | "score": "6.60", 20 | "title": "【MGSだけのおまけ映像付き+25分】絶対的鉄板シチュエーション 6 瀬名きらり", 21 | "ori_title": null, 22 | "magnet": null, 23 | "serial": "絶対的鉄板シチュエーション", 24 | "actress": [ 25 | "瀬名きらり" 26 | ], 27 | "actress_pics": null, 28 | "director": null, 29 | "duration": "185", 30 | "producer": "プレステージ", 31 | "publisher": null, 32 | "uncensored": false, 33 | "publish_date": "2017-09-28", 34 | "preview_pics": [ 35 | "https://image.mgstage.com/images/prestige/abp/647/cap_e_0_abp-647.jpg", 36 | "https://image.mgstage.com/images/prestige/abp/647/cap_e_1_abp-647.jpg", 37 | "https://image.mgstage.com/images/prestige/abp/647/cap_e_2_abp-647.jpg", 38 | "https://image.mgstage.com/images/prestige/abp/647/cap_e_3_abp-647.jpg", 39 | "https://image.mgstage.com/images/prestige/abp/647/cap_e_4_abp-647.jpg", 40 | "https://image.mgstage.com/images/prestige/abp/647/cap_e_5_abp-647.jpg", 41 | "https://image.mgstage.com/images/prestige/abp/647/cap_e_6_abp-647.jpg", 42 | "https://image.mgstage.com/images/prestige/abp/647/cap_e_7_abp-647.jpg", 43 | "https://image.mgstage.com/images/prestige/abp/647/cap_e_8_abp-647.jpg", 44 | "https://image.mgstage.com/images/prestige/abp/647/cap_e_9_abp-647.jpg" 45 | ], 46 | "preview_video": "https://sample.mgstage.com/sample/prestige/abp/647/ABP-647.mp4" 47 | } -------------------------------------------------------------------------------- /unittest/data/ABP-647 (prestige).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "ABP-647", 3 | "cid": null, 4 | "url": "https://www.prestige-av.com/goods/goods_detail.php?sku=ABP-647", 5 | "plot": "プレステージ専属女優『瀬名 きらり』があなたの妄想を実現させます!日常生活の中でふとした瞬間に頭をよぎるエッチな妄想の数々。ちょっぴりドジなレストラン店員さんがボクの股間に飲み物をこぼしたなら、責任を感じる彼女にバックヤードで汚れたパンツを脱がしてもらいフェラでご奉仕させるのに!もしも彼女と訪れた喋り声ひとつしないマンガ喫茶で欲情してしまったら、声が漏れるのを必至に我慢する姿に興奮してびちょ濡れマ○コを突きまくるのに!男なら誰もが一度は妄想し、股間を膨らませたことのあるエッチな鉄板シチュエーションを完全主観でお届けする4シチュエーション160分!!", 6 | "cover": "https://www.prestige-av.com/api/media/goods/prestige/abp/647/pf_abp-647.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "お掃除フェラ", 10 | "イラマチオ", 11 | "女優", 12 | "顔射" 13 | ], 14 | "genre_id": null, 15 | "genre_norm": null, 16 | "score": null, 17 | "title": "絶対的鉄板シチュエーション 6", 18 | "ori_title": null, 19 | "magnet": null, 20 | "serial": "ABSOLUTELY PERFECT", 21 | "actress": [ 22 | "瀬名きらり" 23 | ], 24 | "actress_pics": null, 25 | "director": null, 26 | "duration": "160", 27 | "producer": "プレステージ", 28 | "publisher": null, 29 | "uncensored": false, 30 | "publish_date": "2017-10-05", 31 | "preview_pics": [ 32 | "https://www.prestige-av.com/api/media/goods/prestige/abp/647/cap_e_0_abp-647.jpg", 33 | "https://www.prestige-av.com/api/media/goods/prestige/abp/647/cap_e_1_abp-647.jpg", 34 | "https://www.prestige-av.com/api/media/goods/prestige/abp/647/cap_e_2_abp-647.jpg", 35 | "https://www.prestige-av.com/api/media/goods/prestige/abp/647/cap_e_3_abp-647.jpg", 36 | "https://www.prestige-av.com/api/media/goods/prestige/abp/647/cap_e_4_abp-647.jpg", 37 | "https://www.prestige-av.com/api/media/goods/prestige/abp/647/cap_e_5_abp-647.jpg", 38 | "https://www.prestige-av.com/api/media/goods/prestige/abp/647/cap_e_6_abp-647.jpg", 39 | "https://www.prestige-av.com/api/media/goods/prestige/abp/647/cap_e_7_abp-647.jpg", 40 | "https://www.prestige-av.com/api/media/goods/prestige/abp/647/cap_e_8_abp-647.jpg", 41 | "https://www.prestige-av.com/api/media/goods/prestige/abp/647/cap_e_9_abp-647.jpg" 42 | ], 43 | "preview_video": null 44 | } -------------------------------------------------------------------------------- /unittest/data/AGEMIX-175 (javbus).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "AGEMIX-175", 3 | "cid": null, 4 | "url": "https://www.javbus.com/AGEMIX-175", 5 | "plot": null, 6 | "cover": "https://www.javbus.com/pics/cover/42ic_b.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "乳液", 10 | "姐姐", 11 | "其他戀物癖", 12 | "打手槍" 13 | ], 14 | "genre_id": null, 15 | "genre_norm": [ 16 | "乳汁", 17 | "姐姐", 18 | "其他恋物癖", 19 | "打手枪" 20 | ], 21 | "score": null, 22 | "title": "添い寝手コキ 2", 23 | "ori_title": null, 24 | "magnet": null, 25 | "serial": "添い寝手コキ", 26 | "actress": [ 27 | "坂下えみり", 28 | "上原ソニア", 29 | "茜笑美", 30 | "板野有紀", 31 | "綾瀬れん", 32 | "夏目優希" 33 | ], 34 | "actress_pics": { 35 | "坂下えみり": "https://www.javbus.com/pics/actress/bjl_a.jpg", 36 | "茜笑美": "https://www.javbus.com/pics/actress/8o2_a.jpg", 37 | "板野有紀": "https://www.javbus.com/pics/actress/88e_a.jpg", 38 | "綾瀬れん": "https://www.javbus.com/pics/actress/7nf_a.jpg", 39 | "夏目優希": "https://www.javbus.com/pics/actress/7hw_a.jpg" 40 | }, 41 | "director": null, 42 | "duration": "113", 43 | "producer": "SEXAgent", 44 | "publisher": "SEXAgent", 45 | "uncensored": false, 46 | "publish_date": "2013-12-10", 47 | "preview_pics": [ 48 | "https://pics.dmm.co.jp/digital/video/h_213agemix00175/h_213agemix00175jp-1.jpg", 49 | "https://pics.dmm.co.jp/digital/video/h_213agemix00175/h_213agemix00175jp-2.jpg", 50 | "https://pics.dmm.co.jp/digital/video/h_213agemix00175/h_213agemix00175jp-3.jpg", 51 | "https://pics.dmm.co.jp/digital/video/h_213agemix00175/h_213agemix00175jp-4.jpg", 52 | "https://pics.dmm.co.jp/digital/video/h_213agemix00175/h_213agemix00175jp-5.jpg", 53 | "https://pics.dmm.co.jp/digital/video/h_213agemix00175/h_213agemix00175jp-6.jpg", 54 | "https://pics.dmm.co.jp/digital/video/h_213agemix00175/h_213agemix00175jp-7.jpg", 55 | "https://pics.dmm.co.jp/digital/video/h_213agemix00175/h_213agemix00175jp-8.jpg", 56 | "https://pics.dmm.co.jp/digital/video/h_213agemix00175/h_213agemix00175jp-9.jpg", 57 | "https://pics.dmm.co.jp/digital/video/h_213agemix00175/h_213agemix00175jp-10.jpg" 58 | ], 59 | "preview_video": null 60 | } -------------------------------------------------------------------------------- /unittest/data/AGEMIX-175 (javdb).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "AGEMIX-175", 3 | "cid": null, 4 | "url": "https://javdb.com/v/B88K9", 5 | "plot": null, 6 | "cover": "https://c0.jdbstatic.com/covers/b8/B88K9.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "手淫", 10 | "其他戀物癖", 11 | "姐姐", 12 | "乳液" 13 | ], 14 | "genre_id": null, 15 | "genre_norm": [ 16 | "手淫", 17 | "其他恋物癖", 18 | "姐姐", 19 | "乳汁" 20 | ], 21 | "score": "10.00", 22 | "title": "添い寝手コキ 2", 23 | "ori_title": null, 24 | "magnet": [ 25 | "magnet:?xt=urn:btih:a214b875642480483553c04944f74df4316257a2&dn=AGEMIX-175", 26 | "magnet:?xt=urn:btih:35d0e7eb4bb945698fea8d080503658af7c1bee7&dn=#_agemix-175", 27 | "magnet:?xt=urn:btih:f5cb1fb07f90e8213774c8a31b475dcf9db43adb&dn=AGEMIX-175 添い寝手コキ 2 綾瀬れん 板野有紀 夏目優希 坂下えみり 奏音" 28 | ], 29 | "serial": "添い寝手コキ", 30 | "actress": [ 31 | "板野有紀", 32 | "坂下えみり", 33 | "奏音", 34 | "福咲れん", 35 | "夏目優希" 36 | ], 37 | "actress_pics": null, 38 | "director": null, 39 | "duration": "113", 40 | "producer": "SEX Agent", 41 | "publisher": "SEX Agent", 42 | "uncensored": false, 43 | "publish_date": "2013-08-14", 44 | "preview_pics": [ 45 | "https://c0.jdbstatic.com/samples/b8/B88K9_l_0.jpg", 46 | "https://c0.jdbstatic.com/samples/b8/B88K9_l_1.jpg", 47 | "https://c0.jdbstatic.com/samples/b8/B88K9_l_2.jpg", 48 | "https://c0.jdbstatic.com/samples/b8/B88K9_l_3.jpg", 49 | "https://c0.jdbstatic.com/samples/b8/B88K9_l_4.jpg", 50 | "https://c0.jdbstatic.com/samples/b8/B88K9_l_5.jpg", 51 | "https://c0.jdbstatic.com/samples/b8/B88K9_l_6.jpg", 52 | "https://c0.jdbstatic.com/samples/b8/B88K9_l_7.jpg", 53 | "https://c0.jdbstatic.com/samples/b8/B88K9_l_8.jpg", 54 | "https://c0.jdbstatic.com/samples/b8/B88K9_l_9.jpg" 55 | ], 56 | "preview_video": "https://cc3001.dmm.co.jp/litevideo/freepv/h/h_2/h_213agemix175/h_213agemix175_dm_w.mp4" 57 | } -------------------------------------------------------------------------------- /unittest/data/AGEMIX-175 (javlib).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "AGEMIX-175", 3 | "cid": null, 4 | "url": "https://www.javlibrary.com/cn/?v=javlijbv74", 5 | "plot": null, 6 | "cover": "https://pics.dmm.co.jp/mono/movie/adult/h_213agemix175/h_213agemix175pl.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "打手枪", 10 | "其他恋物癖", 11 | "姐姐", 12 | "乳液" 13 | ], 14 | "genre_id": null, 15 | "genre_norm": null, 16 | "score": null, 17 | "title": "添い寝手コキ 2", 18 | "ori_title": null, 19 | "magnet": null, 20 | "serial": null, 21 | "actress": [ 22 | "夏目优希", 23 | "奏音", 24 | "板野有纪", 25 | "坂下えみり", 26 | "大冢莲" 27 | ], 28 | "actress_pics": null, 29 | "director": null, 30 | "duration": "113", 31 | "producer": "SEX Agent", 32 | "publisher": "SEX Agent", 33 | "uncensored": null, 34 | "publish_date": "2013-08-14", 35 | "preview_pics": null, 36 | "preview_video": null 37 | } -------------------------------------------------------------------------------- /unittest/data/AbW-001 (javlib).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "ABW-001", 3 | "cid": null, 4 | "url": "https://www.javlibrary.com/cn/?v=javmezbn2e", 5 | "plot": null, 6 | "cover": "https://pics.dmm.co.jp/mono/movie/adult/118abw001/118abw001pl.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "中出", 10 | "单体作品", 11 | "巨乳", 12 | "乳交", 13 | "白天出轨" 14 | ], 15 | "genre_id": null, 16 | "genre_norm": null, 17 | "score": "8.20", 18 | "title": "声が出せない状況で…こっそり いちゃラブ「密着」SEX vol.01 エロぉ~い吐息と体温を感じる超接近3本番ALL中出し3連発!! 河合あすな", 19 | "ori_title": null, 20 | "magnet": null, 21 | "serial": null, 22 | "actress": [ 23 | "河合あすな" 24 | ], 25 | "actress_pics": null, 26 | "director": "マサルパンサー", 27 | "duration": "140", 28 | "producer": "プレステージ", 29 | "publisher": "ABSOLUTELY WONDERFUL", 30 | "uncensored": null, 31 | "publish_date": "2020-08-28", 32 | "preview_pics": null, 33 | "preview_video": null 34 | } -------------------------------------------------------------------------------- /unittest/data/CND-037 (airav).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "CND-037", 3 | "cid": null, 4 | "url": "https://www.airav.wiki/video/CND-037", 5 | "plot": null, 6 | "cover": "https://wiki-img.airav.wiki/storage/big_pic/99-07-20067.jpg", 7 | "big_cover": null, 8 | "genre": [], 9 | "genre_id": null, 10 | "genre_norm": null, 11 | "score": null, 12 | "title": null, 13 | "ori_title": null, 14 | "magnet": null, 15 | "serial": null, 16 | "actress": [ 17 | "鈴木心春" 18 | ], 19 | "actress_pics": null, 20 | "director": null, 21 | "duration": null, 22 | "producer": null, 23 | "publisher": null, 24 | "uncensored": null, 25 | "publish_date": "2020-10-31", 26 | "preview_pics": [], 27 | "preview_video": "http://freems5.airav.cc/mv_download/313B10CA071FC910454689BB227D0847/611E6BB3/mv/417137F836FBC097CF313381D102D55B/611E6BB3/0/36000/331522/normal.mp4" 28 | } -------------------------------------------------------------------------------- /unittest/data/DCV-137 (jav321).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "DCV-137", 3 | "cid": "277dcv-137", 4 | "url": "https://en.jav321.com/video/277dcv-137", 5 | "plot": null, 6 | "cover": "https://www.jav321.com/images/documentv/277dcv/137/pb_e_277dcv-137.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "ナンパ", 10 | "パイズリ", 11 | "企画", 12 | "巨乳", 13 | "素人", 14 | "顔射" 15 | ], 16 | "genre_id": [ 17 | "4006", 18 | "5019", 19 | "4007", 20 | "2001", 21 | "4024", 22 | "5023" 23 | ], 24 | "genre_norm": null, 25 | "score": null, 26 | "title": "家まで送ってイイですか? case.137 爆乳元年!シリーズ一番の爆乳!Iカップキャバ嬢!!⇒顔よりもブラジャーの方がデカい!規格外のダイナマイトボディ!⇒爆乳で会話ができる!男を虜にする、エロすぎる接客術とは…⇒男の反応が全て…M男を骨抜きにするチ〇コ&ア〇ルの開発術!⇒SでもありMでもあり…30回イキまくり!⇒”家出時代”男と寝まくったそのワケとは! ", 27 | "ori_title": null, 28 | "magnet": null, 29 | "serial": null, 30 | "actress": [], 31 | "actress_pics": {}, 32 | "director": null, 33 | "duration": "106", 34 | "producer": "ドキュメンTV", 35 | "publisher": null, 36 | "uncensored": null, 37 | "publish_date": "2019-05-17", 38 | "preview_pics": [ 39 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_0_277dcv-137.jpg", 40 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_1_277dcv-137.jpg", 41 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_2_277dcv-137.jpg", 42 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_3_277dcv-137.jpg", 43 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_4_277dcv-137.jpg", 44 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_5_277dcv-137.jpg", 45 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_6_277dcv-137.jpg", 46 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_7_277dcv-137.jpg", 47 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_8_277dcv-137.jpg", 48 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_9_277dcv-137.jpg", 49 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_10_277dcv-137.jpg", 50 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_11_277dcv-137.jpg", 51 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_12_277dcv-137.jpg", 52 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_13_277dcv-137.jpg", 53 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_14_277dcv-137.jpg", 54 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_15_277dcv-137.jpg", 55 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_16_277dcv-137.jpg", 56 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_17_277dcv-137.jpg", 57 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_18_277dcv-137.jpg", 58 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_19_277dcv-137.jpg", 59 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_20_277dcv-137.jpg", 60 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_21_277dcv-137.jpg", 61 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_22_277dcv-137.jpg", 62 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_23_277dcv-137.jpg", 63 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_24_277dcv-137.jpg", 64 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_25_277dcv-137.jpg", 65 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_26_277dcv-137.jpg", 66 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_27_277dcv-137.jpg", 67 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_28_277dcv-137.jpg", 68 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_29_277dcv-137.jpg", 69 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_30_277dcv-137.jpg", 70 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_31_277dcv-137.jpg", 71 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_32_277dcv-137.jpg", 72 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_33_277dcv-137.jpg", 73 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_34_277dcv-137.jpg", 74 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_35_277dcv-137.jpg", 75 | "https://www.jav321.com//images/documentv/277dcv/137/cap_e_36_277dcv-137.jpg" 76 | ], 77 | "preview_video": "https://sample.mgstage.com/sample/documentv/277dcv/137/277DCV-137_sample.mp4" 78 | } -------------------------------------------------------------------------------- /unittest/data/FC2-10000 (fc2).json: -------------------------------------------------------------------------------- 1 | { 2 | "注": "此文件用来测试找不到番号时的处理,故各字段故意为空", 3 | "dvdid": "FC2-10000", 4 | "cid": null, 5 | "url": null, 6 | "plot": null, 7 | "cover": null, 8 | "big_cover": null, 9 | "genre": null, 10 | "genre_id": null, 11 | "genre_norm": null, 12 | "score": null, 13 | "title": null, 14 | "ori_title": null, 15 | "magnet": null, 16 | "serial": null, 17 | "actress": null, 18 | "actress_pics": null, 19 | "director": null, 20 | "duration": null, 21 | "producer": null, 22 | "publisher": null, 23 | "uncensored": null, 24 | "publish_date": null, 25 | "preview_pics": null, 26 | "preview_video": null 27 | } -------------------------------------------------------------------------------- /unittest/data/FC2-10000 (javdb).json: -------------------------------------------------------------------------------- 1 | { 2 | "注": "此文件用来测试找不到番号时的处理,故各字段故意为空", 3 | "dvdid": "FC2-10000", 4 | "cid": null, 5 | "url": null, 6 | "plot": null, 7 | "cover": null, 8 | "big_cover": null, 9 | "genre": null, 10 | "genre_id": null, 11 | "genre_norm": null, 12 | "score": null, 13 | "title": null, 14 | "ori_title": null, 15 | "magnet": null, 16 | "serial": null, 17 | "actress": null, 18 | "actress_pics": null, 19 | "director": null, 20 | "duration": null, 21 | "producer": null, 22 | "publisher": null, 23 | "uncensored": null, 24 | "publish_date": null, 25 | "preview_pics": null, 26 | "preview_video": null 27 | } -------------------------------------------------------------------------------- /unittest/data/FC2-10000 (javmenu).json: -------------------------------------------------------------------------------- 1 | { 2 | "注": "此文件用来测试找不到番号时的处理,故各字段故意为空", 3 | "dvdid": "FC2-10000", 4 | "cid": null, 5 | "url": null, 6 | "plot": null, 7 | "cover": null, 8 | "big_cover": null, 9 | "genre": null, 10 | "genre_id": null, 11 | "genre_norm": null, 12 | "score": null, 13 | "title": null, 14 | "ori_title": null, 15 | "magnet": null, 16 | "serial": null, 17 | "actress": null, 18 | "actress_pics": null, 19 | "director": null, 20 | "duration": null, 21 | "producer": null, 22 | "publisher": null, 23 | "uncensored": null, 24 | "publish_date": null, 25 | "preview_pics": null, 26 | "preview_video": null 27 | } -------------------------------------------------------------------------------- /unittest/data/FC2-1879420 (msin).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "FC2-1879420", 3 | "cid": null, 4 | "url": "https://db.msin.jp/page/movie?id=1675568", 5 | "plot": null, 6 | "cover": "https://img.msin.info/images/cover/fc2/fc2-ppv-1879420.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "素人", 10 | "個人撮影", 11 | "ロリ", 12 | "美少女", 13 | "デルデルシリーズ", 14 | "リーガル" 15 | ], 16 | "genre_id": null, 17 | "genre_norm": null, 18 | "score": null, 19 | "title": "【極上】ロリ美少女リーガルちゃんとガチセックスしてみた", 20 | "ori_title": null, 21 | "magnet": null, 22 | "serial": null, 23 | "actress": [ 24 | "リーガルちゃん(合法ちゃん)" 25 | ], 26 | "actress_pics": { 27 | "リーガルちゃん(合法ちゃん)": "https://img.msin.info/images/actress/30787.jpg" 28 | }, 29 | "director": null, 30 | "duration": "51", 31 | "producer": "deruderuking", 32 | "publisher": null, 33 | "uncensored": null, 34 | "publish_date": "2021-06-20", 35 | "preview_pics": null, 36 | "preview_video": null 37 | } -------------------------------------------------------------------------------- /unittest/data/FC2-1899973 (javmenu).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "FC2-1899973", 3 | "cid": null, 4 | "url": "https://mrzyx.xyz/FC2-1899973", 5 | "plot": null, 6 | "cover": "https://c0.jdbstatic.com/samples/en/ENgb2_l_0.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "私人攝影", 10 | "素人", 11 | "原作", 12 | "美女", 13 | "無碼", 14 | "妻子出軌" 15 | ], 16 | "genre_id": [ 17 | "censored/3011", 18 | "censored/3026", 19 | "censored/3007", 20 | "censored/3028", 21 | "censored/3024", 22 | "censored/3037" 23 | ], 24 | "genre_norm": null, 25 | "score": null, 26 | "title": "【個撮無・衝撃のグラビアGcup美女は社長秘書・NTR・ガチでヤバすぎる動画流出】脱サラした会社の美人社長秘書兼愛人をオカシましたwグラドルだった衝撃Gcup神BODYを貪り鬼イカせ&種付け注入", 27 | "ori_title": null, 28 | "magnet": [ 29 | "magnet:?xt=urn:btih:bb9265f0c1a313e819be75fa264ed35b645924b4&dn=FC2-PPV-1899973", 30 | "magnet:?xt=urn:btih:29aa7a603c59669efd9b0e2f9616aa4bb3a87b3c&dn=FC2PPV-1899973.torrent" 31 | ], 32 | "serial": null, 33 | "actress": null, 34 | "actress_pics": null, 35 | "director": null, 36 | "duration": "104", 37 | "producer": "人事部かとう", 38 | "publisher": null, 39 | "uncensored": null, 40 | "publish_date": "2021-07-03", 41 | "preview_pics": [ 42 | "https://c0.jdbstatic.com/samples/en/ENgb2_l_0.jpg", 43 | "https://c0.jdbstatic.com/samples/en/ENgb2_l_1.jpg", 44 | "https://c0.jdbstatic.com/samples/en/ENgb2_l_2.jpg", 45 | "https://c0.jdbstatic.com/samples/en/ENgb2_l_3.jpg", 46 | "https://c0.jdbstatic.com/samples/en/ENgb2_l_4.jpg", 47 | "https://c0.jdbstatic.com/samples/en/ENgb2_l_5.jpg", 48 | "https://c0.jdbstatic.com/samples/en/ENgb2_l_6.jpg", 49 | "https://c0.jdbstatic.com/samples/en/ENgb2_l_7.jpg", 50 | "https://c0.jdbstatic.com/samples/en/ENgb2_l_8.jpg", 51 | "https://c0.jdbstatic.com/samples/en/ENgb2_l_9.jpg" 52 | ], 53 | "preview_video": null 54 | } -------------------------------------------------------------------------------- /unittest/data/FC2-238629 (avsox).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "FC2-238629", 3 | "cid": null, 4 | "url": null, 5 | "plot": null, 6 | "cover": null, 7 | "big_cover": null, 8 | "genre": null, 9 | "genre_id": null, 10 | "genre_norm": null, 11 | "score": null, 12 | "title": null, 13 | "ori_title": null, 14 | "magnet": null, 15 | "serial": null, 16 | "actress": null, 17 | "actress_pics": null, 18 | "director": null, 19 | "duration": null, 20 | "producer": null, 21 | "publisher": null, 22 | "uncensored": null, 23 | "publish_date": null, 24 | "preview_pics": null, 25 | "preview_video": null 26 | } -------------------------------------------------------------------------------- /unittest/data/FC2-238629 (msin).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "FC2-238629", 3 | "cid": null, 4 | "url": "https://db.msin.jp/page/movie?id=2124358", 5 | "plot": null, 6 | "cover": null, 7 | "big_cover": null, 8 | "genre": [], 9 | "genre_id": null, 10 | "genre_norm": null, 11 | "score": null, 12 | "title": "佐々波綾无码流出", 13 | "ori_title": null, 14 | "magnet": null, 15 | "serial": null, 16 | "actress": [], 17 | "actress_pics": {}, 18 | "director": null, 19 | "duration": null, 20 | "producer": null, 21 | "publisher": null, 22 | "uncensored": null, 23 | "publish_date": null, 24 | "preview_pics": null, 25 | "preview_video": null 26 | } -------------------------------------------------------------------------------- /unittest/data/FC2-2764073 (javmenu).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "FC2-2764073", 3 | "cid": null, 4 | "url": "https://mrzyx.xyz/FC2-2764073", 5 | "plot": null, 6 | "cover": "https://c0.jdbstatic.com/samples/d0/d0z1g_l_0.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "私人攝影", 10 | "素人", 11 | "內射", 12 | "原作", 13 | "流出", 14 | "可愛", 15 | "年輕" 16 | ], 17 | "genre_id": [ 18 | "censored/3011", 19 | "censored/3026", 20 | "censored/3018", 21 | "censored/3007", 22 | "censored/3022", 23 | "censored/3020", 24 | "censored/3031" 25 | ], 26 | "genre_norm": null, 27 | "score": null, 28 | "title": "※年度末限定4980→980pt【違法/売 春] U⓯未経験の娘。刑法に触れているので早めに削除します。", 29 | "ori_title": null, 30 | "magnet": [ 31 | "magnet:?xt=urn:btih:9bc8d2355e76e0ae689596a44aad5059d12e9fdb&dn=FC2PPV 2764073 ※年度末限定4980→980pt【違法 売 春] U⓯未経験の娘。刑法に触れているので早めに削除します。 [有].mp4", 32 | "magnet:?xt=urn:btih:a6f20f3bd652c0f303b736cdcf9487f2f478007d&dn=FC2-PPV-2764073", 33 | "magnet:?xt=urn:btih:fd58c43a7787c03e8f74db6bc66901c90acae9e8&dn=FC2-PPV-2764073" 34 | ], 35 | "serial": null, 36 | "actress": [ 37 | "西宮このみ" 38 | ], 39 | "actress_pics": null, 40 | "director": null, 41 | "duration": "30", 42 | "producer": "えろぷり", 43 | "publisher": null, 44 | "uncensored": null, 45 | "publish_date": "2022-03-30", 46 | "preview_pics": [ 47 | "https://c0.jdbstatic.com/samples/d0/d0z1g_l_0.jpg" 48 | ], 49 | "preview_video": null 50 | } -------------------------------------------------------------------------------- /unittest/data/FC2-3189680 (javdb).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "FC2-3189680", 3 | "cid": null, 4 | "url": "https://javdb.com/v/rmVapJ", 5 | "plot": null, 6 | "cover": "https://c0.jdbstatic.com/covers/rm/rmVapJ.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "私人攝影", 10 | "制服", 11 | "內射" 12 | ], 13 | "genre_id": [ 14 | "fc2?c1=11", 15 | "fc2?c1=19", 16 | "fc2?c1=18" 17 | ], 18 | "genre_norm": null, 19 | "score": "8.04", 20 | "title": "【体育館倉庫】某ハーフ子役モデルを高額援助。計2回のゴムなし大量中出し。※4K特典(1時間越え)", 21 | "ori_title": null, 22 | "magnet": [ 23 | "magnet:?xt=urn:btih:6d4fed9103648ab2e4a22697f363bd243f7feffa&dn=FC2-3189680", 24 | "magnet:?xt=urn:btih:4dea0950176f3c5af0f14ad96528f79d7bdc48fa&dn=FC2PPV 3189680" 25 | ], 26 | "serial": null, 27 | "actress": [ 28 | "永瀬ゆい" 29 | ], 30 | "actress_pics": null, 31 | "director": null, 32 | "duration": "49", 33 | "producer": "体育館倉庫", 34 | "publisher": null, 35 | "uncensored": null, 36 | "publish_date": "2023-02-20", 37 | "preview_pics": [ 38 | "https://c0.jdbstatic.com/samples/rm/rmVapJ_l_0.jpg" 39 | ], 40 | "preview_video": "https://javdb.com/v/rmVapJ" 41 | } -------------------------------------------------------------------------------- /unittest/data/FC2-626157 (msin).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "FC2-626157", 3 | "cid": null, 4 | "url": "https://db.msin.jp/page/movie?id=2233456", 5 | "plot": null, 6 | "cover": null, 7 | "big_cover": null, 8 | "genre": [], 9 | "genre_id": null, 10 | "genre_norm": null, 11 | "score": null, 12 | "title": "ちひろ24歳♪超S級昼顔妻♪【2時間36分】《素人ハメ撮り》《個人撮影》《156》《ちゅぱ王》", 13 | "ori_title": null, 14 | "magnet": null, 15 | "serial": null, 16 | "actress": [ 17 | "ちひろ24歳" 18 | ], 19 | "actress_pics": { 20 | "ちひろ24歳": "https://db.msin.jp/.svg/Noimage.png" 21 | }, 22 | "director": null, 23 | "duration": null, 24 | "producer": "《ちゅぱ王》素人ハメ撮り", 25 | "publisher": null, 26 | "uncensored": null, 27 | "publish_date": "2017-08-03", 28 | "preview_pics": null, 29 | "preview_video": null 30 | } -------------------------------------------------------------------------------- /unittest/data/FC2-718323 (avsox).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "FC2-718323", 3 | "cid": null, 4 | "url": "https://avsox.click/cn/movie/8747275c332b5050", 5 | "plot": null, 6 | "cover": "https://file.netcdn.space/storage/fc2ppv/718323/pl.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "内射", 10 | "第一视角", 11 | "人妻", 12 | "美人" 13 | ], 14 | "genre_id": null, 15 | "genre_norm": null, 16 | "score": null, 17 | "title": "【個人撮影】破壊力抜群のデカケツ美人妻れいなさんと再戦そして大量中出し【背徳の制服編】", 18 | "ori_title": null, 19 | "magnet": null, 20 | "serial": null, 21 | "actress": [], 22 | "actress_pics": null, 23 | "director": null, 24 | "duration": "77", 25 | "producer": "EX-STANDARD", 26 | "publisher": null, 27 | "uncensored": null, 28 | "publish_date": "2017-11-30", 29 | "preview_pics": null, 30 | "preview_video": null 31 | } -------------------------------------------------------------------------------- /unittest/data/FC2-718323 (fc2).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "FC2-718323", 3 | "cid": null, 4 | "url": "https://adult.contents.fc2.com/article/718323/", 5 | "plot": null, 6 | "cover": "https://storage58000.contents.fc2.com/file/315/31464497/1642819488.71.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "人妻", 10 | "ハメ撮り", 11 | "中出し", 12 | "個人撮影", 13 | "オリジナル", 14 | "無修正", 15 | "寝取られ", 16 | "美人", 17 | "可愛い", 18 | "生ハメ" 19 | ], 20 | "genre_id": null, 21 | "genre_norm": null, 22 | "score": "9.91", 23 | "title": "【個人撮影】", 24 | "ori_title": null, 25 | "magnet": null, 26 | "serial": null, 27 | "actress": null, 28 | "actress_pics": null, 29 | "director": null, 30 | "duration": "78", 31 | "producer": "EX-STANDARD", 32 | "publisher": null, 33 | "uncensored": null, 34 | "publish_date": "2017-11-30", 35 | "preview_pics": [ 36 | "https://storage58000.contents.fc2.com/file/315/31464497/1642819488.71.jpg", 37 | "https://storage58000.contents.fc2.com/file/315/31464497/1642819488.8.jpg", 38 | "https://storage58000.contents.fc2.com/file/315/31464497/1642819489.29.jpg", 39 | "https://storage58000.contents.fc2.com/file/315/31464497/1642819489.42.jpg", 40 | "https://storage58000.contents.fc2.com/file/315/31464497/1642819489.71.jpg", 41 | "https://storage58000.contents.fc2.com/file/315/31464497/1642819489.95.jpg", 42 | "https://storage58000.contents.fc2.com/file/315/31464497/1642819490.22.jpg", 43 | "https://storage58000.contents.fc2.com/file/315/31464497/1642819490.49.jpg", 44 | "https://storage58000.contents.fc2.com/file/315/31464497/1642819490.76.jpg", 45 | "https://storage58000.contents.fc2.com/file/315/31464497/1642819491.01.jpg" 46 | ], 47 | "preview_video": "https://vip-videoprem49000.fc2.com/up/201711/29/L/4/cut20171129L0ACggL4.mp4?mid=17fa28e28423683ba038449d47f9aa09" 48 | } -------------------------------------------------------------------------------- /unittest/data/FC2-718323 (javmenu).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "FC2-718323", 3 | "cid": null, 4 | "url": "https://mrzyx.xyz/FC2-718323", 5 | "plot": null, 6 | "cover": "https://c0.jdbstatic.com/samples/dw/DWD4a_l_0.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "私人攝影", 10 | "內射", 11 | "原作", 12 | "美女", 13 | "無碼", 14 | "無套性交", 15 | "可愛", 16 | "人妻" 17 | ], 18 | "genre_id": [ 19 | "censored/3011", 20 | "censored/3018", 21 | "censored/3007", 22 | "censored/3028", 23 | "censored/3024", 24 | "censored/3049", 25 | "censored/3020", 26 | "censored/3043" 27 | ], 28 | "genre_norm": null, 29 | "score": null, 30 | "title": "【個人撮影】破壊力抜群のデカケツ美人妻れいなさんと再戦そして大量中出し【背徳の制服編】", 31 | "ori_title": null, 32 | "magnet": [ 33 | "magnet:?xt=urn:btih:f9b5540026ed91a447497a2a8faddd586cc77e61&dn=[Thz.la]fc2ppv_718323", 34 | "magnet:?xt=urn:btih:8bd621d4a3ed19ad24dd66998ffe708c58d98576&dn=fc2-718323-hd", 35 | "magnet:?xt=urn:btih:5ebf15f5fc430694bdf049a1500ccc18c9025f66&dn=fc2-718323-hd", 36 | "magnet:?xt=urn:btih:6211845d561af6ad1eab273236a5b0e50878bbb4&dn=fc2-718323" 37 | ], 38 | "serial": null, 39 | "actress": null, 40 | "actress_pics": null, 41 | "director": null, 42 | "duration": "77", 43 | "producer": "EX-STANDARD", 44 | "publisher": null, 45 | "uncensored": null, 46 | "publish_date": "2017-11-30", 47 | "preview_pics": [ 48 | "https://c0.jdbstatic.com/samples/dw/DWD4a_l_0.jpg", 49 | "https://c0.jdbstatic.com/samples/dw/DWD4a_l_1.jpg", 50 | "https://c0.jdbstatic.com/samples/dw/DWD4a_l_2.jpg", 51 | "https://c0.jdbstatic.com/samples/dw/DWD4a_l_3.jpg", 52 | "https://c0.jdbstatic.com/samples/dw/DWD4a_l_4.jpg", 53 | "https://c0.jdbstatic.com/samples/dw/DWD4a_l_5.jpg", 54 | "https://c0.jdbstatic.com/samples/dw/DWD4a_l_6.jpg", 55 | "https://c0.jdbstatic.com/samples/dw/DWD4a_l_7.jpg", 56 | "https://c0.jdbstatic.com/samples/dw/DWD4a_l_8.jpg", 57 | "https://c0.jdbstatic.com/samples/dw/DWD4a_l_9.jpg" 58 | ], 59 | "preview_video": null 60 | } -------------------------------------------------------------------------------- /unittest/data/FC2-718323 (msin).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "FC2-718323", 3 | "cid": null, 4 | "url": "https://db.msin.jp/page/movie?id=6593", 5 | "plot": null, 6 | "cover": "https://img.msin.info/images/cover/fc2/fc2-ppv-718323.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "人妻", 10 | "ハメ撮り", 11 | "中出し", 12 | "個人撮影", 13 | "オリジナル", 14 | "無修正", 15 | "寝取られ", 16 | "美人", 17 | "可愛い", 18 | "生ハメ" 19 | ], 20 | "genre_id": null, 21 | "genre_norm": null, 22 | "score": null, 23 | "title": "【個人撮影】破壊力抜群のデカケツ美人妻れいなさんと再戦そして大量中出し【背徳の制服編】", 24 | "ori_title": null, 25 | "magnet": null, 26 | "serial": null, 27 | "actress": [ 28 | "れいな29歳(桜瀬奈)" 29 | ], 30 | "actress_pics": { 31 | "れいな29歳(桜瀬奈)": "https://img.msin.info/images/actress/230.jpg" 32 | }, 33 | "director": null, 34 | "duration": "78", 35 | "producer": "EX-STANDARD", 36 | "publisher": null, 37 | "uncensored": null, 38 | "publish_date": "2017-11-30", 39 | "preview_pics": null, 40 | "preview_video": null 41 | } -------------------------------------------------------------------------------- /unittest/data/FC2-985469 (avsox).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "FC2-985469", 3 | "cid": null, 4 | "url": "https://avsox.click/cn/movie/7f993993987fe5b9", 5 | "plot": null, 6 | "cover": "https://file.netcdn.space/storage/fc2ppv/985469/pl.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "素人", 10 | "内射", 11 | "第一视角", 12 | "角色扮演", 13 | "恋物癖" 14 | ], 15 | "genre_id": null, 16 | "genre_norm": null, 17 | "score": null, 18 | "title": "【個人撮影】JD2回生ちゃんに中出し!エロマンガ先生のパジャマコスで中出しえっちさせててもらいました♪", 19 | "ori_title": null, 20 | "magnet": null, 21 | "serial": null, 22 | "actress": [], 23 | "actress_pics": null, 24 | "director": null, 25 | "duration": "113", 26 | "producer": "COS☆ぱこ", 27 | "publisher": null, 28 | "uncensored": null, 29 | "publish_date": "2018-11-23", 30 | "preview_pics": null, 31 | "preview_video": null 32 | } -------------------------------------------------------------------------------- /unittest/data/FC2-985469 (javdb).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "FC2-985469", 3 | "cid": null, 4 | "url": "https://javdb.com/v/nzA44", 5 | "plot": null, 6 | "cover": "https://c0.jdbstatic.com/covers/nz/nzA44.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "私人攝影", 10 | "素人", 11 | "內射", 12 | "原作", 13 | "無碼", 14 | "角色扮演", 15 | "戀物癖", 16 | "可愛" 17 | ], 18 | "genre_id": [ 19 | "fc2?c1=11", 20 | "fc2?c1=26", 21 | "fc2?c1=18", 22 | "fc2?c1=7", 23 | "fc2?c1=24", 24 | "fc2?c1=9", 25 | "fc2?c1=13", 26 | "fc2?c1=20" 27 | ], 28 | "genre_norm": null, 29 | "score": "8.82", 30 | "title": "【個人撮影・無】JD2回生ちゃんに中出し!エロマンガ先生のパジャマコスで中出しえっちさせててもらいました♪", 31 | "ori_title": null, 32 | "magnet": [ 33 | "magnet:?xt=urn:btih:903ecbf73fd1a466e11e9454388c77c854f2463f&dn=FC2PPV-985469", 34 | "magnet:?xt=urn:btih:851c21dab8d9a4883e8940240107aa94e5e0905d&dn=FC2-PPV-985469", 35 | "magnet:?xt=urn:btih:649d36f0fd470f5950a1edc6fbb5922a1a82a39e&dn=[7sht.me]FC2PPV-985469", 36 | "magnet:?xt=urn:btih:b2a57edade565821bd611b89dfca1f8d0e63e891&dn=fc2-985469", 37 | "magnet:?xt=urn:btih:6be7ae12c4be3a0f7e27d1720b4e23e1134dcfd1&dn=FC2-985469", 38 | "magnet:?xt=urn:btih:e2eaaaa085d14a010ff014991f3ef8ac48954920&dn=FC2-PPV-985469-HD", 39 | "magnet:?xt=urn:btih:fc56fea5e868c3c28f2e3cd1731adc347c55267c&dn=FC2-PPV-983579-985469-纱雾" 40 | ], 41 | "serial": null, 42 | "actress": [], 43 | "actress_pics": null, 44 | "director": null, 45 | "duration": "113", 46 | "producer": "COS☆ぱこ", 47 | "publisher": null, 48 | "uncensored": null, 49 | "publish_date": "2018-11-23", 50 | "preview_pics": [ 51 | "https://c0.jdbstatic.com/samples/nz/nzA44_l_0.jpg", 52 | "https://c0.jdbstatic.com/samples/nz/nzA44_l_1.jpg", 53 | "https://c0.jdbstatic.com/samples/nz/nzA44_l_2.jpg", 54 | "https://c0.jdbstatic.com/samples/nz/nzA44_l_3.jpg", 55 | "https://c0.jdbstatic.com/samples/nz/nzA44_l_4.jpg", 56 | "https://c0.jdbstatic.com/samples/nz/nzA44_l_5.jpg", 57 | "https://c0.jdbstatic.com/samples/nz/nzA44_l_6.jpg", 58 | "https://c0.jdbstatic.com/samples/nz/nzA44_l_7.jpg", 59 | "https://c0.jdbstatic.com/samples/nz/nzA44_l_8.jpg", 60 | "https://c0.jdbstatic.com/samples/nz/nzA44_l_9.jpg" 61 | ], 62 | "preview_video": "https://javdb.com/v/nzA44" 63 | } -------------------------------------------------------------------------------- /unittest/data/FC2-985469 (msin).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "FC2-985469", 3 | "cid": null, 4 | "url": "https://db.msin.jp/page/movie?id=4208", 5 | "plot": null, 6 | "cover": "https://img.msin.info/images/cover/fc2/fc2-ppv-985469.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "ハメ撮り", 10 | "素人", 11 | "中出し", 12 | "個人撮影", 13 | "オリジナル", 14 | "無修正", 15 | "コスプレ", 16 | "フェチ", 17 | "可愛い", 18 | "JD" 19 | ], 20 | "genre_id": null, 21 | "genre_norm": null, 22 | "score": null, 23 | "title": "【個人撮影】JD2回生ちゃんに中出し!エロマンガ先生のパジャマコスで中出しえっちさせててもらいました♪ LAXD-PPV-985469", 24 | "ori_title": null, 25 | "magnet": null, 26 | "serial": null, 27 | "actress": [ 28 | "JD2回ちゃん(やみこ)" 29 | ], 30 | "actress_pics": { 31 | "JD2回ちゃん(やみこ)": "https://img.msin.info/images/actress/29037.jpg" 32 | }, 33 | "director": null, 34 | "duration": null, 35 | "producer": "COS☆ぱこ", 36 | "publisher": null, 37 | "uncensored": null, 38 | "publish_date": "2018-11-23", 39 | "preview_pics": null, 40 | "preview_video": null 41 | } -------------------------------------------------------------------------------- /unittest/data/GANA-2156 (javbus).json: -------------------------------------------------------------------------------- 1 | { 2 | "注": "此文件用来测试找不到番号时的处理,故各字段故意为空", 3 | "dvdid": "GANA-2156", 4 | "cid": null, 5 | "url": null, 6 | "plot": null, 7 | "cover": null, 8 | "big_cover": null, 9 | "genre": null, 10 | "genre_id": null, 11 | "genre_norm": null, 12 | "score": null, 13 | "title": null, 14 | "ori_title": null, 15 | "magnet": null, 16 | "serial": null, 17 | "actress": null, 18 | "actress_pics": null, 19 | "director": null, 20 | "duration": null, 21 | "producer": null, 22 | "publisher": null, 23 | "uncensored": null, 24 | "publish_date": null, 25 | "preview_pics": null, 26 | "preview_video": null 27 | } -------------------------------------------------------------------------------- /unittest/data/GETCHU-4016932 (dl_getchu).json: -------------------------------------------------------------------------------- 1 | { 2 | "注": "此文件用来测试找不到番号时的处理,故各字段故意为空", 3 | "dvdid": "GETCHU-4016932", 4 | "cid": null, 5 | "url": null, 6 | "plot": null, 7 | "cover": null, 8 | "big_cover": null, 9 | "genre": null, 10 | "genre_id": null, 11 | "genre_norm": null, 12 | "score": null, 13 | "title": null, 14 | "ori_title": null, 15 | "magnet": null, 16 | "serial": null, 17 | "actress": null, 18 | "actress_pics": null, 19 | "director": null, 20 | "duration": null, 21 | "producer": null, 22 | "publisher": null, 23 | "uncensored": null, 24 | "publish_date": null, 25 | "preview_pics": null, 26 | "preview_video": null 27 | } -------------------------------------------------------------------------------- /unittest/data/GETCHU-4053720 (dl_getchu).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "GETCHU-4053720", 3 | "cid": null, 4 | "url": "https://dl.getchu.com/i/item4053720", 5 | "plot": "きょうはブルー●ーカイブの時間だよ!", 6 | "cover": "https://dl.getchu.com/data/item_img/40537/4053720/4053720top.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "巨乳", 10 | "中出し", 11 | "足コキ", 12 | "お姉さん" 13 | ], 14 | "genre_id": null, 15 | "genre_norm": null, 16 | "score": null, 17 | "title": "アスナKカップ爆乳バニー〜バニー着たままタイツ破いて中出しSEX〜", 18 | "ori_title": null, 19 | "magnet": null, 20 | "serial": null, 21 | "actress": [ 22 | "丹雫ひよ" 23 | ], 24 | "actress_pics": null, 25 | "director": null, 26 | "duration": "67", 27 | "producer": "スタジオキルシェ/ひよひよくらぶ", 28 | "publisher": null, 29 | "uncensored": null, 30 | "publish_date": "2023-12-06", 31 | "preview_pics": [ 32 | "https://dl.getchu.com/data/item_img/40537/4053720/4053720_2977.jpg" 33 | ], 34 | "preview_video": null 35 | } -------------------------------------------------------------------------------- /unittest/data/HRV-045 (mgstage).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "HRV-045", 3 | "cid": null, 4 | "url": "https://www.mgstage.com/product/product_detail/HRV-045/", 5 | "plot": "可愛いボクの妹・綺麗なボクのお姉ちゃん・・豪華女優陣「野々浦暖・長谷川るい・愛音まりあ・瀬戸きらり・結まきな・乙都さきの」6名収録!妹の大人っぽくなって成長したカラダに欲情してしまったボクは、感情を抑えきれず両親に秘密エッチな関係を持つようになり・・・綺麗なお姉ちゃんに誘惑されエッチな手ほどきをされるとボクは興奮し徐々にエスカレートし責めまくる!", 6 | "cover": "https://image.mgstage.com/images/prestige/hrv/045/pb_e_hrv-045.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "4時間以上作品", 10 | "中出し", 11 | "主観", 12 | "ベスト・総集編", 13 | "姉・妹", 14 | "美少女" 15 | ], 16 | "genre_id": null, 17 | "genre_norm": null, 18 | "score": "6.00", 19 | "title": "ボクと姉・妹のエッチな同棲性活。 BEST 8時間 えっちな姉妹と禁断のいちゃラブSEXライフ 野々浦暖 愛音まりあ 長谷川るい 瀬名きらり 結まきな", 20 | "ori_title": null, 21 | "magnet": null, 22 | "serial": null, 23 | "actress": [ 24 | "愛音まりあ", 25 | "乙都さきの", 26 | "結まきな", 27 | "瀬名きらり", 28 | "長谷川るい", 29 | "野々浦暖" 30 | ], 31 | "actress_pics": null, 32 | "director": null, 33 | "duration": "480", 34 | "producer": "プレステージ", 35 | "publisher": null, 36 | "uncensored": false, 37 | "publish_date": "2020-10-08", 38 | "preview_pics": [ 39 | "https://image.mgstage.com/images/prestige/hrv/045/cap_e_0_hrv-045.jpg", 40 | "https://image.mgstage.com/images/prestige/hrv/045/cap_e_1_hrv-045.jpg", 41 | "https://image.mgstage.com/images/prestige/hrv/045/cap_e_2_hrv-045.jpg", 42 | "https://image.mgstage.com/images/prestige/hrv/045/cap_e_3_hrv-045.jpg", 43 | "https://image.mgstage.com/images/prestige/hrv/045/cap_e_4_hrv-045.jpg", 44 | "https://image.mgstage.com/images/prestige/hrv/045/cap_e_5_hrv-045.jpg", 45 | "https://image.mgstage.com/images/prestige/hrv/045/cap_e_6_hrv-045.jpg", 46 | "https://image.mgstage.com/images/prestige/hrv/045/cap_e_7_hrv-045.jpg", 47 | "https://image.mgstage.com/images/prestige/hrv/045/cap_e_8_hrv-045.jpg", 48 | "https://image.mgstage.com/images/prestige/hrv/045/cap_e_9_hrv-045.jpg" 49 | ], 50 | "preview_video": "https://sample.mgstage.com/sample/prestige/hrv/045/hrv-045_20201006T161301.mp4" 51 | } -------------------------------------------------------------------------------- /unittest/data/ION-020 (javdb).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "ION-020", 3 | "cid": null, 4 | "url": "https://javdb.com/v/9qvxw", 5 | "plot": null, 6 | "cover": "https://c0.jdbstatic.com/covers/9q/9qvxw.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "精選綜合", 10 | "素人作品", 11 | "中出", 12 | "美少女電影", 13 | "偷窺", 14 | "白天出軌" 15 | ], 16 | "genre_id": null, 17 | "genre_norm": [ 18 | "精选?综合", 19 | "素人作品", 20 | "中出", 21 | "美少女电影", 22 | "偷窥", 23 | "白天出轨" 24 | ], 25 | "score": "8.00", 26 | "title": "萌", 27 | "ori_title": null, 28 | "magnet": [ 29 | "magnet:?xt=urn:btih:faabed261ac6aa88f8d87d1f180164a9d28b41c3&dn=ion-020 .mp4", 30 | "magnet:?xt=urn:btih:723aaf5998de9e55e483c33e5558a7417803a569&dn=ION-020.mp4", 31 | "magnet:?xt=urn:btih:3b41b6245efc53fb4ba2690a7cbd6bd4426b1534&dn=ION-020", 32 | "magnet:?xt=urn:btih:4c50351a17c2af97441243caa4f7d304b01a5aa7&dn=ion-020", 33 | "magnet:?xt=urn:btih:4c53a9f79fa366cbfbe2ca0673ab453fe2e8dd3b&dn=ion-020", 34 | "magnet:?xt=urn:btih:35122181963b19e1e4a544dae590bf37e55dd862&dn=ion-020", 35 | "magnet:?xt=urn:btih:416ddb3abdea58d28ee8e95f5b2483cb1b1a256b&dn=ion-020", 36 | "magnet:?xt=urn:btih:9ae11166c949e0dceefd640b234284334fb85b74&dn=ion-020" 37 | ], 38 | "serial": null, 39 | "actress": [ 40 | "七瀬ひな" 41 | ], 42 | "actress_pics": null, 43 | "director": null, 44 | "duration": "59", 45 | "producer": null, 46 | "publisher": "ION イイ女を寝取りたい", 47 | "uncensored": false, 48 | "publish_date": "2019-10-02", 49 | "preview_pics": [ 50 | "https://c0.jdbstatic.com/samples/9q/9qvxw_l_0.jpg", 51 | "https://c0.jdbstatic.com/samples/9q/9qvxw_l_1.jpg", 52 | "https://c0.jdbstatic.com/samples/9q/9qvxw_l_2.jpg", 53 | "https://c0.jdbstatic.com/samples/9q/9qvxw_l_3.jpg", 54 | "https://c0.jdbstatic.com/samples/9q/9qvxw_l_4.jpg" 55 | ], 56 | "preview_video": "https://cc3001.dmm.co.jp/litevideo/freepv/i/ion/ion020/ion020_dm_w.mp4" 57 | } -------------------------------------------------------------------------------- /unittest/data/IPX-177 (airav).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "IPX-177", 3 | "cid": null, 4 | "url": "https://www.airav.wiki/video/IPX-177", 5 | "plot": "迷你裙與過膝襪間的絕對領域!讓人忍不住想摸的Q彈肌膚、肉感大腿!邊看這絕對領域邊足交、股交、臀交!享受著衣幹砲快感,讓身穿過膝襪的小惡魔罵到高潮!", 6 | "cover": "https://wiki-img.airav.wiki/storage/big_pic/99-91-05781.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "癡女", 10 | "中文字幕", 11 | "打手槍", 12 | "美少女", 13 | "AV女優片", 14 | "美腿", 15 | "HD高畫質" 16 | ], 17 | "genre_id": null, 18 | "genre_norm": null, 19 | "score": null, 20 | "title": "讓高傲妹妹穿過膝襪露絕對領域癡女玩弄 相澤南", 21 | "ori_title": null, 22 | "magnet": null, 23 | "serial": null, 24 | "actress": [ 25 | "相澤南" 26 | ], 27 | "actress_pics": null, 28 | "director": null, 29 | "duration": null, 30 | "producer": "IDEA POCKET", 31 | "publisher": null, 32 | "uncensored": null, 33 | "publish_date": "2018-07-19", 34 | "preview_pics": [], 35 | "preview_video": "http://freems.airav.cc/mv_download/E40E9F7C8A4289EBF8E854FF1286CD7B/611E71C4/mv/E392B8E7244E94E20124731BB1658894/611E71C4/0/36000/267912/normal.mp4" 36 | } -------------------------------------------------------------------------------- /unittest/data/IPX-177 (jav321).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "IPX-177", 3 | "cid": "ipx00177", 4 | "url": "https://en.jav321.com/video/ipx00177", 5 | "plot": "ミニスカートとニーハイから覗くきれいな素肌、ニーソの太ももへの食い込み…全てを兼ね備えた「絶対領域」!ピチピチの肌感、ぷにぷにハミもも思わず触りたくなること間違いなし!さらに絶対領域を眺めながら足コキ、股コキ、尻コキ!完全着衣で堪能!!ニーハイを履いた小悪魔に罵られながらイク!!「マヂきもいんだけど!触り方が!!」「完璧すぎる、、絶対領域はぁはぁ」", 6 | "cover": "http://pics.dmm.co.jp//digital/video/ipx00177/ipx00177pl.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "デジモ", 10 | "ハイビジョン", 11 | "単体作品", 12 | "手コキ", 13 | "独占配信", 14 | "痴女", 15 | "美少女", 16 | "脚フェチ" 17 | ], 18 | "genre_id": [ 19 | "6004", 20 | "6533", 21 | "4025", 22 | "5004", 23 | "6548", 24 | "1031", 25 | "1027", 26 | "4008" 27 | ], 28 | "genre_norm": null, 29 | "score": "7.0", 30 | "title": "生意気な妹にニーハイを履かせ僕だけの「絶対領域」を誕生させ僕好みに痴女らせた。 相沢みなみ ", 31 | "ori_title": null, 32 | "magnet": null, 33 | "serial": null, 34 | "actress": [], 35 | "actress_pics": {}, 36 | "director": null, 37 | "duration": "170", 38 | "producer": "アイデアポケット", 39 | "publisher": null, 40 | "uncensored": null, 41 | "publish_date": "2018-07-19", 42 | "preview_pics": [ 43 | "http://pics.dmm.co.jp/digital/video/ipx00177/ipx00177jp-1.jpg", 44 | "http://pics.dmm.co.jp/digital/video/ipx00177/ipx00177jp-2.jpg", 45 | "http://pics.dmm.co.jp/digital/video/ipx00177/ipx00177jp-3.jpg", 46 | "http://pics.dmm.co.jp/digital/video/ipx00177/ipx00177jp-4.jpg", 47 | "http://pics.dmm.co.jp/digital/video/ipx00177/ipx00177jp-5.jpg", 48 | "http://pics.dmm.co.jp/digital/video/ipx00177/ipx00177jp-6.jpg", 49 | "http://pics.dmm.co.jp/digital/video/ipx00177/ipx00177jp-7.jpg", 50 | "http://pics.dmm.co.jp/digital/video/ipx00177/ipx00177jp-8.jpg", 51 | "http://pics.dmm.co.jp/digital/video/ipx00177/ipx00177jp-9.jpg", 52 | "http://pics.dmm.co.jp/digital/video/ipx00177/ipx00177jp-10.jpg", 53 | "http://pics.dmm.co.jp/digital/video/ipx00177/ipx00177jp-11.jpg", 54 | "http://pics.dmm.co.jp/digital/video/ipx00177/ipx00177jp-12.jpg" 55 | ], 56 | "preview_video": "http://awspv3001.r18.com/litevideo/freepv/i/ipx/ipx00177/ipx00177_dmb_w.mp4" 57 | } -------------------------------------------------------------------------------- /unittest/data/IPX-177 (javbus).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "IPX-177", 3 | "cid": null, 4 | "url": "https://www.javbus.com/IPX-177", 5 | "plot": null, 6 | "cover": "https://www.javbus.com/pics/cover/6n54_b.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "單體作品", 10 | "DMM獨家", 11 | "打手槍", 12 | "蕩婦", 13 | "戀腿癖", 14 | "美少女", 15 | "數位馬賽克", 16 | "高畫質" 17 | ], 18 | "genre_id": null, 19 | "genre_norm": [ 20 | "单体作品", 21 | "打手枪", 22 | "荡妇", 23 | "恋腿癖", 24 | "美少女" 25 | ], 26 | "score": null, 27 | "title": "生意気な妹にニーハイを履かせ僕だけの「絶対領域」を誕生させ僕好みに痴女らせた。 相沢みなみ", 28 | "ori_title": null, 29 | "magnet": null, 30 | "serial": null, 31 | "actress": [ 32 | "相沢みなみ" 33 | ], 34 | "actress_pics": { 35 | "相沢みなみ": "https://www.javbus.com/pics/actress/qfy_a.jpg" 36 | }, 37 | "director": "まえだのかほり", 38 | "duration": "170", 39 | "producer": "アイデアポケット", 40 | "publisher": "ティッシュ", 41 | "uncensored": false, 42 | "publish_date": "2018-07-14", 43 | "preview_pics": [ 44 | "https://pics.dmm.co.jp/digital/video/ipx00177/ipx00177jp-1.jpg", 45 | "https://pics.dmm.co.jp/digital/video/ipx00177/ipx00177jp-2.jpg", 46 | "https://pics.dmm.co.jp/digital/video/ipx00177/ipx00177jp-3.jpg", 47 | "https://pics.dmm.co.jp/digital/video/ipx00177/ipx00177jp-4.jpg", 48 | "https://pics.dmm.co.jp/digital/video/ipx00177/ipx00177jp-5.jpg", 49 | "https://pics.dmm.co.jp/digital/video/ipx00177/ipx00177jp-6.jpg", 50 | "https://pics.dmm.co.jp/digital/video/ipx00177/ipx00177jp-7.jpg", 51 | "https://pics.dmm.co.jp/digital/video/ipx00177/ipx00177jp-8.jpg", 52 | "https://pics.dmm.co.jp/digital/video/ipx00177/ipx00177jp-9.jpg", 53 | "https://pics.dmm.co.jp/digital/video/ipx00177/ipx00177jp-10.jpg", 54 | "https://pics.dmm.co.jp/digital/video/ipx00177/ipx00177jp-11.jpg", 55 | "https://pics.dmm.co.jp/digital/video/ipx00177/ipx00177jp-12.jpg" 56 | ], 57 | "preview_video": null 58 | } -------------------------------------------------------------------------------- /unittest/data/IPX-177 (javdb).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "IPX-177", 3 | "cid": null, 4 | "url": "https://javdb.com/v/0ez7k", 5 | "plot": null, 6 | "cover": "https://c0.jdbstatic.com/covers/0e/0ez7k.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "美少女", 10 | "單體作品", 11 | "蕩婦", 12 | "足交", 13 | "數位馬賽克", 14 | "手淫", 15 | "戀腿癖", 16 | "絲襪、過膝襪", 17 | "無碼破解", 18 | "高跟鞋" 19 | ], 20 | "genre_id": null, 21 | "genre_norm": [ 22 | "美少女", 23 | "單體作品", 24 | "荡妇", 25 | "足交", 26 | "手淫", 27 | "恋腿癖", 28 | "过膝袜", 29 | "無碼破解", 30 | "高跟鞋" 31 | ], 32 | "score": "9.38", 33 | "title": "生意気な妹にニーハイを履かせ僕だけの「絶対領域」を誕生させ僕好みに痴女らせた。 相沢みなみ", 34 | "ori_title": null, 35 | "magnet": [ 36 | "magnet:?xt=urn:btih:3492f27212cccafb45c6b7687dc714281e7a085d&dn=IPX-177-U.torrent.无码破解", 37 | "magnet:?xt=urn:btih:91688f292ec619c39797510d41110be8316ce777&dn=[7sht.me]ipx-177-C.torrent", 38 | "magnet:?xt=urn:btih:4fb8a4e49b0f877d8c56d8a885fdcfbf3821db2b&dn=IPX-177 相沢みなみ【中文字幕】.mp4", 39 | "magnet:?xt=urn:btih:c0c799b22aa99b7ebd1e4c6350ebc1ea81d141de&dn=[IPX-177].mp4", 40 | "magnet:?xt=urn:btih:3f6f17675ab821584f7c29913530d4240b366f71&dn=IPX-177.mp4", 41 | "magnet:?xt=urn:btih:2a65a53601aa7e3c6356a64f5756b4e2ffffc7c5&dn=[Thz.la]ipx-177", 42 | "magnet:?xt=urn:btih:1c9f68f66d106af48171f99c1f91a96141a042e3&dn=IPX-177.mp4", 43 | "magnet:?xt=urn:btih:6da97cbe5e9eb27dd6d594d4e58f5c69e9fae085&dn=ipx-177", 44 | "magnet:?xt=urn:btih:e8422d364cb8ae75026c59ebb259d05f393adbad&dn=HD-ipx-177", 45 | "magnet:?xt=urn:btih:2c338baad9ecb729fab8830ea1405304da3582ab&dn=[SHANA][IPX-177].mp4", 46 | "magnet:?xt=urn:btih:41a7be7df1c934cf4b8daf50e803ba214c04c6e8&dn=IPX-177 生意気な妹にニーハイを履かせ僕だけの「絶対領域」を誕生させ僕好みに痴女らせた。 相沢みなみ.mp4", 47 | "magnet:?xt=urn:btih:4cb65bcdf29dbeecea7a3af3ca42a51611dae7cf&dn=你好我的心@第一会所@IPX-177", 48 | "magnet:?xt=urn:btih:0acce05e77e5413d0751ccf99161bff1c1ff05e9&dn=[中文字幕]IPX-177", 49 | "magnet:?xt=urn:btih:8002b5fbd5b5f93c2ae65c2003f176db9586e3c9&dn=[HD中文字幕] IPX-177 生意気な妹にニーハイを履かせ僕だけの「絶対領域」を誕生させ僕好みに痴女らせた。 相沢みなみ", 50 | "magnet:?xt=urn:btih:cd0e0a31e19195997e1b177e08b707f9f63f7edb&dn=112.[1030.ws]ipx-177", 51 | "magnet:?xt=urn:btih:0a4cce961736e73ae2eb15387fbed651a97a7b8f&dn=IPX-177 Aizawa Minami Younger Sister", 52 | "magnet:?xt=urn:btih:fef6fa8fdb529ca87b63ef3d257288670c9859bc&dn=IPX-177-fuckbe.com.mp4", 53 | "magnet:?xt=urn:btih:7f490c6468ee5f611570ba8083912f5d0495d797&dn=hjd-2048.com-0713-ipx-177-h264" 54 | ], 55 | "serial": "僕だけの「絶対領域」を誕生させ僕好みに痴女らせた。", 56 | "actress": [ 57 | "相沢みなみ" 58 | ], 59 | "actress_pics": null, 60 | "director": "まえだのかほり", 61 | "duration": "170", 62 | "producer": "IDEA POCKET", 63 | "publisher": "ティッシュ", 64 | "uncensored": false, 65 | "publish_date": "2018-07-19", 66 | "preview_pics": [ 67 | "https://c0.jdbstatic.com/samples/0e/0ez7k_l_0.jpg", 68 | "https://c0.jdbstatic.com/samples/0e/0ez7k_l_1.jpg", 69 | "https://c0.jdbstatic.com/samples/0e/0ez7k_l_2.jpg", 70 | "https://c0.jdbstatic.com/samples/0e/0ez7k_l_3.jpg", 71 | "https://c0.jdbstatic.com/samples/0e/0ez7k_l_4.jpg", 72 | "https://c0.jdbstatic.com/samples/0e/0ez7k_l_5.jpg", 73 | "https://c0.jdbstatic.com/samples/0e/0ez7k_l_6.jpg", 74 | "https://c0.jdbstatic.com/samples/0e/0ez7k_l_7.jpg", 75 | "https://c0.jdbstatic.com/samples/0e/0ez7k_l_8.jpg", 76 | "https://c0.jdbstatic.com/samples/0e/0ez7k_l_9.jpg", 77 | "https://c0.jdbstatic.com/samples/0e/0ez7k_l_10.jpg", 78 | "https://c0.jdbstatic.com/samples/0e/0ez7k_l_11.jpg" 79 | ], 80 | "preview_video": "https://cc3001.dmm.co.jp/litevideo/freepv/i/ipx/ipx00177/ipx00177_dm_w.mp4" 81 | } -------------------------------------------------------------------------------- /unittest/data/IPX-177 (javlib).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "IPX-177", 3 | "cid": null, 4 | "url": "https://www.javlibrary.com/cn/?v=javli7dz24", 5 | "plot": null, 6 | "cover": "https://pics.dmm.co.jp/mono/movie/adult/ipx177/ipx177pl.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "打手枪", 10 | "单体作品", 11 | "美少女", 12 | "荡妇", 13 | "恋腿癖", 14 | "数位马赛克" 15 | ], 16 | "genre_id": null, 17 | "genre_norm": null, 18 | "score": "8.70", 19 | "title": "生意気な妹にニーハイを履かせ僕だけの「絶対領域」を誕生させ僕好みに痴女らせた。 相沢みなみ", 20 | "ori_title": null, 21 | "magnet": null, 22 | "serial": null, 23 | "actress": [ 24 | "相沢みなみ" 25 | ], 26 | "actress_pics": null, 27 | "director": "まえだのかほり", 28 | "duration": "170", 29 | "producer": "IDEA POCKET", 30 | "publisher": "ティッシュ", 31 | "uncensored": null, 32 | "publish_date": "2018-07-19", 33 | "preview_pics": null, 34 | "preview_video": null 35 | } -------------------------------------------------------------------------------- /unittest/data/IPX-177 (javmenu).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "IPX-177", 3 | "cid": null, 4 | "url": "https://mrzyx.xyz/IPX-177", 5 | "plot": null, 6 | "cover": "https://pics.vpdmm.cc/digital/video/ipx00177/ipx00177pl.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "美少女", 10 | "蕩婦", 11 | "足交", 12 | "數位馬賽克", 13 | "手淫", 14 | "戀腿癖", 15 | "過膝襪", 16 | "無碼破解", 17 | "高跟鞋", 18 | "單體作品" 19 | ], 20 | "genre_id": [ 21 | "censored/5", 22 | "censored/48", 23 | "censored/61", 24 | "censored/85", 25 | "censored/102", 26 | "censored/107", 27 | "censored/198", 28 | "censored/348", 29 | "censored/352", 30 | "censored/28" 31 | ], 32 | "genre_norm": null, 33 | "score": null, 34 | "title": "生意気な妹にニーハイを履かせ僕だけの「絶対領域」を誕生させ僕好みに痴女らせた。 相沢みなみ", 35 | "ori_title": null, 36 | "magnet": [ 37 | "magnet:?xt=urn:btih:3492f27212cccafb45c6b7687dc714281e7a085d&dn=IPX-177-U.torrent.无码破解", 38 | "magnet:?xt=urn:btih:91688f292ec619c39797510d41110be8316ce777&dn=[7sht.me]ipx-177-C.torrent", 39 | "magnet:?xt=urn:btih:4fb8a4e49b0f877d8c56d8a885fdcfbf3821db2b&dn=IPX-177 相沢みなみ【中文字幕】.mp4", 40 | "magnet:?xt=urn:btih:c0c799b22aa99b7ebd1e4c6350ebc1ea81d141de&dn=[IPX-177].mp4", 41 | "magnet:?xt=urn:btih:3f6f17675ab821584f7c29913530d4240b366f71&dn=IPX-177.mp4", 42 | "magnet:?xt=urn:btih:2a65a53601aa7e3c6356a64f5756b4e2ffffc7c5&dn=[Thz.la]ipx-177", 43 | "magnet:?xt=urn:btih:1c9f68f66d106af48171f99c1f91a96141a042e3&dn=IPX-177.mp4", 44 | "magnet:?xt=urn:btih:6da97cbe5e9eb27dd6d594d4e58f5c69e9fae085&dn=ipx-177", 45 | "magnet:?xt=urn:btih:e8422d364cb8ae75026c59ebb259d05f393adbad&dn=HD-ipx-177", 46 | "magnet:?xt=urn:btih:2c338baad9ecb729fab8830ea1405304da3582ab&dn=[SHANA][IPX-177].mp4", 47 | "magnet:?xt=urn:btih:41a7be7df1c934cf4b8daf50e803ba214c04c6e8&dn=IPX-177 生意気な妹にニーハイを履かせ僕だけの「絶対領域」を誕生させ僕好みに痴女らせた。 相沢みなみ.mp4", 48 | "magnet:?xt=urn:btih:4cb65bcdf29dbeecea7a3af3ca42a51611dae7cf&dn=你好我的心@第一会所@IPX-177", 49 | "magnet:?xt=urn:btih:0acce05e77e5413d0751ccf99161bff1c1ff05e9&dn=[中文字幕]IPX-177", 50 | "magnet:?xt=urn:btih:8002b5fbd5b5f93c2ae65c2003f176db9586e3c9&dn=[HD中文字幕] IPX-177 生意気な妹にニーハイを履かせ僕だけの「絶対領域」を誕生させ僕好みに痴女らせた。 相沢みなみ", 51 | "magnet:?xt=urn:btih:cd0e0a31e19195997e1b177e08b707f9f63f7edb&dn=112.[1030.ws]ipx-177", 52 | "magnet:?xt=urn:btih:0a4cce961736e73ae2eb15387fbed651a97a7b8f&dn=IPX-177 Aizawa Minami Younger Sister" 53 | ], 54 | "serial": null, 55 | "actress": [ 56 | "相澤南", 57 | "久道実", 58 | "渡辺琢磨" 59 | ], 60 | "actress_pics": null, 61 | "director": null, 62 | "duration": "170", 63 | "producer": "IDEA POCKET", 64 | "publisher": null, 65 | "uncensored": null, 66 | "publish_date": "2018-07-19", 67 | "preview_pics": [ 68 | "https://pics.vpdmm.cc/digital/video/ipx00177/ipx00177jp-1.jpg", 69 | "https://pics.vpdmm.cc/digital/video/ipx00177/ipx00177jp-2.jpg", 70 | "https://pics.vpdmm.cc/digital/video/ipx00177/ipx00177jp-3.jpg", 71 | "https://pics.vpdmm.cc/digital/video/ipx00177/ipx00177jp-4.jpg", 72 | "https://pics.vpdmm.cc/digital/video/ipx00177/ipx00177jp-5.jpg", 73 | "https://pics.vpdmm.cc/digital/video/ipx00177/ipx00177jp-6.jpg", 74 | "https://pics.vpdmm.cc/digital/video/ipx00177/ipx00177jp-7.jpg", 75 | "https://pics.vpdmm.cc/digital/video/ipx00177/ipx00177jp-8.jpg", 76 | "https://pics.vpdmm.cc/digital/video/ipx00177/ipx00177jp-9.jpg", 77 | "https://pics.vpdmm.cc/digital/video/ipx00177/ipx00177jp-10.jpg", 78 | "https://pics.vpdmm.cc/digital/video/ipx00177/ipx00177jp-11.jpg", 79 | "https://pics.vpdmm.cc/digital/video/ipx00177/ipx00177jp-12.jpg" 80 | ], 81 | "preview_video": null 82 | } -------------------------------------------------------------------------------- /unittest/data/IPZ-037 (javbus).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "IPZ-037", 3 | "cid": null, 4 | "url": "https://www.javbus.com/IPZ-037", 5 | "plot": null, 6 | "cover": "https://www.javbus.com/pics/cover/1ng2_b.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "高畫質", 10 | "數位馬賽克", 11 | "DMM獨家", 12 | "單體作品", 13 | "多P" 14 | ], 15 | "genre_id": null, 16 | "genre_norm": [ 17 | "单体作品", 18 | "多P" 19 | ], 20 | "score": null, 21 | "title": "断り切れずにヤラせちゃう女 私押しに弱いんです Rio", 22 | "ori_title": null, 23 | "magnet": null, 24 | "serial": "断り切れずにヤラせちゃう女私押しに弱いんです", 25 | "actress": [ 26 | "Rio(柚木ティナ)" 27 | ], 28 | "actress_pics": { 29 | "Rio(柚木ティナ)": "https://www.javbus.com/pics/actress/2mx_a.jpg" 30 | }, 31 | "director": "K.C.武田", 32 | "duration": "175", 33 | "producer": "アイデアポケット", 34 | "publisher": "ティッシュ", 35 | "uncensored": false, 36 | "publish_date": "2013-01-26", 37 | "preview_pics": [ 38 | "https://pics.dmm.co.jp/digital/video/ipz00037/ipz00037jp-1.jpg", 39 | "https://pics.dmm.co.jp/digital/video/ipz00037/ipz00037jp-2.jpg", 40 | "https://pics.dmm.co.jp/digital/video/ipz00037/ipz00037jp-3.jpg", 41 | "https://pics.dmm.co.jp/digital/video/ipz00037/ipz00037jp-4.jpg", 42 | "https://pics.dmm.co.jp/digital/video/ipz00037/ipz00037jp-5.jpg", 43 | "https://pics.dmm.co.jp/digital/video/ipz00037/ipz00037jp-6.jpg", 44 | "https://pics.dmm.co.jp/digital/video/ipz00037/ipz00037jp-7.jpg", 45 | "https://pics.dmm.co.jp/digital/video/ipz00037/ipz00037jp-8.jpg", 46 | "https://pics.dmm.co.jp/digital/video/ipz00037/ipz00037jp-9.jpg", 47 | "https://pics.dmm.co.jp/digital/video/ipz00037/ipz00037jp-10.jpg", 48 | "https://pics.dmm.co.jp/digital/video/ipz00037/ipz00037jp-11.jpg", 49 | "https://pics.dmm.co.jp/digital/video/ipz00037/ipz00037jp-12.jpg" 50 | ], 51 | "preview_video": null 52 | } -------------------------------------------------------------------------------- /unittest/data/KING-048 (javbus).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "KING-048", 3 | "cid": null, 4 | "url": "https://www.javbus.com/KING-048", 5 | "plot": null, 6 | "cover": "https://www.busfan.cfd/pics/cover/8kib_b.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "女優按摩棒", 10 | "潮吹", 11 | "中出", 12 | "高中女生" 13 | ], 14 | "genre_id": null, 15 | "genre_norm": [ 16 | "女优按摩棒", 17 | "潮吹", 18 | "中出", 19 | "高中女生" 20 | ], 21 | "score": null, 22 | "title": "らら", 23 | "ori_title": null, 24 | "magnet": null, 25 | "serial": null, 26 | "actress": [], 27 | "actress_pics": {}, 28 | "director": null, 29 | "duration": "62", 30 | "producer": null, 31 | "publisher": null, 32 | "uncensored": false, 33 | "publish_date": "2021-09-15", 34 | "preview_pics": [ 35 | "https://www.busfan.cfd/pics/sample/8kib_1.jpg", 36 | "https://www.busfan.cfd/pics/sample/8kib_2.jpg", 37 | "https://www.busfan.cfd/pics/sample/8kib_3.jpg", 38 | "https://www.busfan.cfd/pics/sample/8kib_4.jpg", 39 | "https://www.busfan.cfd/pics/sample/8kib_5.jpg" 40 | ], 41 | "preview_video": null 42 | } -------------------------------------------------------------------------------- /unittest/data/KQBD-089 (msin).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "KQBD-089", 3 | "cid": "244kqbd00089", 4 | "url": "https://db.msin.jp/jp.page/movie?id=1206328", 5 | "plot": null, 6 | "cover": "https://pics.dmm.co.jp/digital/video/244kqbd00089/244kqbd00089pl.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "4K", 10 | "ハイビジョン", 11 | "単体作品", 12 | "フェラ", 13 | "電マ", 14 | "女子校生", 15 | "拘束", 16 | "セーラー服" 17 | ], 18 | "genre_id": null, 19 | "genre_norm": null, 20 | "score": null, 21 | "title": "【4Kリマスター版】制服美少女と性交 心花ゆら", 22 | "ori_title": null, 23 | "magnet": null, 24 | "serial": "制服美少女と性交", 25 | "actress": [ 26 | "心花ゆら" 27 | ], 28 | "actress_pics": { 29 | "心花ゆら": "https://img.msin.info/jp.images/actress/1036363.jpg" 30 | }, 31 | "director": null, 32 | "duration": "124", 33 | "producer": "ドリームチケット", 34 | "publisher": null, 35 | "uncensored": false, 36 | "publish_date": "2023-11-30", 37 | "preview_pics": [ 38 | "https://pics.dmm.co.jp/digital/video/244kqbd00089/244kqbd00089jp-1.jpg", 39 | "https://pics.dmm.co.jp/digital/video/244kqbd00089/244kqbd00089jp-2.jpg", 40 | "https://pics.dmm.co.jp/digital/video/244kqbd00089/244kqbd00089jp-3.jpg", 41 | "https://pics.dmm.co.jp/digital/video/244kqbd00089/244kqbd00089jp-4.jpg", 42 | "https://pics.dmm.co.jp/digital/video/244kqbd00089/244kqbd00089jp-5.jpg", 43 | "https://pics.dmm.co.jp/digital/video/244kqbd00089/244kqbd00089jp-6.jpg", 44 | "https://pics.dmm.co.jp/digital/video/244kqbd00089/244kqbd00089jp-7.jpg", 45 | "https://pics.dmm.co.jp/digital/video/244kqbd00089/244kqbd00089jp-8.jpg", 46 | "https://pics.dmm.co.jp/digital/video/244kqbd00089/244kqbd00089jp-9.jpg", 47 | "https://pics.dmm.co.jp/digital/video/244kqbd00089/244kqbd00089jp-10.jpg", 48 | "https://pics.dmm.co.jp/digital/video/244kqbd00089/244kqbd00089jp-11.jpg", 49 | "https://pics.dmm.co.jp/digital/video/244kqbd00089/244kqbd00089jp-12.jpg", 50 | "https://pics.dmm.co.jp/digital/video/244kqbd00089/244kqbd00089jp-13.jpg", 51 | "https://pics.dmm.co.jp/digital/video/244kqbd00089/244kqbd00089jp-14.jpg", 52 | "https://pics.dmm.co.jp/digital/video/244kqbd00089/244kqbd00089jp-15.jpg", 53 | "https://pics.dmm.co.jp/digital/video/244kqbd00089/244kqbd00089jp-16.jpg", 54 | "https://pics.dmm.co.jp/digital/video/244kqbd00089/244kqbd00089jp-17.jpg", 55 | "https://pics.dmm.co.jp/digital/video/244kqbd00089/244kqbd00089jp-18.jpg", 56 | "https://pics.dmm.co.jp/digital/video/244kqbd00089/244kqbd00089jp-19.jpg", 57 | "https://pics.dmm.co.jp/digital/video/244kqbd00089/244kqbd00089jp-20.jpg" 58 | ], 59 | "preview_video": null 60 | } -------------------------------------------------------------------------------- /unittest/data/MTF-020 (javbus).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "MTF-020", 3 | "cid": null, 4 | "url": "https://www.javbus.com/MTF-020", 5 | "plot": null, 6 | "cover": "https://www.javbus.com/pics/cover/3nv0_b.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "美少女", 10 | "亂倫", 11 | "偶像藝人", 12 | "單體作品", 13 | "高中女生" 14 | ], 15 | "genre_id": null, 16 | "genre_norm": [ 17 | "美少女", 18 | "乱伦", 19 | "偶像艺人", 20 | "单体作品", 21 | "高中女生" 22 | ], 23 | "score": null, 24 | "title": "制服シンデレラの秘密", 25 | "ori_title": null, 26 | "magnet": null, 27 | "serial": null, 28 | "actress": [ 29 | "浅倉舞" 30 | ], 31 | "actress_pics": { 32 | "浅倉舞": "https://www.javbus.com/pics/actress/3af_a.jpg" 33 | }, 34 | "director": null, 35 | "duration": "55", 36 | "producer": "h.m.p", 37 | "publisher": "Tiffany", 38 | "uncensored": false, 39 | "publish_date": null, 40 | "preview_pics": [ 41 | "https://pics.dmm.co.jp/digital/video/41mtf00020/41mtf00020jp-1.jpg", 42 | "https://pics.dmm.co.jp/digital/video/41mtf00020/41mtf00020jp-2.jpg", 43 | "https://pics.dmm.co.jp/digital/video/41mtf00020/41mtf00020jp-3.jpg", 44 | "https://pics.dmm.co.jp/digital/video/41mtf00020/41mtf00020jp-4.jpg", 45 | "https://pics.dmm.co.jp/digital/video/41mtf00020/41mtf00020jp-5.jpg", 46 | "https://pics.dmm.co.jp/digital/video/41mtf00020/41mtf00020jp-6.jpg", 47 | "https://pics.dmm.co.jp/digital/video/41mtf00020/41mtf00020jp-7.jpg", 48 | "https://pics.dmm.co.jp/digital/video/41mtf00020/41mtf00020jp-8.jpg", 49 | "https://pics.dmm.co.jp/digital/video/41mtf00020/41mtf00020jp-9.jpg", 50 | "https://pics.dmm.co.jp/digital/video/41mtf00020/41mtf00020jp-10.jpg", 51 | "https://pics.dmm.co.jp/digital/video/41mtf00020/41mtf00020jp-11.jpg", 52 | "https://pics.dmm.co.jp/digital/video/41mtf00020/41mtf00020jp-12.jpg", 53 | "https://pics.dmm.co.jp/digital/video/41mtf00020/41mtf00020jp-13.jpg", 54 | "https://pics.dmm.co.jp/digital/video/41mtf00020/41mtf00020jp-14.jpg", 55 | "https://pics.dmm.co.jp/digital/video/41mtf00020/41mtf00020jp-15.jpg", 56 | "https://pics.dmm.co.jp/digital/video/41mtf00020/41mtf00020jp-16.jpg", 57 | "https://pics.dmm.co.jp/digital/video/41mtf00020/41mtf00020jp-17.jpg", 58 | "https://pics.dmm.co.jp/digital/video/41mtf00020/41mtf00020jp-18.jpg", 59 | "https://pics.dmm.co.jp/digital/video/41mtf00020/41mtf00020jp-19.jpg", 60 | "https://pics.dmm.co.jp/digital/video/41mtf00020/41mtf00020jp-20.jpg" 61 | ], 62 | "preview_video": null 63 | } -------------------------------------------------------------------------------- /unittest/data/NANP-030 (javbus).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "NANP-030", 3 | "cid": null, 4 | "url": "https://www.javbus.com/NANP-030", 5 | "plot": null, 6 | "cover": "https://www.javbus.com/pics/cover/5ev3_b.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "業餘", 10 | "獵豔", 11 | "偷窥", 12 | "乳房", 13 | "高畫質", 14 | "立即口交" 15 | ], 16 | "genre_id": null, 17 | "genre_norm": [ 18 | "业余", 19 | "猎艳", 20 | "偷窥", 21 | "乳房", 22 | "立即口交" 23 | ], 24 | "score": null, 25 | "title": "Taking Home An Amateur After Picking Her Up! Secretly Filming The SEX And Illegally Selling The Vide", 26 | "ori_title": null, 27 | "magnet": null, 28 | "serial": null, 29 | "actress": [ 30 | "涼木みらい" 31 | ], 32 | "actress_pics": { 33 | "涼木みらい": "https://www.javbus.com/pics/actress/nau_a.jpg" 34 | }, 35 | "director": null, 36 | "duration": null, 37 | "producer": null, 38 | "publisher": null, 39 | "uncensored": false, 40 | "publish_date": "2016-03-11", 41 | "preview_pics": [ 42 | "https://pics.dmm.co.jp/digital/video/h_891nanp00030/h_891nanp00030jp-1.jpg", 43 | "https://pics.dmm.co.jp/digital/video/h_891nanp00030/h_891nanp00030jp-2.jpg", 44 | "https://pics.dmm.co.jp/digital/video/h_891nanp00030/h_891nanp00030jp-3.jpg", 45 | "https://pics.dmm.co.jp/digital/video/h_891nanp00030/h_891nanp00030jp-4.jpg", 46 | "https://pics.dmm.co.jp/digital/video/h_891nanp00030/h_891nanp00030jp-5.jpg", 47 | "https://pics.dmm.co.jp/digital/video/h_891nanp00030/h_891nanp00030jp-6.jpg", 48 | "https://pics.dmm.co.jp/digital/video/h_891nanp00030/h_891nanp00030jp-7.jpg", 49 | "https://pics.dmm.co.jp/digital/video/h_891nanp00030/h_891nanp00030jp-8.jpg", 50 | "https://pics.dmm.co.jp/digital/video/h_891nanp00030/h_891nanp00030jp-9.jpg", 51 | "https://pics.dmm.co.jp/digital/video/h_891nanp00030/h_891nanp00030jp-10.jpg", 52 | "https://pics.dmm.co.jp/digital/video/h_891nanp00030/h_891nanp00030jp-11.jpg", 53 | "https://pics.dmm.co.jp/digital/video/h_891nanp00030/h_891nanp00030jp-12.jpg", 54 | "https://pics.dmm.co.jp/digital/video/h_891nanp00030/h_891nanp00030jp-13.jpg", 55 | "https://pics.dmm.co.jp/digital/video/h_891nanp00030/h_891nanp00030jp-14.jpg", 56 | "https://pics.dmm.co.jp/digital/video/h_891nanp00030/h_891nanp00030jp-15.jpg", 57 | "https://pics.dmm.co.jp/digital/video/h_891nanp00030/h_891nanp00030jp-16.jpg", 58 | "https://pics.dmm.co.jp/digital/video/h_891nanp00030/h_891nanp00030jp-17.jpg", 59 | "https://pics.dmm.co.jp/digital/video/h_891nanp00030/h_891nanp00030jp-18.jpg", 60 | "https://pics.dmm.co.jp/digital/video/h_891nanp00030/h_891nanp00030jp-19.jpg", 61 | "https://pics.dmm.co.jp/digital/video/h_891nanp00030/h_891nanp00030jp-20.jpg" 62 | ], 63 | "preview_video": null 64 | } -------------------------------------------------------------------------------- /unittest/data/OPCYN-174 (jav321).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "OPCYN-174", 3 | "cid": "opcyn174", 4 | "url": "https://en.jav321.com/video/opcyn174", 5 | "plot": "どこからどう見ても初心でセックスのセの字も知らなそうな美少女だが…実は淫乱ドスケベ!妹系の可愛らしい皮をかぶった痴女娘が小さなカラダでデカチンを貪りまくる!!カラダに合わせた小さなミニマ●コがデカチンを飲み込んで悲鳴をあげる!男を熱狂させる小柄な美少女が聖獣と化して乱れまくる姿は必見!!", 6 | "cover": "http://pics.dmm.co.jp/digital/amateur/opcyn174/opcyn174jp-001.jpg", 7 | "big_cover": null, 8 | "genre": [], 9 | "genre_id": [], 10 | "genre_norm": null, 11 | "score": null, 12 | "title": "いちか ", 13 | "ori_title": null, 14 | "magnet": null, 15 | "serial": null, 16 | "actress": [], 17 | "actress_pics": {}, 18 | "director": null, 19 | "duration": "71", 20 | "producer": null, 21 | "publisher": null, 22 | "uncensored": null, 23 | "publish_date": "2021-07-07", 24 | "preview_pics": [ 25 | "http://pics.dmm.co.jp/digital/amateur/opcyn174/opcyn174jp-002.jpg", 26 | "http://pics.dmm.co.jp/digital/amateur/opcyn174/opcyn174jp-003.jpg", 27 | "http://pics.dmm.co.jp/digital/amateur/opcyn174/opcyn174jp-004.jpg", 28 | "http://pics.dmm.co.jp/digital/amateur/opcyn174/opcyn174jp-005.jpg" 29 | ], 30 | "preview_video": null 31 | } -------------------------------------------------------------------------------- /unittest/data/RED-096 (airav).json: -------------------------------------------------------------------------------- 1 | { 2 | "注": "此文件用来测试找不到番号时的处理,故各字段故意为空", 3 | "dvdid": "RED-096", 4 | "cid": null, 5 | "url": null, 6 | "plot": null, 7 | "cover": null, 8 | "big_cover": null, 9 | "genre": null, 10 | "genre_id": null, 11 | "genre_norm": null, 12 | "score": null, 13 | "title": null, 14 | "ori_title": null, 15 | "magnet": null, 16 | "serial": null, 17 | "actress": null, 18 | "actress_pics": null, 19 | "director": null, 20 | "duration": null, 21 | "producer": null, 22 | "publisher": null, 23 | "uncensored": null, 24 | "publish_date": null, 25 | "preview_pics": null, 26 | "preview_video": null 27 | } -------------------------------------------------------------------------------- /unittest/data/SCUTE-1177 (jav321).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "SCUTE-1177", 3 | "cid": "scute1177", 4 | "url": "https://en.jav321.com/video/scute1177", 5 | "plot": null, 6 | "cover": "http://pics.dmm.co.jp/digital/amateur/scute1177/scute1177jp.jpg", 7 | "big_cover": null, 8 | "genre": [], 9 | "genre_id": [], 10 | "genre_norm": null, 11 | "score": "10.0", 12 | "title": "いちか ", 13 | "ori_title": null, 14 | "magnet": null, 15 | "serial": null, 16 | "actress": [], 17 | "actress_pics": {}, 18 | "director": null, 19 | "duration": "53", 20 | "producer": null, 21 | "publisher": null, 22 | "uncensored": null, 23 | "publish_date": "2021-12-11", 24 | "preview_pics": [], 25 | "preview_video": null 26 | } -------------------------------------------------------------------------------- /unittest/data/SIRO-3093 (mgstage).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "SIRO-3093", 3 | "cid": null, 4 | "url": "https://www.mgstage.com/product/product_detail/SIRO-3093/", 5 | "plot": "スレンダー美女のみどりちゃん!真っ白いもちもちの肌にCカップの可愛いおっぱい!顔だけじゃなくて声もおっぱいも可愛い♪マジ天使♪こんな子と今からセックスできるなんて考えただけで興奮しちゃいますね!純白のパンツの下からはぷにぷにの可愛いパイパンおまんちょ。感度がいいようですぐにぐっちゅぐちゅ!小さいお口でち○ぽをぱっくんちょ!口いっぱいにほうばりながら一生懸命舐めてる姿がこれまた可愛い♪挿入中も控えめな喘ぎ声で感じてる姿がとても可愛くて興奮し、パイパンおまんちょから挿入部ががっつり見えてさらに興奮度アップ♪声がアニメ声だからか、腰を振ってるといけない事してる気分になっちゃいますな♪ナニをしても可愛いみどりちゃん。おじさん興奮しっぱなしで●しくなりそうでしたww", 6 | "cover": "https://image.mgstage.com/images/shirouto/siro/3093/pb_e_siro-3093.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "独占配信", 10 | "素人", 11 | "スレンダー", 12 | "美尻", 13 | "電マ", 14 | "パイパン" 15 | ], 16 | "genre_id": null, 17 | "genre_norm": null, 18 | "score": "8.80", 19 | "title": "【初撮り】ネットでAV応募→AV体験撮影 350", 20 | "ori_title": null, 21 | "magnet": null, 22 | "serial": "【初撮り】ネットでAV応募→AV体験撮影", 23 | "actress": [ 24 | "みどり 18歳 カラオケ屋アルバイト" 25 | ], 26 | "actress_pics": null, 27 | "director": null, 28 | "duration": "53", 29 | "producer": "シロウトTV", 30 | "publisher": null, 31 | "uncensored": false, 32 | "publish_date": "2017-06-24", 33 | "preview_pics": [ 34 | "https://image.mgstage.com/images/shirouto/siro/3093/cap_e_0_siro-3093.jpg", 35 | "https://image.mgstage.com/images/shirouto/siro/3093/cap_e_1_siro-3093.jpg", 36 | "https://image.mgstage.com/images/shirouto/siro/3093/cap_e_2_siro-3093.jpg", 37 | "https://image.mgstage.com/images/shirouto/siro/3093/cap_e_3_siro-3093.jpg", 38 | "https://image.mgstage.com/images/shirouto/siro/3093/cap_e_4_siro-3093.jpg", 39 | "https://image.mgstage.com/images/shirouto/siro/3093/cap_e_5_siro-3093.jpg", 40 | "https://image.mgstage.com/images/shirouto/siro/3093/cap_e_6_siro-3093.jpg" 41 | ], 42 | "preview_video": "https://sample.mgstage.com/sample/shirouto/siro/3093/SIRO-3093_sample.mp4" 43 | } -------------------------------------------------------------------------------- /unittest/data/SIRO-4718 (mgstage).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "SIRO-4718", 3 | "cid": null, 4 | "url": "https://www.mgstage.com/product/product_detail/SIRO-4718/", 5 | "plot": "プレイ内容:インタビュー、ベロキス、背後から乳房揉み~耳舐め、乳首弄り舐め、クリ擦り、指入れ、手マン大量潮吹き、男根弄り、濃厚フェラ、裏筋舐め、男の乳首舐め~手コキ、正常位挿入、騎乗位、立ちバック、寝バック、正常位、顔射、お掃除フェラ\nあらすじ:左手●指に指輪を光らせながらインタビューに答える現役美容師「ゆりのさん、33歳。」人妻でありながら性豪であることを自称し、旦那容認でセフレもいると語る彼女。敏感ボディを責め立てるとレンズにかかるほどの潮を撒き散らかし、待ちわびていたとばかりに男根に濃厚奉仕を繰り出して..", 6 | "cover": "https://image.mgstage.com/images/shirouto/siro/4718/pb_e_siro-4718.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "独占配信", 10 | "配信専用", 11 | "素人", 12 | "初撮り", 13 | "フルハイビジョン(FHD)", 14 | "ハメ撮り", 15 | "人妻", 16 | "美尻", 17 | "三十路" 18 | ], 19 | "genre_id": null, 20 | "genre_norm": null, 21 | "score": "7.60", 22 | "title": "【初撮り】【奇跡の33歳】【美しい桃尻】類い稀なる美貌を携えたSEX大好き人妻美容師が登場。大股開きの騎乗位で結合部を露わにしながら快楽に美顔を蕩けさせて.. ネットでAV応募→AV体験撮影 1695", 23 | "ori_title": null, 24 | "magnet": null, 25 | "serial": "【初撮り】ネットでAV応募→AV体験撮影", 26 | "actress": [ 27 | "ゆりの 33歳 美容師" 28 | ], 29 | "actress_pics": null, 30 | "director": null, 31 | "duration": "65", 32 | "producer": "シロウトTV", 33 | "publisher": null, 34 | "uncensored": false, 35 | "publish_date": "2021-12-01", 36 | "preview_pics": [ 37 | "https://image.mgstage.com/images/shirouto/siro/4718/cap_e_0_siro-4718.jpg", 38 | "https://image.mgstage.com/images/shirouto/siro/4718/cap_e_1_siro-4718.jpg", 39 | "https://image.mgstage.com/images/shirouto/siro/4718/cap_e_2_siro-4718.jpg", 40 | "https://image.mgstage.com/images/shirouto/siro/4718/cap_e_3_siro-4718.jpg", 41 | "https://image.mgstage.com/images/shirouto/siro/4718/cap_e_4_siro-4718.jpg", 42 | "https://image.mgstage.com/images/shirouto/siro/4718/cap_e_5_siro-4718.jpg", 43 | "https://image.mgstage.com/images/shirouto/siro/4718/cap_e_6_siro-4718.jpg", 44 | "https://image.mgstage.com/images/shirouto/siro/4718/cap_e_7_siro-4718.jpg", 45 | "https://image.mgstage.com/images/shirouto/siro/4718/cap_e_8_siro-4718.jpg", 46 | "https://image.mgstage.com/images/shirouto/siro/4718/cap_e_9_siro-4718.jpg", 47 | "https://image.mgstage.com/images/shirouto/siro/4718/cap_e_10_siro-4718.jpg", 48 | "https://image.mgstage.com/images/shirouto/siro/4718/cap_e_11_siro-4718.jpg" 49 | ], 50 | "preview_video": "https://sample.mgstage.com/sample/shirouto/siro/4718/siro-4718_20211118T152801.mp4" 51 | } -------------------------------------------------------------------------------- /unittest/data/SKY-247 (jav321).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "SKY-247", 3 | "cid": "SKY-247", 4 | "url": "https://en.jav321.com/video/SKY-247", 5 | "plot": "もかと見せつける開帳オナニー!快感に耐え切れず激しい潮吹き連発!", 6 | "cover": null, 7 | "big_cover": null, 8 | "genre": [], 9 | "genre_id": [], 10 | "genre_norm": null, 11 | "score": null, 12 | "title": "ゴールドエンジェル 22 中野ありさ ", 13 | "ori_title": null, 14 | "magnet": null, 15 | "serial": null, 16 | "actress": [], 17 | "actress_pics": {}, 18 | "director": null, 19 | "duration": "109", 20 | "producer": "Tokyo Hot", 21 | "publisher": null, 22 | "uncensored": null, 23 | "publish_date": "2017-04-23", 24 | "preview_pics": null, 25 | "preview_video": "http://my.cdn.tokyo-hot.com/media/samples/SKY-247.mp4" 26 | } -------------------------------------------------------------------------------- /unittest/data/SKYHD-012 (airav).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "SKYHD-012", 3 | "cid": null, 4 | "url": "https://www.airav.wiki/video/SKYHD-012", 5 | "plot": null, 6 | "cover": "https://wiki-img.airav.wiki/storage/big_pic/88-07-0064.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "中出", 10 | "巨乳", 11 | "無碼片", 12 | "美少女", 13 | "藍光" 14 | ], 15 | "genre_id": null, 16 | "genre_norm": null, 17 | "score": null, 18 | "title": "SKY ANGEL BLUE Vol.12 (藍光版)", 19 | "ori_title": null, 20 | "magnet": null, 21 | "serial": null, 22 | "actress": [ 23 | "櫻井莉亞" 24 | ], 25 | "actress_pics": null, 26 | "director": null, 27 | "duration": null, 28 | "producer": "SKY HIGH", 29 | "publisher": null, 30 | "uncensored": null, 31 | "publish_date": "2009-03-16", 32 | "preview_pics": [], 33 | "preview_video": null 34 | } -------------------------------------------------------------------------------- /unittest/data/SQTE-148 (airav).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "SQTE-148", 3 | "cid": null, 4 | "url": "https://www.airav.wiki/video/SQTE-148", 5 | "plot": "全都是可愛正妹的成人網站S-Cute來啦、這次從人氣排行榜中挑出前30名來獻給你!快來享受美少女們害羞姿態、看到興奮爽翻天吧!", 6 | "cover": "https://wiki-img.airav.wiki/storage/big_pic/177867.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "超過4小時", 10 | "素人", 11 | "合輯、精選", 12 | "美少女", 13 | "迷你系" 14 | ], 15 | "genre_id": null, 16 | "genre_norm": null, 17 | "score": null, 18 | "title": "S-Cute 2016年銷售排行榜前30名 - 上", 19 | "ori_title": null, 20 | "magnet": null, 21 | "serial": null, 22 | "actress": [ 23 | "星野美優", 24 | "佳苗瑠華", 25 | "彩城友理奈", 26 | "西川結衣", 27 | "司美琴", 28 | "涼川絢音", 29 | "宮崎彩", 30 | "鈴原愛蜜莉", 31 | "美咲佳奈", 32 | "美波愛星", 33 | "稀夕蘭蘭", 34 | "花音詩織", 35 | "廣瀨海", 36 | "大野美鈴", 37 | "大島美緒", 38 | "椎名空", 39 | "向井藍", 40 | "上杉玲奈", 41 | "葵玲奈", 42 | "唯川千尋", 43 | "姬川優奈", 44 | "佐倉亞衣" 45 | ], 46 | "actress_pics": null, 47 | "director": null, 48 | "duration": null, 49 | "producer": "S-Cute", 50 | "publisher": null, 51 | "uncensored": null, 52 | "publish_date": "2016-12-01", 53 | "preview_pics": [ 54 | "https://wiki-img.airav.wiki/storage/sections/177867_001.jpg", 55 | "https://wiki-img.airav.wiki/storage/sections/177867_002.jpg", 56 | "https://wiki-img.airav.wiki/storage/sections/177867_003.jpg", 57 | "https://wiki-img.airav.wiki/storage/sections/177867_004.jpg", 58 | "https://wiki-img.airav.wiki/storage/sections/177867_005.jpg", 59 | "https://wiki-img.airav.wiki/storage/sections/177867_006.jpg", 60 | "https://wiki-img.airav.wiki/storage/sections/177867_007.jpg", 61 | "https://wiki-img.airav.wiki/storage/sections/177867_008.jpg", 62 | "https://wiki-img.airav.wiki/storage/sections/177867_009.jpg", 63 | "https://wiki-img.airav.wiki/storage/sections/177867_010.jpg", 64 | "https://wiki-img.airav.wiki/storage/sections/177867_011.jpg", 65 | "https://wiki-img.airav.wiki/storage/sections/177867_012.jpg", 66 | "https://wiki-img.airav.wiki/storage/sections/177867_013.jpg", 67 | "https://wiki-img.airav.wiki/storage/sections/177867_014.jpg", 68 | "https://wiki-img.airav.wiki/storage/sections/177867_015.jpg", 69 | "https://wiki-img.airav.wiki/storage/sections/177867_016.jpg", 70 | "https://wiki-img.airav.wiki/storage/sections/177867_017.jpg", 71 | "https://wiki-img.airav.wiki/storage/sections/177867_018.jpg", 72 | "https://wiki-img.airav.wiki/storage/sections/177867_019.jpg", 73 | "https://wiki-img.airav.wiki/storage/sections/177867_020.jpg" 74 | ], 75 | "preview_video": "http://freems.airav.cc/mv_download/A1EC6B065F22F9437E7A9DA14B10E0E9/61497429/mv/A595F9149551ED4830A11BF3E6F74BB0/61497429/0/36000/208383/normal.mp4" 76 | } -------------------------------------------------------------------------------- /unittest/data/SQTE-148 (javbus).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "SQTE-148", 3 | "cid": null, 4 | "url": "https://www.javbus.com/SQTE-148", 5 | "plot": null, 6 | "cover": "https://www.javbus.com/pics/cover/5r4o_b.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "合集", 10 | "DMM獨家", 11 | "蘿莉塔", 12 | "業餘", 13 | "美少女", 14 | "4小時以上作品" 15 | ], 16 | "genre_id": null, 17 | "genre_norm": [ 18 | "合集", 19 | "萝莉", 20 | "业余", 21 | "美少女", 22 | "4小时以上作品" 23 | ], 24 | "score": null, 25 | "title": "S-Cute年間売上ランキング2016 Top30", 26 | "ori_title": null, 27 | "magnet": null, 28 | "serial": "S-Cute年間売上ランキング", 29 | "actress": [ 30 | "佳苗るか", 31 | "彩城ゆりな", 32 | "宮崎あや", 33 | "みなみ愛星", 34 | "美咲かんな", 35 | "広瀬うみ", 36 | "大島美緒", 37 | "上杉玲奈", 38 | "唯川千尋", 39 | "姫川ゆうな", 40 | "あおいれな", 41 | "涼川絢音", 42 | "花音しおり", 43 | "向井藍", 44 | "羽月希", 45 | "紗藤まゆ", 46 | "早川伊織", 47 | "西川ゆい", 48 | "椎名そら", 49 | "稀夕らら", 50 | "司ミコト", 51 | "大野美鈴", 52 | "さくら亜衣", 53 | "鈴原エミリ", 54 | "あゆな虹恋" 55 | ], 56 | "actress_pics": { 57 | "佳苗るか": "https://www.javbus.com/pics/actress/7s4_a.jpg", 58 | "彩城ゆりな": "https://www.javbus.com/pics/actress/85q_a.jpg", 59 | "宮崎あや": "https://www.javbus.com/pics/actress/m8c_a.jpg", 60 | "みなみ愛星": "https://www.javbus.com/pics/actress/nig_a.jpg", 61 | "美咲かんな": "https://www.javbus.com/pics/actress/nsy_a.jpg", 62 | "広瀬うみ": "https://www.javbus.com/pics/actress/onn_a.jpg", 63 | "大島美緒": "https://www.javbus.com/pics/actress/oyy_a.jpg", 64 | "唯川千尋": "https://www.javbus.com/pics/actress/pgb_a.jpg", 65 | "姫川ゆうな": "https://www.javbus.com/pics/actress/q02_a.jpg", 66 | "あおいれな": "https://www.javbus.com/pics/actress/pey_a.jpg", 67 | "涼川絢音": "https://www.javbus.com/pics/actress/mp3_a.jpg", 68 | "花音しおり": "https://www.javbus.com/pics/actress/od4_a.jpg", 69 | "向井藍": "https://www.javbus.com/pics/actress/pdb_a.jpg", 70 | "羽月希": "https://www.javbus.com/pics/actress/2cj_a.jpg", 71 | "紗藤まゆ": "https://www.javbus.com/pics/actress/m77_a.jpg", 72 | "早川伊織": "https://www.javbus.com/pics/actress/pdk_a.jpg", 73 | "西川ゆい": "https://www.javbus.com/pics/actress/9oc_a.jpg", 74 | "椎名そら": "https://www.javbus.com/pics/actress/p84_a.jpg", 75 | "稀夕らら": "https://www.javbus.com/pics/actress/nx9_a.jpg", 76 | "司ミコト": "https://www.javbus.com/pics/actress/956_a.jpg", 77 | "大野美鈴": "https://www.javbus.com/pics/actress/ozk_a.jpg", 78 | "鈴原エミリ": "https://www.javbus.com/pics/actress/n81_a.jpg", 79 | "あゆな虹恋": "https://www.javbus.com/pics/actress/p8x_a.jpg" 80 | }, 81 | "director": null, 82 | "duration": "240", 83 | "producer": "S-Cute", 84 | "publisher": "S-Cute(S-Cute)", 85 | "uncensored": false, 86 | "publish_date": "2016-11-28", 87 | "preview_pics": [ 88 | "https://pics.dmm.co.jp/digital/video/sqte00148/sqte00148jp-1.jpg", 89 | "https://pics.dmm.co.jp/digital/video/sqte00148/sqte00148jp-2.jpg", 90 | "https://pics.dmm.co.jp/digital/video/sqte00148/sqte00148jp-3.jpg", 91 | "https://pics.dmm.co.jp/digital/video/sqte00148/sqte00148jp-4.jpg", 92 | "https://pics.dmm.co.jp/digital/video/sqte00148/sqte00148jp-5.jpg", 93 | "https://pics.dmm.co.jp/digital/video/sqte00148/sqte00148jp-6.jpg", 94 | "https://pics.dmm.co.jp/digital/video/sqte00148/sqte00148jp-7.jpg", 95 | "https://pics.dmm.co.jp/digital/video/sqte00148/sqte00148jp-8.jpg", 96 | "https://pics.dmm.co.jp/digital/video/sqte00148/sqte00148jp-9.jpg", 97 | "https://pics.dmm.co.jp/digital/video/sqte00148/sqte00148jp-10.jpg", 98 | "https://pics.dmm.co.jp/digital/video/sqte00148/sqte00148jp-11.jpg", 99 | "https://pics.dmm.co.jp/digital/video/sqte00148/sqte00148jp-12.jpg", 100 | "https://pics.dmm.co.jp/digital/video/sqte00148/sqte00148jp-13.jpg", 101 | "https://pics.dmm.co.jp/digital/video/sqte00148/sqte00148jp-14.jpg", 102 | "https://pics.dmm.co.jp/digital/video/sqte00148/sqte00148jp-15.jpg", 103 | "https://pics.dmm.co.jp/digital/video/sqte00148/sqte00148jp-16.jpg", 104 | "https://pics.dmm.co.jp/digital/video/sqte00148/sqte00148jp-17.jpg", 105 | "https://pics.dmm.co.jp/digital/video/sqte00148/sqte00148jp-18.jpg", 106 | "https://pics.dmm.co.jp/digital/video/sqte00148/sqte00148jp-19.jpg", 107 | "https://pics.dmm.co.jp/digital/video/sqte00148/sqte00148jp-20.jpg" 108 | ], 109 | "preview_video": null 110 | } -------------------------------------------------------------------------------- /unittest/data/SSNI-589 (javlib).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "SSNI-589", 3 | "cid": null, 4 | "url": "https://www.javlibrary.com/cn/?v=javli6igzq", 5 | "plot": null, 6 | "cover": "https://pics.dmm.co.jp/mono/movie/adult/ssni589/ssni589pl.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "单体作品", 10 | "巨乳", 11 | "女上位", 12 | "荡妇", 13 | "艺人", 14 | "薄马赛克" 15 | ], 16 | "genre_id": null, 17 | "genre_norm": null, 18 | "score": "8.10", 19 | "title": "三上悠亜の全力イクイク騎乗位マニアックス", 20 | "ori_title": null, 21 | "magnet": null, 22 | "serial": null, 23 | "actress": [ 24 | "三上悠亜" 25 | ], 26 | "actress_pics": null, 27 | "director": "紋℃", 28 | "duration": "150", 29 | "producer": "S1 NO.1 STYLE", 30 | "publisher": "S1 NO.1 STYLE", 31 | "uncensored": null, 32 | "publish_date": "2019-10-19", 33 | "preview_pics": null, 34 | "preview_video": null 35 | } -------------------------------------------------------------------------------- /unittest/data/STAR-299 (airav).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "STAR-299", 3 | "cid": null, 4 | "url": "https://www.airav.wiki/video/STAR-299", 5 | "plot": "話題網路偶像「片桐衿里香」在人氣系列作中首次登場!毫無任何劇本的演出,與男優兩人獨處的空間之間,發掘出衿里香究極的淫蕩本性!", 6 | "cover": "https://wiki-img.airav.wiki/storage/big_pic/65649.jpg", 7 | "big_cover": null, 8 | "genre": [], 9 | "genre_id": null, 10 | "genre_norm": null, 11 | "score": null, 12 | "title": null, 13 | "ori_title": null, 14 | "magnet": null, 15 | "serial": null, 16 | "actress": [ 17 | "片桐衿里香" 18 | ], 19 | "actress_pics": null, 20 | "director": null, 21 | "duration": null, 22 | "producer": "SOD", 23 | "publisher": null, 24 | "uncensored": null, 25 | "publish_date": "2011-08-06", 26 | "preview_pics": [ 27 | "https://wiki-img.airav.wiki/storage/sections/65649_001.jpg", 28 | "https://wiki-img.airav.wiki/storage/sections/65649_002.jpg", 29 | "https://wiki-img.airav.wiki/storage/sections/65649_003.jpg", 30 | "https://wiki-img.airav.wiki/storage/sections/65649_004.jpg", 31 | "https://wiki-img.airav.wiki/storage/sections/65649_005.jpg", 32 | "https://wiki-img.airav.wiki/storage/sections/65649_006.jpg", 33 | "https://wiki-img.airav.wiki/storage/sections/65649_007.jpg", 34 | "https://wiki-img.airav.wiki/storage/sections/65649_008.jpg", 35 | "https://wiki-img.airav.wiki/storage/sections/65649_009.jpg", 36 | "https://wiki-img.airav.wiki/storage/sections/65649_010.jpg", 37 | "https://wiki-img.airav.wiki/storage/sections/65649_011.jpg", 38 | "https://wiki-img.airav.wiki/storage/sections/65649_012.jpg", 39 | "https://wiki-img.airav.wiki/storage/sections/65649_013.jpg", 40 | "https://wiki-img.airav.wiki/storage/sections/65649_014.jpg", 41 | "https://wiki-img.airav.wiki/storage/sections/65649_015.jpg", 42 | "https://wiki-img.airav.wiki/storage/sections/65649_016.jpg", 43 | "https://wiki-img.airav.wiki/storage/sections/65649_017.jpg", 44 | "https://wiki-img.airav.wiki/storage/sections/65649_018.jpg", 45 | "https://wiki-img.airav.wiki/storage/sections/65649_019.jpg" 46 | ], 47 | "preview_video": "http://freemsall.airav.cc/mp4vod_path/tBHq7-7elUJT8zBNeyxYXA/1652681726/0/3600/124352/normal.mp4" 48 | } -------------------------------------------------------------------------------- /unittest/data/STAR-676 (javbus).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "STAR-676", 3 | "cid": null, 4 | "url": "https://www.javbus.com/STAR-676", 5 | "plot": null, 6 | "cover": "https://www.javsee.men/pics/cover/5hhh_b.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "中出", 10 | "單體作品", 11 | "強姦", 12 | "監禁", 13 | "已婚婦女", 14 | "性奴", 15 | "高畫質" 16 | ], 17 | "genre_id": null, 18 | "genre_norm": [ 19 | "中出", 20 | "单体作品", 21 | "强奸", 22 | "监禁", 23 | "已婚妇女", 24 | "性奴" 25 | ], 26 | "score": null, 27 | "title": "Iori Kogawa Married Woman Abducted & Raped - Breaking IN A Rich Young Wife While Her Husband's Away", 28 | "ori_title": null, 29 | "magnet": null, 30 | "serial": null, 31 | "actress": [ 32 | "古川いおり" 33 | ], 34 | "actress_pics": { 35 | "古川いおり": "https://www.javsee.men/pics/actress/9mi_a.jpg" 36 | }, 37 | "director": null, 38 | "duration": null, 39 | "producer": "SODクリエイト", 40 | "publisher": null, 41 | "uncensored": false, 42 | "publish_date": "2016-05-12", 43 | "preview_pics": [ 44 | "https://pics.dmm.co.jp/digital/video/1star00676/1star00676jp-1.jpg", 45 | "https://pics.dmm.co.jp/digital/video/1star00676/1star00676jp-2.jpg", 46 | "https://pics.dmm.co.jp/digital/video/1star00676/1star00676jp-3.jpg", 47 | "https://pics.dmm.co.jp/digital/video/1star00676/1star00676jp-4.jpg", 48 | "https://pics.dmm.co.jp/digital/video/1star00676/1star00676jp-5.jpg", 49 | "https://pics.dmm.co.jp/digital/video/1star00676/1star00676jp-6.jpg", 50 | "https://pics.dmm.co.jp/digital/video/1star00676/1star00676jp-7.jpg", 51 | "https://pics.dmm.co.jp/digital/video/1star00676/1star00676jp-8.jpg", 52 | "https://pics.dmm.co.jp/digital/video/1star00676/1star00676jp-9.jpg", 53 | "https://pics.dmm.co.jp/digital/video/1star00676/1star00676jp-10.jpg", 54 | "https://pics.dmm.co.jp/digital/video/1star00676/1star00676jp-11.jpg", 55 | "https://pics.dmm.co.jp/digital/video/1star00676/1star00676jp-12.jpg", 56 | "https://pics.dmm.co.jp/digital/video/1star00676/1star00676jp-13.jpg", 57 | "https://pics.dmm.co.jp/digital/video/1star00676/1star00676jp-14.jpg", 58 | "https://pics.dmm.co.jp/digital/video/1star00676/1star00676jp-15.jpg", 59 | "https://pics.dmm.co.jp/digital/video/1star00676/1star00676jp-16.jpg", 60 | "https://pics.dmm.co.jp/digital/video/1star00676/1star00676jp-17.jpg", 61 | "https://pics.dmm.co.jp/digital/video/1star00676/1star00676jp-18.jpg", 62 | "https://pics.dmm.co.jp/digital/video/1star00676/1star00676jp-19.jpg", 63 | "https://pics.dmm.co.jp/digital/video/1star00676/1star00676jp-20.jpg" 64 | ], 65 | "preview_video": null 66 | } -------------------------------------------------------------------------------- /unittest/data/STARS-213 (javlib).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "STARS-213", 3 | "cid": null, 4 | "url": "https://www.javlibrary.com/cn/?v=javli63cze", 5 | "plot": null, 6 | "cover": "https://pics.dmm.co.jp/mono/movie/adult/1stars213/1stars213pl.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "单体作品", 10 | "首次亮相", 11 | "美少女", 12 | "潮吹" 13 | ], 14 | "genre_id": null, 15 | "genre_norm": null, 16 | "score": "7.50", 17 | "title": "朝比奈ななせ AV DEBUT", 18 | "ori_title": null, 19 | "magnet": null, 20 | "serial": null, 21 | "actress": [ 22 | "朝比奈ななせ" 23 | ], 24 | "actress_pics": null, 25 | "director": "キョウセイ", 26 | "duration": "185", 27 | "producer": "SOD Create", 28 | "publisher": null, 29 | "uncensored": null, 30 | "publish_date": "2020-03-12", 31 | "preview_pics": null, 32 | "preview_video": null 33 | } -------------------------------------------------------------------------------- /unittest/data/STARS-256 (javdb).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "STARS-256", 3 | "cid": null, 4 | "url": "https://javdb.com/v/eDZBr", 5 | "plot": null, 6 | "cover": "https://c0.jdbstatic.com/covers/ed/eDZBr.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "淫亂真實", 10 | "單體作品", 11 | "平胸", 12 | "美少女電影", 13 | "無碼破解" 14 | ], 15 | "genre_id": null, 16 | "genre_norm": [ 17 | "淫乱?真实", 18 | "單體作品", 19 | "平胸", 20 | "美少女电影", 21 | "無碼破解" 22 | ], 23 | "score": "9.14", 24 | "title": "如同剛出生小鹿般雙腳顫抖 1日中狂抽猛插性交 永野一夏", 25 | "ori_title": "生まれたての子鹿の如く崩れ落とす 1日中超ピストン性交 永野いち夏", 26 | "magnet": [ 27 | "magnet:?xt=urn:btih:4641aa8de0a29d80b98111116806d82fac687562&dn=STARS-256-U.torrent.无码破解", 28 | "magnet:?xt=urn:btih:8de6747ebd3c772c2e582c2bb97440f74a1e37c0&dn=stars-256.torrent", 29 | "magnet:?xt=urn:btih:b826f69e327b65f8e036b9b2e34566a77ead9178&dn=STARS-256.mp4", 30 | "magnet:?xt=urn:btih:f33f2f712049dffc9564d7ebc85b426d874c3c5c&dn=STARS-256", 31 | "magnet:?xt=urn:btih:6faeb4e4119f11dcee3191b2dfa5d7df8304b982&dn=stars-256", 32 | "magnet:?xt=urn:btih:9463b6eb5d08c18c915acc971efd8c59e00411de&dn=STARS-256.HD", 33 | "magnet:?xt=urn:btih:3712dc3d34f6967af9862e007b610b147268fb82&dn=HD_stars-256", 34 | "magnet:?xt=urn:btih:2620e75b5b178838596332d099292afdbab72df9&dn=STARS256", 35 | "magnet:?xt=urn:btih:654aa99fe5f7207f91ed65e2367c6d0008b4bfcc&dn=STARS-256.mp4", 36 | "magnet:?xt=urn:btih:ba820a03dddebdf0ffbc7e55d22f6247756b44f4&dn=STARS-256 1 .mp4", 37 | "magnet:?xt=urn:btih:a1d73b741204ad7b84db7ee60a7a2219494c6f05&dn=STARS-256.mp4" 38 | ], 39 | "serial": "生まれたての子鹿の如く崩れ落とす1日中超ピストン性交", 40 | "actress": [ 41 | "永野いち夏" 42 | ], 43 | "actress_pics": null, 44 | "director": "トレンディ山口", 45 | "duration": "115", 46 | "producer": "SOD Create", 47 | "publisher": null, 48 | "uncensored": false, 49 | "publish_date": "2020-07-09", 50 | "preview_pics": [ 51 | "https://c0.jdbstatic.com/samples/ed/eDZBr_l_0.jpg", 52 | "https://c0.jdbstatic.com/samples/ed/eDZBr_l_1.jpg", 53 | "https://c0.jdbstatic.com/samples/ed/eDZBr_l_2.jpg", 54 | "https://c0.jdbstatic.com/samples/ed/eDZBr_l_3.jpg", 55 | "https://c0.jdbstatic.com/samples/ed/eDZBr_l_4.jpg", 56 | "https://c0.jdbstatic.com/samples/ed/eDZBr_l_5.jpg", 57 | "https://c0.jdbstatic.com/samples/ed/eDZBr_l_6.jpg", 58 | "https://c0.jdbstatic.com/samples/ed/eDZBr_l_7.jpg", 59 | "https://c0.jdbstatic.com/samples/ed/eDZBr_l_8.jpg", 60 | "https://c0.jdbstatic.com/samples/ed/eDZBr_l_9.jpg", 61 | "https://c0.jdbstatic.com/samples/ed/eDZBr_l_10.jpg", 62 | "https://c0.jdbstatic.com/samples/ed/eDZBr_l_11.jpg", 63 | "https://c0.jdbstatic.com/samples/ed/eDZBr_l_12.jpg", 64 | "https://c0.jdbstatic.com/samples/ed/eDZBr_l_13.jpg", 65 | "https://c0.jdbstatic.com/samples/ed/eDZBr_l_14.jpg", 66 | "https://c0.jdbstatic.com/samples/ed/eDZBr_l_15.jpg", 67 | "https://c0.jdbstatic.com/samples/ed/eDZBr_l_16.jpg", 68 | "https://c0.jdbstatic.com/samples/ed/eDZBr_l_17.jpg", 69 | "https://c0.jdbstatic.com/samples/ed/eDZBr_l_18.jpg" 70 | ], 71 | "preview_video": "https://cc3001.dmm.co.jp/litevideo/freepv/1/1st/1stars00256/1stars00256_dm_w.mp4" 72 | } -------------------------------------------------------------------------------- /unittest/data/d_aisoft3356 (fanza).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": null, 3 | "cid": "d_aisoft3356", 4 | "url": "https://www.dmm.co.jp/mono/doujin/-/detail/=/cid=d_aisoft3356/", 5 | "plot": "夏の田舎、蝉の音に混じり聞こえてくる元気な掛け声。\n暇を持て余し木陰で昼寝をしていたあなたは、姪のわかばとふたばにかくれんぼに誘われました。\nそして汗だくになって遊ぶ中、わかばと一緒に今は使われていない古民家へやって来ます。\n\nそこは隠れるには絶好の秘密の場所で……\n\nこれは背伸びしたい年頃のおませな双子姉妹と、\n少し歳の離れたおじさんとのちょっぴりエッチな夏の一齣です。\n\n・基本動画50本+差分+テキスト\n・動画解像度 960×600\n\n対応OS:WindowsVista / Windows7\n製品仕様:DVD-ROM1枚 DVD型トールケース", 6 | "cover": "https://pics.dmm.co.jp/mono/doujin/d_aisoft3356/d_aisoft3356pl.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "3DCG", 10 | "動画・アニメーション", 11 | "音声付き", 12 | "オリジナル", 13 | "貧乳・微乳", 14 | "ミニ系", 15 | "サンプル動画" 16 | ], 17 | "genre_id": [ 18 | "12", 19 | "13", 20 | "15", 21 | "20", 22 | "2005", 23 | "2008", 24 | "6102" 25 | ], 26 | "genre_norm": null, 27 | "score": "10.00", 28 | "title": "夏のひめごと。", 29 | "ori_title": null, 30 | "magnet": null, 31 | "serial": null, 32 | "actress": null, 33 | "actress_pics": null, 34 | "director": null, 35 | "duration": null, 36 | "producer": null, 37 | "publisher": null, 38 | "uncensored": false, 39 | "publish_date": "2014-11-07", 40 | "preview_pics": [ 41 | "https://pics.dmm.co.jp/mono/doujin/d_aisoft3356/d_aisoft3356js-001.jpg", 42 | "https://pics.dmm.co.jp/mono/doujin/d_aisoft3356/d_aisoft3356js-002.jpg", 43 | "https://pics.dmm.co.jp/mono/doujin/d_aisoft3356/d_aisoft3356js-003.jpg", 44 | "https://pics.dmm.co.jp/mono/doujin/d_aisoft3356/d_aisoft3356js-004.jpg" 45 | ], 46 | "preview_video": null 47 | } -------------------------------------------------------------------------------- /unittest/data/gyutto-266923 (gyutto).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "GYUTTO-266923", 3 | "cid": null, 4 | "url": "http://gyutto.com/i/item266923?select_uaflag=1", 5 | "plot": "シコリティ高すぎイキ狂い巨乳肉便器ちゃん。本能から妊娠望んでる女はこうなるらC。無責任中出し合法系ビッチなので着床狙いで子宮ドプドプにしておきました。おかずローテ入り不可避極上オナネタ", 6 | "cover": "http://gyutto.com/data/item_img/2669/266923/266923.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "巨乳", 10 | "フェラ", 11 | "中出し", 12 | "おっぱい", 13 | "孕ませ", 14 | "ニーソックス・ニーソ", 15 | "美少女", 16 | "コスプレ動画+写真" 17 | ], 18 | "genre_id": null, 19 | "genre_norm": null, 20 | "score": null, 21 | "title": "【一発かんたんDL版】受精中毒レム!子宮堕ちビッチ発狂痙攣イキでおっぱい揺れ揺れ!妊娠したがり美巨乳ちゃんの孕み様を見よ!完全肉便器極エロボディ子種仕込み中出し絶頂孕まSEX!! ", 22 | "ori_title": null, 23 | "magnet": null, 24 | "serial": null, 25 | "actress": null, 26 | "actress_pics": null, 27 | "director": null, 28 | "duration": null, 29 | "producer": "こすっち", 30 | "publisher": null, 31 | "uncensored": null, 32 | "publish_date": "2023-12-08", 33 | "preview_pics": [ 34 | "http://gyutto.com/data/item_img/2669/266923/266923.jpg", 35 | "http://gyutto.com/data/item_img/2669/266923/266923_430.jpg", 36 | "http://gyutto.com/data/item_img/2669/266923/266923_431.jpg", 37 | "http://gyutto.com/data/item_img/2669/266923/266923_432.jpg" 38 | ], 39 | "preview_video": null 40 | } -------------------------------------------------------------------------------- /unittest/data/hjmo00214 (fanza).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": null, 3 | "cid": "hjmo00214", 4 | "url": "https://www.dmm.co.jp/digital/videoa/-/detail/=/cid=hjmo00214/", 5 | "plot": "はじめ企画もついに7周年と言う事で、はじめ企画の大人気シリーズ・彼チン新作の登場です!!愛する彼氏のチンコを5本の中から当てることが出来たら賞金!!外れると大好きな彼氏の目の前で残酷な罰ゲームが!!チンコだけを頼りに必死になって探す彼女達、意外と分からない様で苦戦してます!!", 6 | "cover": "https://pics.dmm.co.jp/digital/video/hjmo00214/hjmo00214pl.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "ハイビジョン", 10 | "寝取り・寝取られ・NTR", 11 | "素人", 12 | "企画", 13 | "デジモ", 14 | "独占配信", 15 | "羞恥" 16 | ], 17 | "genre_id": [ 18 | "6533", 19 | "4111", 20 | "4024", 21 | "4007", 22 | "6004", 23 | "6548", 24 | "28" 25 | ], 26 | "genre_norm": null, 27 | "score": "8.00", 28 | "title": "彼女なら!彼氏のち○ぽ当ててみろ!! 11", 29 | "ori_title": null, 30 | "magnet": null, 31 | "serial": "彼女なら!彼氏のち○ぽ当ててみろ!!", 32 | "actress": [], 33 | "actress_pics": null, 34 | "director": "はじめ", 35 | "duration": "235", 36 | "producer": "はじめ企画", 37 | "publisher": null, 38 | "uncensored": false, 39 | "publish_date": "2011-12-08", 40 | "preview_pics": [ 41 | "https://pics.dmm.co.jp/digital/video/hjmo00214/hjmo00214-1.jpg", 42 | "https://pics.dmm.co.jp/digital/video/hjmo00214/hjmo00214-2.jpg", 43 | "https://pics.dmm.co.jp/digital/video/hjmo00214/hjmo00214-3.jpg", 44 | "https://pics.dmm.co.jp/digital/video/hjmo00214/hjmo00214-4.jpg", 45 | "https://pics.dmm.co.jp/digital/video/hjmo00214/hjmo00214-5.jpg", 46 | "https://pics.dmm.co.jp/digital/video/hjmo00214/hjmo00214-6.jpg", 47 | "https://pics.dmm.co.jp/digital/video/hjmo00214/hjmo00214-7.jpg", 48 | "https://pics.dmm.co.jp/digital/video/hjmo00214/hjmo00214-8.jpg", 49 | "https://pics.dmm.co.jp/digital/video/hjmo00214/hjmo00214-9.jpg", 50 | "https://pics.dmm.co.jp/digital/video/hjmo00214/hjmo00214-10.jpg" 51 | ], 52 | "preview_video": "https://cc3001.dmm.co.jp/litevideo/freepv/h/hjm/hjmo00214/hjmo00214_dmb_s.mp4" 53 | } -------------------------------------------------------------------------------- /unittest/data/hjmo00214 (msin).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "HJMO-214", 3 | "cid": "hjmo00214", 4 | "url": "https://db.msin.jp/jp.page/movie?id=226579", 5 | "plot": null, 6 | "cover": "https://pics.dmm.co.jp/digital/video/hjmo00214/hjmo00214pl.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "羞恥", 10 | "独占配信", 11 | "デジモ", 12 | "企画", 13 | "素人", 14 | "寝取り", 15 | "寝取られ", 16 | "NTR", 17 | "ハイビジョン" 18 | ], 19 | "genre_id": null, 20 | "genre_norm": null, 21 | "score": null, 22 | "title": "彼女なら!彼氏のち○ぽ当ててみろ!! 11", 23 | "ori_title": null, 24 | "magnet": null, 25 | "serial": "彼女なら!彼氏のち○ぽ当ててみろ!!", 26 | "actress": [ 27 | "りか(菊川里菜)", 28 | "めい", 29 | "なみ", 30 | "まい(倉木みお)", 31 | "ゆり" 32 | ], 33 | "actress_pics": { 34 | "りか(菊川里菜)": "https://img.msin.info/jp.images/actress/1011262.jpg", 35 | "めい": "https://db.msin.jp/.svg/Noimage.png", 36 | "なみ": "https://db.msin.jp/.svg/Noimage.png", 37 | "まい(倉木みお)": "https://img.msin.info/jp.images/actress/1005866.jpg", 38 | "ゆり": "https://db.msin.jp/.svg/Noimage.png" 39 | }, 40 | "director": "はじめ", 41 | "duration": "235", 42 | "producer": "はじめ企画", 43 | "publisher": null, 44 | "uncensored": false, 45 | "publish_date": "2011-12-08", 46 | "preview_pics": [ 47 | "https://pics.dmm.co.jp/digital/video/hjmo00214/hjmo00214jp-1.jpg", 48 | "https://pics.dmm.co.jp/digital/video/hjmo00214/hjmo00214jp-2.jpg", 49 | "https://pics.dmm.co.jp/digital/video/hjmo00214/hjmo00214jp-3.jpg", 50 | "https://pics.dmm.co.jp/digital/video/hjmo00214/hjmo00214jp-4.jpg", 51 | "https://pics.dmm.co.jp/digital/video/hjmo00214/hjmo00214jp-5.jpg", 52 | "https://pics.dmm.co.jp/digital/video/hjmo00214/hjmo00214jp-6.jpg", 53 | "https://pics.dmm.co.jp/digital/video/hjmo00214/hjmo00214jp-7.jpg", 54 | "https://pics.dmm.co.jp/digital/video/hjmo00214/hjmo00214jp-8.jpg", 55 | "https://pics.dmm.co.jp/digital/video/hjmo00214/hjmo00214jp-9.jpg", 56 | "https://pics.dmm.co.jp/digital/video/hjmo00214/hjmo00214jp-10.jpg" 57 | ], 58 | "preview_video": null 59 | } -------------------------------------------------------------------------------- /unittest/data/ipx-001 (javlib).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": "IPX-001", 3 | "cid": null, 4 | "url": "https://www.javlibrary.com/cn/?v=javliktmzq", 5 | "plot": null, 6 | "cover": "https://pics.dmm.co.jp/mono/movie/adult/ipx001/ipx001pl.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "单体作品", 10 | "高中女生", 11 | "巨乳", 12 | "第一人称摄影", 13 | "美少女", 14 | "深喉", 15 | "数位马赛克" 16 | ], 17 | "genre_id": null, 18 | "genre_norm": null, 19 | "score": "6.30", 20 | "title": "女子校生便所交際 便所でしか濡れないおじさん好きの変態マゾ美少女と思う存分便所ファック! 妃月るい", 21 | "ori_title": null, 22 | "magnet": null, 23 | "serial": null, 24 | "actress": [ 25 | "音琴るい" 26 | ], 27 | "actress_pics": null, 28 | "director": "刃修羅", 29 | "duration": "160", 30 | "producer": "IDEA POCKET", 31 | "publisher": "ティッシュ", 32 | "uncensored": null, 33 | "publish_date": "2017-08-11", 34 | "preview_pics": null, 35 | "preview_video": null 36 | } -------------------------------------------------------------------------------- /unittest/data/midv-001 (javdb).json: -------------------------------------------------------------------------------- 1 | { 2 | "注": "此文件用来测试多影片同番号的处理,各字段故意为空", 3 | "dvdid": "midv-001", 4 | "cid": null, 5 | "url": null, 6 | "plot": null, 7 | "cover": null, 8 | "big_cover": null, 9 | "genre": null, 10 | "genre_id": null, 11 | "genre_norm": null, 12 | "score": null, 13 | "title": null, 14 | "ori_title": null, 15 | "magnet": null, 16 | "serial": null, 17 | "actress": null, 18 | "actress_pics": null, 19 | "director": null, 20 | "duration": null, 21 | "producer": null, 22 | "publisher": null, 23 | "uncensored": null, 24 | "publish_date": null, 25 | "preview_pics": null, 26 | "preview_video": null 27 | } -------------------------------------------------------------------------------- /unittest/data/midv-001 (javlib).json: -------------------------------------------------------------------------------- 1 | { 2 | "注": "此文件用来测试多影片同番号的处理,各字段故意为空", 3 | "dvdid": "midv-001", 4 | "cid": null, 5 | "url": null, 6 | "plot": null, 7 | "cover": null, 8 | "big_cover": null, 9 | "genre": null, 10 | "genre_id": null, 11 | "genre_norm": null, 12 | "score": null, 13 | "title": null, 14 | "ori_title": null, 15 | "magnet": null, 16 | "serial": null, 17 | "actress": null, 18 | "actress_pics": null, 19 | "director": null, 20 | "duration": null, 21 | "producer": null, 22 | "publisher": null, 23 | "uncensored": null, 24 | "publish_date": null, 25 | "preview_pics": null, 26 | "preview_video": null 27 | } -------------------------------------------------------------------------------- /unittest/data/parathd03639 (fanza).json: -------------------------------------------------------------------------------- 1 | { 2 | "作为此类影片的代表": "https://www.dmm.co.jp/monthly/paradisetv/-/detail/=/cid=parathd03639/", 3 | "dvdid": null, 4 | "cid": "parathd03639", 5 | "url": "https://www.dmm.co.jp/digital/videoa/-/detail/=/cid=parathd03639/", 6 | "plot": "★推定Gカップの巨乳の奥さん、推定Hカップの長女、推定Hカップの次女が働いている家族経営のコンビニでバイトする俺が3人ともモノにする物語!3人の巨乳とSEXだ!◆推定Hカップの長女さくら(25)。胸の盛り上がる服に思わず手が出てしまった!バイト終わりのバックヤードで制服姿の長女とおっぱいを揺らしてSEX!◆推定Hカップの次女ねる(20)。バイト終わりに制服から私服へ着替えていた。我慢できず自分の住むアパートに誘って次女とヤる!Hカップ爆乳にパイズリしてもらい昇天寸前!◆推定Gカップの奥さん(45)。夫である店長が夜勤で不在。自宅を訪ねて奥さんともSEX!", 7 | "cover": "https://pics.dmm.co.jp/digital/video/parathd03639/parathd03639pl.jpg", 8 | "big_cover": null, 9 | "genre": [ 10 | "中出し", 11 | "ドラマ", 12 | "巨乳", 13 | "職業色々", 14 | "ハイビジョン", 15 | "パラダイスTV" 16 | ], 17 | "genre_id": [ 18 | "5001", 19 | "4114", 20 | "2001", 21 | "1026", 22 | "6533", 23 | "6008" 24 | ], 25 | "genre_norm": null, 26 | "score": "10.00", 27 | "title": "【連続スケベ小説 総集編】コンビニで働く巨乳母娘3人と中●しSEXしちゃった俺", 28 | "ori_title": null, 29 | "magnet": null, 30 | "serial": "連続スケベ小説", 31 | "actress": [], 32 | "actress_pics": null, 33 | "director": null, 34 | "duration": "115", 35 | "producer": "パラダイステレビ", 36 | "publisher": null, 37 | "uncensored": false, 38 | "publish_date": "2023-01-05", 39 | "preview_pics": [ 40 | "https://pics.dmm.co.jp/digital/video/parathd03639/parathd03639-1.jpg", 41 | "https://pics.dmm.co.jp/digital/video/parathd03639/parathd03639-2.jpg", 42 | "https://pics.dmm.co.jp/digital/video/parathd03639/parathd03639-3.jpg", 43 | "https://pics.dmm.co.jp/digital/video/parathd03639/parathd03639-4.jpg", 44 | "https://pics.dmm.co.jp/digital/video/parathd03639/parathd03639-5.jpg", 45 | "https://pics.dmm.co.jp/digital/video/parathd03639/parathd03639-6.jpg", 46 | "https://pics.dmm.co.jp/digital/video/parathd03639/parathd03639-7.jpg", 47 | "https://pics.dmm.co.jp/digital/video/parathd03639/parathd03639-8.jpg", 48 | "https://pics.dmm.co.jp/digital/video/parathd03639/parathd03639-9.jpg", 49 | "https://pics.dmm.co.jp/digital/video/parathd03639/parathd03639-10.jpg", 50 | "https://pics.dmm.co.jp/digital/video/parathd03639/parathd03639-11.jpg", 51 | "https://pics.dmm.co.jp/digital/video/parathd03639/parathd03639-12.jpg", 52 | "https://pics.dmm.co.jp/digital/video/parathd03639/parathd03639-13.jpg", 53 | "https://pics.dmm.co.jp/digital/video/parathd03639/parathd03639-14.jpg", 54 | "https://pics.dmm.co.jp/digital/video/parathd03639/parathd03639-15.jpg", 55 | "https://pics.dmm.co.jp/digital/video/parathd03639/parathd03639-16.jpg", 56 | "https://pics.dmm.co.jp/digital/video/parathd03639/parathd03639-17.jpg", 57 | "https://pics.dmm.co.jp/digital/video/parathd03639/parathd03639-18.jpg", 58 | "https://pics.dmm.co.jp/digital/video/parathd03639/parathd03639-19.jpg", 59 | "https://pics.dmm.co.jp/digital/video/parathd03639/parathd03639-20.jpg" 60 | ], 61 | "preview_video": "https://cc3001.dmm.co.jp/litevideo/freepv/p/par/parathd03639/parathd03639_mhb_w.mp4" 62 | } -------------------------------------------------------------------------------- /unittest/data/sqte00300 (fanza).json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdid": null, 3 | "cid": "sqte00300", 4 | "url": "https://www.dmm.co.jp/digital/videoa/-/detail/=/cid=sqte00300/", 5 | "plot": "女の子の自然なエロさを追求するアダルトサイトS-Cute。モデルみたいにスレンダーでなおかつお尻もオッパイも実に美味しそう!ちょっと恥ずかしがりな女の子だけど、脱いだらエロ美しい体をクネクネやらしくよがらせて感じまくる!ハメ潮まで吹いちゃうヘンタイっぷりに腰が止まりません!", 6 | "cover": "https://pics.dmm.co.jp/digital/video/sqte00300/sqte00300pl.jpg", 7 | "big_cover": null, 8 | "genre": [ 9 | "ハイビジョン", 10 | "美少女", 11 | "恋愛", 12 | "スレンダー", 13 | "騎乗位", 14 | "フェラ" 15 | ], 16 | "genre_id": [ 17 | "6533", 18 | "1027", 19 | "555", 20 | "2006", 21 | "4106", 22 | "5002" 23 | ], 24 | "genre_norm": null, 25 | "score": "8.00", 26 | "title": "おしゃべりは得意じゃないけど、脱いだらエロいって褒められます。", 27 | "ori_title": null, 28 | "magnet": null, 29 | "serial": null, 30 | "actress": [ 31 | "笠木いちか", 32 | "月乃ルナ" 33 | ], 34 | "actress_pics": null, 35 | "director": null, 36 | "duration": "196", 37 | "producer": "S-Cute", 38 | "publisher": null, 39 | "uncensored": false, 40 | "publish_date": "2020-05-10", 41 | "preview_pics": [ 42 | "https://pics.dmm.co.jp/digital/video/sqte00300/sqte00300-1.jpg", 43 | "https://pics.dmm.co.jp/digital/video/sqte00300/sqte00300-2.jpg", 44 | "https://pics.dmm.co.jp/digital/video/sqte00300/sqte00300-3.jpg", 45 | "https://pics.dmm.co.jp/digital/video/sqte00300/sqte00300-4.jpg", 46 | "https://pics.dmm.co.jp/digital/video/sqte00300/sqte00300-5.jpg", 47 | "https://pics.dmm.co.jp/digital/video/sqte00300/sqte00300-6.jpg", 48 | "https://pics.dmm.co.jp/digital/video/sqte00300/sqte00300-7.jpg", 49 | "https://pics.dmm.co.jp/digital/video/sqte00300/sqte00300-8.jpg", 50 | "https://pics.dmm.co.jp/digital/video/sqte00300/sqte00300-9.jpg", 51 | "https://pics.dmm.co.jp/digital/video/sqte00300/sqte00300-10.jpg", 52 | "https://pics.dmm.co.jp/digital/video/sqte00300/sqte00300-11.jpg", 53 | "https://pics.dmm.co.jp/digital/video/sqte00300/sqte00300-12.jpg", 54 | "https://pics.dmm.co.jp/digital/video/sqte00300/sqte00300-13.jpg", 55 | "https://pics.dmm.co.jp/digital/video/sqte00300/sqte00300-14.jpg", 56 | "https://pics.dmm.co.jp/digital/video/sqte00300/sqte00300-15.jpg", 57 | "https://pics.dmm.co.jp/digital/video/sqte00300/sqte00300-16.jpg", 58 | "https://pics.dmm.co.jp/digital/video/sqte00300/sqte00300-17.jpg", 59 | "https://pics.dmm.co.jp/digital/video/sqte00300/sqte00300-18.jpg", 60 | "https://pics.dmm.co.jp/digital/video/sqte00300/sqte00300-19.jpg", 61 | "https://pics.dmm.co.jp/digital/video/sqte00300/sqte00300-20.jpg" 62 | ], 63 | "preview_video": "https://cc3001.dmm.co.jp/litevideo/freepv/s/sqt/sqte00300/sqte00300_mhb_w.mp4" 64 | } -------------------------------------------------------------------------------- /unittest/test_avid.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import uuid 4 | import pytest 5 | from shutil import rmtree 6 | 7 | file_dir = os.path.dirname(__file__) 8 | sys.path.insert(0, os.path.abspath(os.path.join(file_dir, '..'))) 9 | from core.avid import * 10 | 11 | 12 | @pytest.fixture 13 | def prepare_files(files): 14 | """按照指定的文件列表创建对应的文件,并在测试完成后删除它们 15 | 16 | Args: 17 | files (list of tuple): 文件列表,仅接受相对路径 18 | """ 19 | tmp_folder = 'tmp_' + uuid.uuid4().hex[:8] 20 | for i in files: 21 | path = os.path.join(tmp_folder, i) 22 | folder = os.path.split(path)[0] 23 | if folder and (not os.path.exists(folder)): 24 | os.makedirs(folder) 25 | with open(path, 'wt', encoding='utf-8') as f: 26 | f.write(path) 27 | yield 28 | rmtree(tmp_folder) 29 | return 30 | 31 | 32 | def test_fc2(): 33 | assert 'FC2-123456' == get_id('(2017) [FC2-123456] 【個人撮影】') 34 | assert 'FC2-123456' == get_id('fc2-ppv-123456-1.delogo.mp4') 35 | assert 'FC2-123456' == get_id('FC2-PPV-123456.mp4') 36 | assert 'FC2-123456' == get_id('FC2PPV-123456 Yuukiy') 37 | assert 'FC2-1234567' == get_id('fc2-ppv_1234567-2.mp4') 38 | 39 | 40 | def test_normal(): 41 | assert '' == get_id('Yuukiy') 42 | assert 'ABC-12' == get_id('ABC-12_01.mkv') 43 | assert 'ABC-123' == get_id('Sky Angel Vol.6 月丘うさぎ(ABC-123).avi') 44 | assert 'ABCD-123' == get_id('ABCD-123.mp4') 45 | 46 | 47 | def test_cid_valid(): 48 | assert 'ab012st' == get_cid('ab012st') 49 | assert 'ab012st' == get_cid('ab012st.mp4') 50 | assert '123_0456' == get_cid('123_0456.mp4') 51 | assert '123abc00045' == get_cid('123abc00045.mp4') 52 | assert '403abcd56789' == get_cid('403abcd56789_1') 53 | assert 'h_001abc00001' == get_cid('h_001abc00001.mp4') 54 | assert '1234wvr00001rp' == get_cid('1234wvr00001rp.mp4') 55 | assert '402abc_hello000089' == get_cid('402abc_hello000089.mp4') 56 | assert 'h_826zizd021' == get_cid('h_826zizd021.mp4') 57 | 58 | 59 | def test_from_file(): 60 | # 用来控制是否将转换结果覆盖原文件(便于检查所有失败的条目) 61 | write_back = False 62 | rewrite_lines = [] 63 | 64 | datafile = os.path.join(file_dir, 'testdata_avid.txt') 65 | with open(datafile, 'rt', encoding='utf-8') as f: 66 | lines = f.readlines() 67 | for line_no, line in enumerate(lines, start=1): 68 | items = line.strip('\r\n').split('\t') 69 | if len(items) == 2: 70 | (filename, avid), ignore = items, False 71 | else: 72 | filename, avid, ignore = items 73 | guess_id = get_id(filename) 74 | if write_back: 75 | rewrite_lines.append(f'{filename}\t{guess_id}\n') 76 | continue 77 | if guess_id != avid: 78 | if ignore: 79 | print(f"Ignored: {guess_id} != {avid}\t'{filename}'") 80 | else: 81 | assert guess_id == avid, f'AV ID not match at line {line_no}' 82 | if write_back: 83 | with open(datafile, 'wt', encoding='utf-8') as f: 84 | f.writelines(rewrite_lines) 85 | 86 | 87 | def test_cid_invalid(): 88 | assert '' == get_cid('hasUpperletter.mp4') 89 | assert '' == get_cid('存在非ASCII字符.mp4') 90 | assert '' == get_cid('has-dash.mp4') 91 | assert '' == get_cid('403_abcd56789_fgh') 92 | assert '' == get_cid('many_parts1234-12.mp4') 93 | assert '' == get_cid('abc12.mp4') 94 | assert '' == get_cid('ab012st/仅文件夹名称为cid.mp4') 95 | assert '' == get_cid('123_0456st.mp4') 96 | 97 | 98 | @pytest.mark.parametrize('files', [('Unknown.mp4',)]) 99 | def test_by_folder_name1(prepare_files): 100 | assert '' == get_id('Unknown.mp4') 101 | 102 | 103 | @pytest.mark.parametrize('files', [('FC2-123456/Unknown.mp4',)]) 104 | def test_by_folder_name2(prepare_files): 105 | assert 'FC2-123456' == get_id('FC2-123456/Unknown.mp4') 106 | 107 | 108 | @pytest.mark.parametrize('files', [('ABC-123/CDF-456.mp4',)]) 109 | def test_by_folder_name2(prepare_files): 110 | assert 'CDF-456' == get_id('ABC-123/CDF-456.mp4') 111 | -------------------------------------------------------------------------------- /unittest/test_crawlers.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import logging 4 | import requests 5 | from urllib.parse import urlsplit 6 | 7 | 8 | file_dir = os.path.dirname(__file__) 9 | data_dir = os.path.join(file_dir, 'data') 10 | sys.path.insert(0, os.path.abspath(os.path.join(file_dir, '..'))) 11 | 12 | from core.datatype import MovieInfo 13 | from web.exceptions import CrawlerError, SiteBlocked 14 | 15 | 16 | logger = logging.getLogger(__name__) 17 | 18 | 19 | def test_crawler(crawler_params): 20 | """包装函数,便于通过参数判断测试用例生成,以及负责将参数解包后进行实际调用""" 21 | # crawler_params: ('ABC-123', 'javlib', 'path_to_local_json') 22 | # TODO: 在Github actions环境中总是无法通过Cloudflare的检测,因此暂时忽略需要过验证站点的失败项 23 | try: 24 | site, params = crawler_params[1], crawler_params[:2] 25 | compare(*crawler_params) 26 | except requests.exceptions.ReadTimeout: 27 | logger.warning(f"{site} 连接超时: {params}") 28 | except Exception as e: 29 | if os.getenv('GITHUB_ACTIONS') and (site in ['javdb', 'javlib', 'airav']): 30 | logger.debug(f'检测到Github actions环境,已忽略测试失败项: {params}') 31 | logger.exception(e) 32 | else: 33 | raise 34 | 35 | def compare(avid, scraper, file): 36 | """从本地的数据文件生成Movie实例,并与在线抓取到的数据进行比较""" 37 | local = MovieInfo(from_file=file) 38 | if scraper != 'fanza': 39 | online = MovieInfo(avid) 40 | else: 41 | online = MovieInfo(cid=avid) 42 | # 导入抓取器模块 43 | scraper_mod = 'web.' + scraper 44 | __import__(scraper_mod) 45 | mod = sys.modules[scraper_mod] 46 | if hasattr(mod, 'parse_clean_data'): 47 | parse_data = getattr(mod, 'parse_clean_data') 48 | else: 49 | parse_data = getattr(mod, 'parse_data') 50 | 51 | try: 52 | parse_data(online) 53 | except SiteBlocked as e: 54 | logger.warning(e) 55 | return 56 | except (CrawlerError, requests.exceptions.ReadTimeout) as e: 57 | logger.info(e) 58 | 59 | try: 60 | # 解包数据再进行比较,以便测试不通过时快速定位不相等的键值 61 | local_vars = vars(local) 62 | online_vars = vars(online) 63 | for k, v in online_vars.items(): 64 | # 部分字段可能随时间变化,因此只要这些字段不是一方有值一方无值就行 65 | if k in ['score', 'magnet']: 66 | assert bool(v) == bool(local_vars.get(k, None)) 67 | elif k == 'preview_video' and scraper in ['airav', 'javdb']: 68 | assert bool(v) == bool(local_vars.get(k, None)) 69 | # JavBus采用免代理域名时图片地址也会是免代理域名,因此只比较path部分即可 70 | elif k == 'cover' and scraper == 'javbus': 71 | assert urlsplit(v).path == urlsplit(local_vars.get(k, None)).path 72 | elif k == 'actress_pics' and scraper == 'javbus': 73 | local_tmp = online_tmp = {} 74 | local_pics = local_vars.get(k) 75 | if local_pics: 76 | local_tmp = {name: urlsplit(url).path for name, url in local_pics.items()} 77 | if v: 78 | online_tmp = {name: urlsplit(url).path for name, url in v.items()} 79 | assert local_tmp == online_tmp 80 | elif k == 'preview_pics' and scraper == 'javbus': 81 | local_pics = local_vars.get(k) 82 | if local_pics: 83 | local_tmp = [urlsplit(i).path for i in local_pics] 84 | if v: 85 | online_tmp = [urlsplit(i).path for i in v] 86 | assert local_tmp == online_tmp 87 | # 对顺序没有要求的list型字段,比较时也应该忽略顺序信息 88 | elif k in ['genre', 'genre_id', 'genre_norm', 'actress']: 89 | if isinstance(v, list): 90 | loc_v = local_vars.get(k) 91 | if loc_v is None: 92 | loc_v = [] 93 | assert sorted(v) == sorted(loc_v) 94 | else: 95 | assert v == local_vars.get(k, None) 96 | else: 97 | assert v == local_vars.get(k, None) 98 | except AssertionError: 99 | # 本地运行时更新已有的测试数据,方便利用版本控制系统检查差异项 100 | if not os.getenv('GITHUB_ACTIONS'): 101 | online.dump(file) 102 | raise 103 | except Exception as e: 104 | logger.error(e) 105 | raise 106 | -------------------------------------------------------------------------------- /unittest/test_exe.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import random 4 | import string 5 | import shutil 6 | from glob import glob 7 | 8 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 9 | from core.config import conf, rel_path_from_exe 10 | cfg, args = conf() 11 | 12 | 13 | def test_javsp_exe(): 14 | cwd = os.getcwd() 15 | dist_dir = os.path.normpath(os.path.join(os.path.dirname(__file__) + '/../dist')) 16 | os.chdir(dist_dir) 17 | 18 | size = cfg.File.ignore_video_file_less_than 19 | tmp_folder = 'TMP_' + ''.join(random.choices(string.ascii_uppercase, k=6)) 20 | FILE = '300MAAN-642.RIP.f4v' 21 | try: 22 | os.system(f"fsutil file createnew {FILE} {size}") 23 | exit_code = os.system(f"JavSP.exe --auto-exit --input . --output {tmp_folder}") 24 | assert exit_code == 0, f"Non-zero exit code: {exit_code}" 25 | # Check generated files 26 | files = glob(tmp_folder + '/**/*.*', recursive=True) 27 | assert all('横宮七海' in i for i in files), "Actress name not found" 28 | assert any(i.endswith('fanart.jpg') for i in files), "fanart not found" 29 | assert any(i.endswith('poster.jpg') for i in files), "poster not found" 30 | assert any(i.endswith('.f4v') for i in files), "video file not found" 31 | assert any(i.endswith('.nfo') for i in files), "nfo file not found" 32 | finally: 33 | if os.path.exists(FILE): 34 | os.remove(FILE) 35 | if os.path.exists(tmp_folder): 36 | shutil.rmtree(tmp_folder) 37 | os.chdir(cwd) 38 | 39 | -------------------------------------------------------------------------------- /unittest/test_func.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import random 4 | 5 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 6 | from core.func import * 7 | 8 | 9 | def test_remove_trail_actor_in_title(): 10 | run = remove_trail_actor_in_title 11 | delimiters = list('-xX &·,; &・,;') 12 | title1 = '东风夜放花千树,更吹落、星如雨。' 13 | title2 = '辛弃疾 ' + title1 14 | names = ['辛弃疾', '牛顿', '爱因斯坦', '阿基米德', '伽利略'] 15 | 16 | def combine(items): 17 | sep = random.choice(delimiters) 18 | new_str = sep.join(items) 19 | print(new_str) 20 | return new_str 21 | 22 | # 定义测试用例 23 | assert title1 == run(combine([title1, '辛弃疾']), names) 24 | assert title1 == run(combine([title1] + names), names) 25 | assert title1 == run(combine([title1, '辛弃疾']), names) 26 | assert title2 == run(combine([title2] + names), names) 27 | -------------------------------------------------------------------------------- /unittest/test_lib.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import random 4 | 5 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 6 | from core.lib import * 7 | 8 | 9 | def test_detect_special_attr(): 10 | run = detect_special_attr 11 | 12 | # 定义测试用例 13 | assert run('STARS-225_UNCENSORED_LEAKED.mp4') == 'U' 14 | assert run('STARS-225_UNCENSORED_LEAKED-C.mp4') == 'UC' 15 | assert run('STARS-225_无码.mp4') == '' 16 | assert run('STARS-225_无码流出.mp4') == 'U' 17 | assert run('STARS-225_无码破解.mp4') == 'U' 18 | assert run('STARS-225_UNCEN.mp4') == 'U' 19 | assert run('STARS-225_UNCEN-C.mp4') == 'UC' 20 | assert run('STARS-225u.mp4', 'STARS-225') == 'U' 21 | assert run('STARS-225C.mp4', 'STARS-225') == 'C' 22 | assert run('STARS-225uC.mp4', 'STARS-225') == 'UC' 23 | assert run('STARS225u.mp4', 'STARS-225') == 'U' 24 | assert run('STARS225C.mp4', 'STARS-225') == 'C' 25 | assert run('STARS225uC.mp4', 'STARS-225') == 'UC' 26 | -------------------------------------------------------------------------------- /unittest/test_proxyfree.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 5 | from web.proxyfree import * 6 | 7 | 8 | def test_get_url(): 9 | assert get_proxy_free_url('javlib') != '' 10 | assert get_proxy_free_url('javdb') != '' 11 | 12 | 13 | def test_get_url_with_prefer(): 14 | prefer_url = 'https://www.baidu.com' 15 | assert prefer_url == get_proxy_free_url('javlib', prefer_url) 16 | -------------------------------------------------------------------------------- /web/avsox.py: -------------------------------------------------------------------------------- 1 | """从avsox抓取数据""" 2 | import os 3 | import sys 4 | import logging 5 | 6 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 7 | from web.base import get_html 8 | from web.exceptions import * 9 | from core.config import conf, rel_path_from_exe 10 | cfg, args = conf() 11 | from core.datatype import MovieInfo 12 | 13 | 14 | logger = logging.getLogger(__name__) 15 | base_url = cfg.ProxyFree.avsox 16 | 17 | 18 | def parse_data(movie: MovieInfo): 19 | """解析指定番号的影片数据""" 20 | # avsox无法直接跳转到影片的网页,因此先搜索再从搜索结果中寻找目标网页 21 | full_id = movie.dvdid 22 | if full_id.startswith('FC2-'): 23 | full_id = full_id.replace('FC2-', 'FC2-PPV-') 24 | html = get_html(f'{base_url}/cn/search/{full_id}') 25 | ids = html.xpath("//div[@class='photo-info']/span/date[1]/text()") 26 | urls = html.xpath("//a[contains(@class, 'movie-box')]/@href") 27 | ids_lower = list(map(str.lower, ids)) 28 | if full_id.lower() in ids_lower: 29 | url = urls[ids_lower.index(full_id.lower())] 30 | else: 31 | raise MovieNotFoundError(__name__, movie.dvdid, ids) 32 | 33 | # 提取影片信息 34 | html = get_html(url) 35 | container = html.xpath("/html/body/div[@class='container']")[0] 36 | title = container.xpath("h3/text()")[0] 37 | cover = container.xpath("//a[@class='bigImage']/@href")[0] 38 | info = container.xpath("div/div[@class='col-md-3 info']")[0] 39 | dvdid = info.xpath("p/span[@style]/text()")[0] 40 | publish_date = info.xpath("p/span[text()='发行时间:']")[0].tail.strip() 41 | duration = info.xpath("p/span[text()='长度:']")[0].tail.replace('分钟', '').strip() 42 | producer, serial = None, None 43 | producer_tag = info.xpath("p[text()='制作商: ']")[0].getnext().xpath("a") 44 | if producer_tag: 45 | producer = producer_tag[0].text_content() 46 | serial_tag = info.xpath("p[text()='系列:']") 47 | if serial_tag: 48 | serial = serial_tag[0].getnext().xpath("a/text()")[0] 49 | genre = info.xpath("p/span[@class='genre']/a/text()") 50 | actress = container.xpath("//a[@class='avatar-box']/span/text()") 51 | 52 | movie.dvdid = dvdid.replace('FC2-PPV-', 'FC2-') 53 | movie.url = url 54 | movie.title = title.replace(dvdid, '').strip() 55 | movie.cover = cover 56 | movie.publish_date = publish_date 57 | movie.duration = duration 58 | movie.genre = genre 59 | movie.actress = actress 60 | if full_id.startswith('FC2-'): 61 | # avsox把FC2作品的拍摄者归类到'系列'而制作商固定为'FC2-PPV',这既不合理也与其他的站点不兼容,因此进行调整 62 | movie.producer = serial 63 | else: 64 | movie.producer = producer 65 | movie.serial = serial 66 | 67 | 68 | if __name__ == "__main__": 69 | import pretty_errors 70 | pretty_errors.configure(display_link=True) 71 | logger.root.handlers[1].level = logging.DEBUG 72 | 73 | movie = MovieInfo('FC2-718323') 74 | try: 75 | parse_data(movie) 76 | print(movie) 77 | except CrawlerError as e: 78 | logger.error(e, exc_info=1) 79 | -------------------------------------------------------------------------------- /web/avwiki.py: -------------------------------------------------------------------------------- 1 | """从av-wiki抓取数据""" 2 | import os 3 | import sys 4 | import logging 5 | 6 | 7 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 8 | from web.base import * 9 | from web.exceptions import * 10 | from core.datatype import MovieInfo 11 | 12 | logger = logging.getLogger(__name__) 13 | base_url = 'https://av-wiki.net' 14 | 15 | 16 | def parse_data(movie: MovieInfo): 17 | """从网页抓取并解析指定番号的数据 18 | Args: 19 | movie (MovieInfo): 要解析的影片信息,解析后的信息直接更新到此变量内 20 | """ 21 | movie.url = url = f'{base_url}/{movie.dvdid}' 22 | resp = request_get(url, delay_raise=True) 23 | if resp.status_code == 404: 24 | raise MovieNotFoundError(__name__, movie.dvdid) 25 | html = resp2html(resp) 26 | 27 | cover_tag = html.xpath("//header/div/a[@class='image-link-border']/img") 28 | if cover_tag: 29 | try: 30 | srcset = cover_tag[0].get('srcset').split(', ') 31 | src_set_urls = {} 32 | for src in srcset: 33 | url, width = src.split() 34 | width = int(width.rstrip('w')) 35 | src_set_urls[width] = url 36 | max_pic = sorted(src_set_urls.items(), key=lambda x:x[0], reverse=True) 37 | movie.cover = max_pic[0][1] 38 | except: 39 | movie.cover = cover_tag[0].get('src') 40 | body = html.xpath("//section[@class='article-body']")[0] 41 | title = body.xpath("div/p/text()")[0] 42 | title = title.replace(f"【{movie.dvdid}】", '') 43 | cite_url = body.xpath("div/cite/a/@href")[0] 44 | cite_url = cite_url.split('?aff=')[0] 45 | info = body.xpath("dl[@class='dltable']")[0] 46 | dt_txt_ls, dd_tags = info.xpath("dt/text()"), info.xpath("dd") 47 | data = {} 48 | for dt_txt, dd in zip(dt_txt_ls, dd_tags): 49 | a_tag = dd.xpath('a') 50 | if len(a_tag) == 0: 51 | dd_txt = dd.text 52 | else: 53 | dd_txt = a_tag[0].text 54 | data[dt_txt.strip()] = dd_txt.strip() 55 | 56 | ATTR_MAP = {'メーカー': 'producer', 'AV女優名': 'actress', 'メーカー品番': 'dvdid', 'シリーズ': 'serial', '配信開始日': 'publish_date'} 57 | for key, attr in ATTR_MAP.items(): 58 | setattr(movie, attr, data.get(key)) 59 | movie.title = title 60 | movie.uncensored = False # 服务器在日本且面向日本国内公开发售,不会包含无码片 61 | 62 | 63 | if __name__ == "__main__": 64 | import pretty_errors 65 | pretty_errors.configure(display_link=True) 66 | 67 | movie = MovieInfo('259LUXU-593') 68 | try: 69 | parse_data(movie) 70 | print(movie) 71 | except CrawlerError as e: 72 | logger.error(e, exc_info=1) 73 | -------------------------------------------------------------------------------- /web/dl_getchu.py: -------------------------------------------------------------------------------- 1 | """从dl.getchu官网抓取数据""" 2 | import os 3 | import sys 4 | import logging 5 | 6 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 7 | from web.base import resp2html, request_get 8 | from web.exceptions import * 9 | from core.datatype import MovieInfo 10 | 11 | logger = logging.getLogger(__name__) 12 | 13 | # https://dl.getchu.com/i/item4045373 14 | base_url = 'https://dl.getchu.com' 15 | # dl.getchu用utf-8会乱码 16 | base_encode = 'euc-jp' 17 | 18 | 19 | def get_movie_title(html): 20 | container = html.xpath("//form[@action='https://dl.getchu.com/cart/']/div/table[2]") 21 | if len(container) > 0: 22 | container = container[0] 23 | rows = container.xpath('.//tr') 24 | title = '' 25 | for row in rows: 26 | cell_texts = [] 27 | for cell in row.xpath('.//td/div'): 28 | # 获取单元格文本内容 29 | cell_text = None 30 | if cell.text: 31 | title = str(cell.text).strip() 32 | return title 33 | 34 | 35 | def get_movie_img(html, getchu_id): 36 | img_src = '' 37 | container = html.xpath(f'//img[contains(@src, "{getchu_id}top.jpg")]') 38 | if len(container) > 0: 39 | container = container[0] 40 | img_src = container.get('src') 41 | return img_src 42 | 43 | 44 | def get_movie_preview(html, getchu_id): 45 | preview_pics = [] 46 | container = html.xpath(f'//img[contains(@src, "{getchu_id}_")]') 47 | if len(container) > 0: 48 | for c in container: 49 | preview_pics.append(c.get('src')) 50 | return preview_pics 51 | 52 | 53 | def parse_data(movie: MovieInfo): 54 | """解析指定番号的影片数据""" 55 | # 去除番号中的'GETCHU'字样 56 | id_uc = movie.dvdid.upper() 57 | if not id_uc.startswith('GETCHU-'): 58 | raise ValueError('Invalid GETCHU number: ' + movie.dvdid) 59 | getchu_id = id_uc.replace('GETCHU-', '') 60 | # 抓取网页 61 | url = f'{base_url}/i/item{getchu_id}' 62 | r = request_get(url, delay_raise=True) 63 | if r.status_code == 404: 64 | raise MovieNotFoundError(__name__, movie.dvdid) 65 | html = resp2html(r, base_encode) 66 | container = html.xpath("//form[@action='https://dl.getchu.com/cart/']/div/table[3]") 67 | if len(container) > 0: 68 | container = container[0] 69 | # 将表格提取为键值对 70 | rows = container.xpath('.//table/tr') 71 | kv_rows = [i for i in rows if len(i) == 2] 72 | data = {} 73 | for row in kv_rows: 74 | # 获取单元格文本内容 75 | key = row.xpath("td[@class='bluetext']/text()")[0] 76 | # 是否包含a标签: 有的属性是用表示的,不是text 77 | a_tags = row.xpath("td[2]/a") 78 | if a_tags: 79 | value = [i.text for i in a_tags] 80 | else: 81 | # 获取第2个td标签的内容(下标从1开始计数) 82 | value = row.xpath("td[2]/text()") 83 | if len(value) > 0: 84 | value = value[0].strip() 85 | data[key] = value 86 | 87 | for key, value in data.items(): 88 | if key == 'サークル': 89 | producer = value[0] 90 | elif key == '作者': 91 | # 暂时没有在getchu找到多个actress的片子 92 | actress = [value] 93 | elif key == '画像数&ページ数': 94 | duration = value.replace('分', '') 95 | elif key == '配信開始日': 96 | publish_date = value.replace('/', '-') 97 | elif key == '趣向': 98 | genre = value 99 | elif key == '作品内容': 100 | plot = value 101 | 102 | movie.title = get_movie_title(html) 103 | movie.cover = get_movie_img(html, getchu_id) 104 | movie.preview_pics = get_movie_preview(html, getchu_id) 105 | movie.dvdid = id_uc 106 | movie.url = url 107 | movie.producer = producer 108 | movie.actress = actress 109 | movie.duration = duration 110 | movie.publish_date = publish_date 111 | movie.genre = genre 112 | movie.plot = plot 113 | 114 | 115 | if __name__ == "__main__": 116 | import pretty_errors 117 | 118 | pretty_errors.configure(display_link=True) 119 | logger.root.handlers[1].level = logging.DEBUG 120 | 121 | movie = MovieInfo('getchu-4053720') 122 | try: 123 | parse_data(movie) 124 | print(movie) 125 | except CrawlerError as e: 126 | logger.error(e, exc_info=1) 127 | -------------------------------------------------------------------------------- /web/exceptions.py: -------------------------------------------------------------------------------- 1 | """网页抓取相关的异常""" 2 | __all__ = ['CrawlerError', 'MovieNotFoundError', 'MovieDuplicateError', 'SiteBlocked', 3 | 'SitePermissionError', 'CredentialError', 'WebsiteError', 'OtherError'] 4 | 5 | 6 | class CrawlerError(Exception): 7 | """所有站点抓取器相关异常的基类""" 8 | 9 | 10 | class MovieNotFoundError(CrawlerError): 11 | """表示某个站点没有抓取到某部影片""" 12 | # 保持异常消息的简洁,同时又支持使用'logger.info(e, exc_info=True)'记录完整信息 13 | def __init__(self, mod, avid, *args) -> None: 14 | msg = f"{mod}: 未找到影片: '{avid}'" 15 | super().__init__(msg, *args) 16 | 17 | def __str__(self): 18 | return self.args[0] 19 | 20 | 21 | class MovieDuplicateError(CrawlerError): 22 | """影片重复""" 23 | def __init__(self, mod, avid, dup_count, *args) -> None: 24 | msg = f"{mod}: '{avid}': 存在{dup_count}个完全匹配目标番号的搜索结果" 25 | super().__init__(msg, *args) 26 | 27 | def __str__(self): 28 | return self.args[0] 29 | 30 | 31 | class SiteBlocked(CrawlerError): 32 | """由于IP段或者触发反爬机制等原因导致用户被站点封锁""" 33 | 34 | 35 | class SitePermissionError(CrawlerError): 36 | """由于缺少权限而无法访问影片资源""" 37 | 38 | 39 | class CredentialError(CrawlerError): 40 | """由于缺少Cookies等凭据而无法访问影片资源""" 41 | 42 | 43 | class WebsiteError(CrawlerError): 44 | """非预期的状态码等网页故障""" 45 | 46 | 47 | class OtherError(CrawlerError): 48 | """其他尚未分类的错误""" 49 | -------------------------------------------------------------------------------- /web/fc2.py: -------------------------------------------------------------------------------- 1 | """从FC2官网抓取数据""" 2 | import os 3 | import sys 4 | import logging 5 | 6 | 7 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 8 | from web.base import get_html, request_get 9 | from web.exceptions import * 10 | from core.config import conf, rel_path_from_exe 11 | cfg, args = conf() 12 | from core.lib import strftime_to_minutes 13 | from core.datatype import MovieInfo 14 | 15 | 16 | logger = logging.getLogger(__name__) 17 | base_url = 'https://adult.contents.fc2.com' 18 | 19 | 20 | def get_movie_score(fc2_id): 21 | """通过评论数据来计算FC2的影片评分(10分制),无法获得评分时返回None""" 22 | html = get_html(f'{base_url}/article/{fc2_id}/review') 23 | review_tags = html.xpath("//ul[@class='items_comment_headerReviewInArea']/li") 24 | reviews = {} 25 | for tag in review_tags: 26 | score = int(tag.xpath("div/span/text()")[0]) 27 | vote = int(tag.xpath("span")[0].text_content()) 28 | reviews[score] = vote 29 | total_votes = sum(reviews.values()) 30 | if (total_votes >= 2): # 至少也该有两个人评价才有参考意义一点吧 31 | summary = sum([k*v for k, v in reviews.items()]) 32 | final_score = summary / total_votes * 2 # 乘以2转换为10分制 33 | return final_score 34 | 35 | 36 | def parse_data(movie: MovieInfo): 37 | """解析指定番号的影片数据""" 38 | # 去除番号中的'FC2'字样 39 | id_uc = movie.dvdid.upper() 40 | if not id_uc.startswith('FC2-'): 41 | raise ValueError('Invalid FC2 number: ' + movie.dvdid) 42 | fc2_id = id_uc.replace('FC2-', '') 43 | # 抓取网页 44 | url = f'{base_url}/article/{fc2_id}/' 45 | html = get_html(url) 46 | container = html.xpath("//div[@class='items_article_left']") 47 | if len(container) > 0: 48 | container = container[0] 49 | else: 50 | raise MovieNotFoundError(__name__, movie.dvdid) 51 | title = container.xpath("//div[@class='items_article_headerInfo']/h3/text()")[0] 52 | thumb_tag = container.xpath("//div[@class='items_article_MainitemThumb']")[0] 53 | thumb_pic = thumb_tag.xpath("span/img/@src")[0] 54 | duration_str = thumb_tag.xpath("span/p[@class='items_article_info']/text()")[0] 55 | # FC2没有制作商和发行商的区分,作为个人市场,影片页面的'by'更接近于制作商 56 | producer = container.xpath("//li[text()='by ']/a/text()")[0] 57 | genre = container.xpath("//a[@class='tag tagTag']/text()") 58 | date_str = container.xpath("//div[@class='items_article_Releasedate']/p/text()")[0] 59 | publish_date = date_str[-10:].replace('/', '-') # '販売日 : 2017/11/30' 60 | preview_pics = container.xpath("//ul[@data-feed='sample-images']/li/a/@href") 61 | 62 | if cfg.Crawler.hardworking_mode: 63 | # 通过评论数据来计算准确的评分 64 | score = get_movie_score(fc2_id) 65 | if score: 66 | movie.score = f'{score:.2f}' 67 | # 预览视频是动态加载的,不在静态网页中 68 | desc_frame_url = container.xpath("//section[@class='items_article_Contents']/iframe/@src")[0] 69 | key = desc_frame_url.split('=')[-1] # /widget/article/718323/description?ac=60fc08fa... 70 | api_url = f'{base_url}/api/v2/videos/{fc2_id}/sample?key={key}' 71 | r = request_get(api_url).json() 72 | movie.preview_video = r['path'] 73 | else: 74 | # 获取影片评分。影片页面的评分只能粗略到星级,且没有分数,要通过类名来判断,如'items_article_Star5'表示5星 75 | score_tag_attr = container.xpath("//a[@class='items_article_Stars']/p/span/@class")[0] 76 | score = int(score_tag_attr[-1]) * 2 77 | movie.score = f'{score:.2f}' 78 | 79 | movie.dvdid = id_uc 80 | movie.url = url 81 | movie.title = title 82 | movie.genre = genre 83 | movie.producer = producer 84 | movie.duration = str(strftime_to_minutes(duration_str)) 85 | movie.publish_date = publish_date 86 | movie.preview_pics = preview_pics 87 | # FC2的封面是220x220的,和正常封面尺寸、比例都差太多。如果有预览图片,则使用第一张预览图作为封面 88 | if movie.preview_pics: 89 | movie.cover = preview_pics[0] 90 | else: 91 | movie.cover = thumb_pic 92 | 93 | 94 | if __name__ == "__main__": 95 | import pretty_errors 96 | pretty_errors.configure(display_link=True) 97 | logger.root.handlers[1].level = logging.DEBUG 98 | 99 | movie = MovieInfo('FC2-12345') 100 | try: 101 | parse_data(movie) 102 | print(movie) 103 | except CrawlerError as e: 104 | logger.error(e, exc_info=1) 105 | -------------------------------------------------------------------------------- /web/fc2fan.py: -------------------------------------------------------------------------------- 1 | """解析fc2fan本地镜像的数据""" 2 | # FC2官网的影片下架就无法再抓取数据,如果用户有fc2fan的镜像,那可以尝试从镜像中解析影片数据 3 | import os 4 | import re 5 | import sys 6 | import logging 7 | import lxml.html 8 | import requests 9 | 10 | 11 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 12 | from web.base import resp2html 13 | from web.exceptions import * 14 | from core.config import conf, rel_path_from_exe 15 | cfg, args = conf() 16 | from core.datatype import MovieInfo 17 | 18 | 19 | logger = logging.getLogger(__name__) 20 | base_path = cfg.Crawler.fc2fan_local_path 21 | use_local_mirror = os.path.exists(base_path) 22 | 23 | 24 | def parse_data(movie: MovieInfo): 25 | """解析指定番号的影片数据""" 26 | if use_local_mirror: 27 | html_file = f'{base_path}/{movie.dvdid}.html' 28 | if not os.path.exists(html_file): 29 | raise MovieNotFoundError(__name__, movie.dvdid, html_file) 30 | html = lxml.html.parse(html_file) 31 | else: 32 | url = f"https://fc2club.top/html/{movie.dvdid}.html" 33 | r = requests.get(url) 34 | if r.status_code == 404: 35 | raise MovieNotFoundError(__name__, movie.dvdid) 36 | elif r.text == '': 37 | raise WebsiteError(f'fc2fan: 站点不可用 (HTTP {r.status_code}): {url}') 38 | html = resp2html(r) 39 | try: 40 | container = html.xpath("//div[@class='col-sm-8']")[0] 41 | except IndexError: 42 | raise WebsiteError(f'fc2fan: 站点不可用') 43 | title = container.xpath("h3/text()")[0] 44 | score_str = container.xpath("h5/strong[text()='影片评分']")[0].tail.strip() 45 | match = re.search(r'\d+', score_str) 46 | if match: 47 | score = int(match.group()) / 10 # fc2fan站长是按100分来打分的 48 | movie.score = f'{score:.1f}' 49 | resource_info = container.xpath("h5/strong[text()='资源参数']")[0].tail 50 | if '无码' in resource_info: 51 | movie.uncensored = True 52 | elif '有码' in resource_info: 53 | movie.uncensored = False 54 | # FC2没有制作商和发行商的区分,作为个人市场,卖家更接近于制作商 55 | producer = container.xpath("h5/strong[text()='卖家信息']")[0].getnext().text 56 | if producer: 57 | movie.producer = producer.strip() 58 | genre = container.xpath("h5/strong[text()='影片标签']/../a/text()") 59 | actress = container.xpath("h5/strong[text()='女优名字']/../a/text()") 60 | preview_pics = container.xpath("//ul[@class='slides']/li/img/@src") 61 | if use_local_mirror: 62 | preview_pics = [os.path.normpath(os.path.join(base_path, i)) for i in preview_pics] 63 | # big_preview = container.xpath("//img[@id='thumbpic']/../@href")[0] # 影片真实截图,目前暂时用不到 64 | 65 | movie.title = title 66 | movie.genre = genre 67 | movie.actress = actress 68 | if preview_pics: 69 | movie.preview_pics = preview_pics 70 | movie.cover = preview_pics[0] 71 | 72 | 73 | if __name__ == "__main__": 74 | import pretty_errors 75 | pretty_errors.configure(display_link=True) 76 | logger.root.handlers[1].level = logging.DEBUG 77 | 78 | movie = MovieInfo('FC2-1879420') 79 | try: 80 | parse_data(movie) 81 | print(movie) 82 | except CrawlerError as e: 83 | logger.error(e, exc_info=1) 84 | -------------------------------------------------------------------------------- /web/gyutto.py: -------------------------------------------------------------------------------- 1 | """从https://gyutto.com/官网抓取数据""" 2 | import os 3 | import sys 4 | import logging 5 | import time 6 | 7 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 8 | from web.base import resp2html, request_get 9 | from web.exceptions import * 10 | from core.datatype import MovieInfo 11 | 12 | logger = logging.getLogger(__name__) 13 | 14 | # https://dl.gyutto.com/i/item266923 15 | base_url = 'http://gyutto.com' 16 | base_encode = 'euc-jp' 17 | 18 | def get_movie_title(html): 19 | container = html.xpath("//h1") 20 | if len(container) > 0: 21 | container = container[0] 22 | title = container.text 23 | 24 | return title 25 | 26 | def get_movie_img(html, index = 1): 27 | images = [] 28 | container = html.xpath("//a[@class='highslide']/img") 29 | if len(container) > 0: 30 | if index == 0: 31 | return container[0].get('src') 32 | 33 | for row in container: 34 | images.append(row.get('src')) 35 | 36 | return images 37 | 38 | def parse_data(movie: MovieInfo): 39 | """解析指定番号的影片数据""" 40 | # 去除番号中的'gyutto'字样 41 | id_uc = movie.dvdid.upper() 42 | if not id_uc.startswith('GYUTTO-'): 43 | raise ValueError('Invalid gyutto number: ' + movie.dvdid) 44 | gyutto_id = id_uc.replace('GYUTTO-', '') 45 | # 抓取网页 46 | url = f'{base_url}/i/item{gyutto_id}?select_uaflag=1' 47 | r = request_get(url, delay_raise=True) 48 | if r.status_code == 404: 49 | raise MovieNotFoundError(__name__, movie.dvdid) 50 | html = resp2html(r, base_encode) 51 | container = html.xpath("//dl[@class='BasicInfo clearfix']") 52 | 53 | for row in container: 54 | key = row.xpath(".//dt/text()") 55 | if key[0] == "サークル": 56 | producer = ''.join(row.xpath(".//dd/a/text()")) 57 | elif key[0] == "ジャンル": 58 | genre = row.xpath(".//dd/a/text()") 59 | elif key[0] == "配信開始日": 60 | date = row.xpath(".//dd/text()") 61 | date_str = ''.join(date) 62 | date_time = time.strptime(date_str, "%Y年%m月%d日") 63 | publish_date = time.strftime("%Y-%m-%d", date_time) 64 | 65 | plot = html.xpath("//div[@class='unit_DetailLead']/p/text()")[0] 66 | 67 | movie.title = get_movie_title(html) 68 | movie.cover = get_movie_img(html, 0) 69 | movie.preview_pics = get_movie_img(html) 70 | movie.dvdid = id_uc 71 | movie.url = url 72 | movie.producer = producer 73 | # movie.actress = actress 74 | # movie.duration = duration 75 | movie.publish_date = publish_date 76 | movie.genre = genre 77 | movie.plot = plot 78 | 79 | if __name__ == "__main__": 80 | import pretty_errors 81 | 82 | pretty_errors.configure(display_link=True) 83 | logger.root.handlers[1].level = logging.DEBUG 84 | movie = MovieInfo('gyutto-266923') 85 | 86 | try: 87 | parse_data(movie) 88 | print(movie) 89 | except CrawlerError as e: 90 | logger.error(e, exc_info=1) 91 | -------------------------------------------------------------------------------- /web/jav321.py: -------------------------------------------------------------------------------- 1 | """从jav321抓取数据""" 2 | import os 3 | import re 4 | import sys 5 | import logging 6 | 7 | 8 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 9 | from web.base import post_html 10 | from web.exceptions import * 11 | from core.datatype import MovieInfo 12 | 13 | 14 | logger = logging.getLogger(__name__) 15 | base_url = 'https://www.jav321.com' 16 | 17 | 18 | def parse_data(movie: MovieInfo): 19 | """解析指定番号的影片数据""" 20 | html = post_html(f'{base_url}/search', data={'sn': movie.dvdid}) 21 | page_url = html.xpath("//ul[@class='dropdown-menu']/li/a/@href")[0] 22 | #TODO: 注意cid是dmm的概念。如果影片来自MGSTAGE,这里的cid很可能是jav321自己添加的,例如 345SIMM-542 23 | cid = page_url.split('/')[-1] # /video/ipx00177 24 | # 如果从URL匹配到的cid是'search',说明还停留在搜索页面,找不到这部影片 25 | if cid == 'search': 26 | raise MovieNotFoundError(__name__, movie.dvdid) 27 | title = html.xpath("//div[@class='panel-heading']/h3/text()")[0] 28 | info = html.xpath("//div[@class='col-md-9']")[0] 29 | # jav321的不同信息字段间没有明显分隔,只能通过url来匹配目标标签 30 | company_tags = info.xpath("a[contains(@href,'/company/')]/text()") 31 | if company_tags: 32 | movie.producer = company_tags[0] 33 | # actress, actress_pics 34 | # jav321现在连女优信息都没有了,首页通过女优栏跳转过去也全是空白 35 | actress, actress_pics = [], {} 36 | actress_tags = html.xpath("//div[@class='thumbnail']/a[contains(@href,'/star/')]/img") 37 | for tag in actress_tags: 38 | name = tag.tail.strip() 39 | pic_url = tag.get('src') 40 | actress.append(name) 41 | # jav321的女优头像完全是应付了事:即使女优实际没有头像,也会有一个看起来像模像样的url, 42 | # 因而无法通过url判断女优头像图片是否有效。有其他选择时最好不要使用jav321的女优头像数据 43 | actress_pics[name] = pic_url 44 | # genre, genre_id 45 | genre_tags = info.xpath("a[contains(@href,'/genre/')]") 46 | genre, genre_id = [], [] 47 | for tag in genre_tags: 48 | genre.append(tag.text) 49 | genre_id.append(tag.get('href').split('/')[-2]) # genre/4025/1 50 | dvdid = info.xpath("b[text()='品番']")[0].tail.replace(': ', '').upper() 51 | publish_date = info.xpath("b[text()='配信開始日']")[0].tail.replace(': ', '') 52 | duration_str = info.xpath("b[text()='収録時間']")[0].tail 53 | match = re.search(r'\d+', duration_str) 54 | if match: 55 | movie.duration = match.group(0) 56 | # 仅部分影片有评分且评分只能粗略到星级而没有分数,要通过星级的图片来判断,如'/img/35.gif'表示3.5星 57 | score_tag = info.xpath("//b[text()='平均評価']/following-sibling::img/@data-original") 58 | if score_tag: 59 | score = int(score_tag[0][5:7])/5 # /10*2 60 | movie.score = str(score) 61 | serial_tag = info.xpath("a[contains(@href,'/series/')]/text()") 62 | if serial_tag: 63 | movie.serial = serial_tag[0] 64 | preview_video_tag = info.xpath("//video/source/@src") 65 | if preview_video_tag: 66 | movie.preview_video = preview_video_tag[0] 67 | plot_tag = info.xpath("//div[@class='panel-body']/div[@class='row']/div[@class='col-md-12']/text()") 68 | if plot_tag: 69 | movie.plot = plot_tag[0] 70 | preview_pics = html.xpath("//div[@class='col-xs-12 col-md-12']/p/a/img[@class='img-responsive']/@src") 71 | if len(preview_pics) == 0: 72 | # 尝试搜索另一种布局下的封面,需要使用onerror过滤掉明明没有封面时网站往里面塞的默认URL 73 | preview_pics = html.xpath("//div/div/div[@class='col-md-3']/img[@onerror and @class='img-responsive']/@src") 74 | # 磁力和ed2k链接是依赖js脚本加载的,无法通过静态网页来解析 75 | 76 | movie.url = page_url 77 | movie.cid = cid 78 | movie.dvdid = dvdid 79 | movie.title = title 80 | movie.actress = actress 81 | movie.actress_pics = actress_pics 82 | movie.genre = genre 83 | movie.genre_id = genre_id 84 | movie.publish_date = publish_date 85 | # preview_pics的第一张图始终是封面,剩下的才是预览图 86 | if len(preview_pics) > 0: 87 | movie.cover = preview_pics[0] 88 | movie.preview_pics = preview_pics[1:] 89 | 90 | 91 | if __name__ == "__main__": 92 | import pretty_errors 93 | pretty_errors.configure(display_link=True) 94 | logger.root.handlers[1].level = logging.DEBUG 95 | 96 | movie = MovieInfo('SCUTE-1177') 97 | try: 98 | parse_data(movie) 99 | print(movie) 100 | except CrawlerError as e: 101 | logger.error(e, exc_info=1) 102 | -------------------------------------------------------------------------------- /web/javbus.py: -------------------------------------------------------------------------------- 1 | """从JavBus抓取数据""" 2 | import os 3 | import sys 4 | import logging 5 | 6 | 7 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 8 | from web.base import * 9 | from web.exceptions import * 10 | from core.func import * 11 | from core.config import conf, rel_path_from_exe 12 | cfg, args = conf() 13 | from core.datatype import MovieInfo, GenreMap 14 | 15 | 16 | logger = logging.getLogger(__name__) 17 | genre_map = GenreMap('data/genre_javbus.csv') 18 | permanent_url = 'https://www.javbus.com' 19 | if cfg.Network.proxy: 20 | base_url = permanent_url 21 | else: 22 | base_url = cfg.ProxyFree.javbus 23 | 24 | 25 | def parse_data(movie: MovieInfo): 26 | """从网页抓取并解析指定番号的数据 27 | Args: 28 | movie (MovieInfo): 要解析的影片信息,解析后的信息直接更新到此变量内 29 | """ 30 | url = f'{base_url}/{movie.dvdid}' 31 | resp = request_get(url, delay_raise=True) 32 | # 疑似JavBus检测到类似爬虫的行为时会要求登录,不过发现目前不需要登录也可以从重定向前的网页中提取信息 33 | if resp.history and resp.history[0].status_code == 302: 34 | html = resp2html(resp.history[0]) 35 | else: 36 | html = resp2html(resp) 37 | # 引入登录验证后状态码不再准确,因此还要额外通过检测标题来确认是否发生了404 38 | page_title = html.xpath('/html/head/title/text()') 39 | if page_title and page_title[0].startswith('404 Page Not Found!'): 40 | raise MovieNotFoundError(__name__, movie.dvdid) 41 | 42 | container = html.xpath("//div[@class='container']")[0] 43 | title = container.xpath("h3/text()")[0] 44 | cover = container.xpath("//a[@class='bigImage']/img/@src")[0] 45 | preview_pics = container.xpath("//div[@id='sample-waterfall']/a/@href") 46 | info = container.xpath("//div[@class='col-md-3 info']")[0] 47 | dvdid = info.xpath("p/span[text()='識別碼:']")[0].getnext().text 48 | publish_date = info.xpath("p/span[text()='發行日期:']")[0].tail.strip() 49 | duration = info.xpath("p/span[text()='長度:']")[0].tail.replace('分鐘', '').strip() 50 | director_tag = info.xpath("p/span[text()='導演:']") 51 | if director_tag: # xpath没有匹配时将得到空列表 52 | movie.director = director_tag[0].getnext().text.strip() 53 | producer_tag = info.xpath("p/span[text()='製作商:']") 54 | if producer_tag: 55 | text = producer_tag[0].getnext().text 56 | if text: 57 | movie.producer = text.strip() 58 | publisher_tag = info.xpath("p/span[text()='發行商:']") 59 | if publisher_tag: 60 | movie.publisher = publisher_tag[0].getnext().text.strip() 61 | serial_tag = info.xpath("p/span[text()='系列:']") 62 | if serial_tag: 63 | movie.serial = serial_tag[0].getnext().text 64 | # genre, genre_id 65 | genre_tags = info.xpath("//span[@class='genre']/label/a") 66 | genre, genre_id = [], [] 67 | for tag in genre_tags: 68 | tag_url = tag.get('href') 69 | pre_id = tag_url.split('/')[-1] 70 | genre.append(tag.text) 71 | if 'uncensored' in tag_url: 72 | movie.uncensored = True 73 | genre_id.append('uncensored-' + pre_id) 74 | else: 75 | movie.uncensored = False 76 | genre_id.append(pre_id) 77 | # JavBus的磁力链接是依赖js脚本加载的,无法通过静态网页来解析 78 | # actress, actress_pics 79 | actress, actress_pics = [], {} 80 | actress_tags = html.xpath("//a[@class='avatar-box']/div/img") 81 | for tag in actress_tags: 82 | name = tag.get('title') 83 | pic_url = tag.get('src') 84 | actress.append(name) 85 | if not pic_url.endswith('nowprinting.gif'): # 略过默认的头像 86 | actress_pics[name] = pic_url 87 | # 整理数据并更新movie的相应属性 88 | movie.url = f'{permanent_url}/{movie.dvdid}' 89 | movie.dvdid = dvdid 90 | movie.title = title.replace(dvdid, '').strip() 91 | movie.cover = cover 92 | movie.preview_pics = preview_pics 93 | if publish_date != '0000-00-00': # 丢弃无效的发布日期 94 | movie.publish_date = publish_date 95 | movie.duration = duration if int(duration) else None 96 | movie.genre = genre 97 | movie.genre_id = genre_id 98 | movie.actress = actress 99 | movie.actress_pics = actress_pics 100 | 101 | 102 | def parse_clean_data(movie: MovieInfo): 103 | """解析指定番号的影片数据并进行清洗""" 104 | parse_data(movie) 105 | movie.genre_norm = genre_map.map(movie.genre_id) 106 | movie.genre_id = None # 没有别的地方需要再用到,清空genre id(暗示已经完成转换) 107 | 108 | 109 | if __name__ == "__main__": 110 | import pretty_errors 111 | pretty_errors.configure(display_link=True) 112 | logger.root.handlers[1].level = logging.DEBUG 113 | 114 | movie = MovieInfo('NANP-030') 115 | try: 116 | parse_clean_data(movie) 117 | print(movie) 118 | except CrawlerError as e: 119 | logger.error(e, exc_info=1) 120 | -------------------------------------------------------------------------------- /web/javmenu.py: -------------------------------------------------------------------------------- 1 | """从JavMenu抓取数据""" 2 | import os 3 | import sys 4 | import logging 5 | 6 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 7 | from web.base import Request, resp2html 8 | from web.exceptions import * 9 | from core.datatype import MovieInfo 10 | 11 | 12 | request = Request() 13 | 14 | logger = logging.getLogger(__name__) 15 | base_url = 'https://mrzyx.xyz' 16 | 17 | 18 | def parse_data(movie: MovieInfo): 19 | """从网页抓取并解析指定番号的数据 20 | Args: 21 | movie (MovieInfo): 要解析的影片信息,解析后的信息直接更新到此变量内 22 | """ 23 | # JavMenu网页做得很不走心,将就了 24 | url = f'{base_url}/{movie.dvdid}' 25 | r = request.get(url) 26 | if r.history: 27 | # 被重定向到主页说明找不到影片资源 28 | raise MovieNotFoundError(__name__, movie.dvdid) 29 | 30 | html = resp2html(r) 31 | container = html.xpath("//div[@class='col-md-9 px-0']")[0] 32 | title = container.xpath("div[@class='col-12 mb-3']/h1/strong/text()")[0] 33 | # 竟然还在标题里插广告,真的疯了。要不是我已经写了抓取器,才懒得维护这个破站 34 | title = title.replace(' | JAV目錄大全 | 每日更新', '') 35 | title = title.replace(' 免費在線看', '').replace(' 免費AV在線看', '') 36 | cover_tag = container.xpath("//div[@class='single-video']")[0] 37 | video_tag = cover_tag.find('video') 38 | if video_tag is not None: 39 | # URL首尾竟然也有空格…… 40 | movie.cover = video_tag.get('data-poster').strip() 41 | # 预览影片改为blob了,无法获取 42 | # movie.preview_video = video_tag.find('source').get('src').strip() 43 | else: 44 | cover_img_tag = container.xpath("//img[@class='lazy rounded']/@data-src") 45 | if cover_img_tag: 46 | movie.cover = cover_img_tag[0].strip() 47 | info = container.xpath("//div[@class='card-body']")[0] 48 | publish_date = info.xpath("div/span[contains(text(), '日期:')]")[0].getnext().text 49 | duration = info.xpath("div/span[contains(text(), '時長:')]")[0].getnext().text.replace('分鐘', '') 50 | producer = info.xpath("div/span[contains(text(), '製作:')]/following-sibling::a/span/text()") 51 | if producer: 52 | movie.producer = producer[0] 53 | genre_tags = info.xpath("//a[@class='genre']") 54 | genre, genre_id = [], [] 55 | for tag in genre_tags: 56 | items = tag.get('href').split('/') 57 | pre_id = items[-3] + '/' + items[-1] 58 | genre.append(tag.text.strip()) 59 | genre_id.append(pre_id) 60 | # genre的链接中含有censored字段,但是无法用来判断影片是否有码,因为完全不可靠…… 61 | actress = info.xpath("div/span[contains(text(), '女優:')]/following-sibling::*/a/text()") or None 62 | magnet_table = container.xpath("//table[contains(@class, 'magnet-table')]/tbody") 63 | if magnet_table: 64 | magnet_links = magnet_table[0].xpath("tr/td/a/@href") 65 | # 它的FC2数据是从JavDB抓的,JavDB更换图片服务器后它也跟上了,似乎数据更新频率还可以 66 | movie.magnet = [i.replace('[javdb.com]','') for i in magnet_links] 67 | preview_pics = container.xpath("//a[@data-fancybox='gallery']/@href") 68 | 69 | if (not movie.cover) and preview_pics: 70 | movie.cover = preview_pics[0] 71 | movie.url = url 72 | movie.title = title.replace(movie.dvdid, '').strip() 73 | movie.preview_pics = preview_pics 74 | movie.publish_date = publish_date 75 | movie.duration = duration 76 | movie.genre = genre 77 | movie.genre_id = genre_id 78 | movie.actress = actress 79 | 80 | 81 | if __name__ == "__main__": 82 | import pretty_errors 83 | pretty_errors.configure(display_link=True) 84 | logger.root.handlers[1].level = logging.DEBUG 85 | 86 | movie = MovieInfo('IPX-177') 87 | try: 88 | parse_data(movie) 89 | print(movie) 90 | except CrawlerError as e: 91 | logger.error(e, exc_info=1) 92 | -------------------------------------------------------------------------------- /web/prestige.py: -------------------------------------------------------------------------------- 1 | """从蚊香社-prestige抓取数据""" 2 | import os 3 | import re 4 | import sys 5 | import logging 6 | 7 | 8 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 9 | from web.base import * 10 | from web.exceptions import * 11 | from core.datatype import MovieInfo 12 | 13 | 14 | logger = logging.getLogger(__name__) 15 | base_url = 'https://www.prestige-av.com' 16 | # prestige要求访问者携带已通过R18认证的cookies才能够获得完整数据,否则会被重定向到认证页面 17 | # (其他多数网站的R18认证只是在网页上遮了一层,完整数据已经传回,不影响爬虫爬取) 18 | cookies = {'__age_auth__': 'true'} 19 | 20 | 21 | def parse_data(movie: MovieInfo): 22 | """从网页抓取并解析指定番号的数据 23 | Args: 24 | movie (MovieInfo): 要解析的影片信息,解析后的信息直接更新到此变量内 25 | """ 26 | url = f'{base_url}/goods/goods_detail.php?sku={movie.dvdid}' 27 | resp = request_get(url, cookies=cookies, delay_raise=True) 28 | if resp.status_code == 500: 29 | # 500错误表明prestige没有这部影片的数据,不是网络问题,因此不再重试 30 | raise MovieNotFoundError(__name__, movie.dvdid) 31 | resp.raise_for_status() 32 | html = resp2html(resp) 33 | container_tags = html.xpath("//section[@class='px-4 mb-4 md:px-8 md:mb-16']") 34 | if not container_tags: 35 | raise MovieNotFoundError(__name__, movie.dvdid) 36 | 37 | container = container_tags[0] 38 | title = container.xpath("h1/span")[0].tail.strip() 39 | cover = container.xpath("//div[@class='c-ratio-image mr-8']/picture/source/img/@src")[0] 40 | cover = cover.split('?')[0] 41 | actress = container.xpath("//p[text()='出演者:']/following-sibling::div/p/a/text()") 42 | # 移除女优名中的空格,使女优名与其他网站保持一致 43 | actress = [i.strip().replace(' ', '') for i in actress] 44 | duration_str = container.xpath("//p[text()='収録時間:']")[0].getnext().text_content() 45 | match = re.search(r'\d+', duration_str) 46 | if match: 47 | movie.duration = match.group(0) 48 | date_url = container.xpath("//p[text()='発売日:']/following-sibling::div/a/@href")[0] 49 | publish_date = date_url.split('?date=')[-1] 50 | producer = container.xpath("//p[text()='メーカー:']/following-sibling::div/a/text()")[0].strip() 51 | dvdid = container.xpath("//p[text()='品番:']/following-sibling::div/p/text()")[0] 52 | genre_tags = container.xpath("//p[text()='ジャンル:']/following-sibling::div/a") 53 | genre = [tag.text.strip() for tag in genre_tags] 54 | serial = container.xpath("//p[text()='レーベル:']/following-sibling::div/a/text()")[0].strip() 55 | plot = container.xpath("//h2[text()='商品紹介']/following-sibling::p")[0].text.strip() 56 | preview_pics = container.xpath("//h2[text()='サンプル画像']/following-sibling::div/div/picture/source/img/@src") 57 | preview_pics = [i.split('?')[0] for i in preview_pics] 58 | 59 | # prestige改版后已经无法获取高清封面,此前已经获取的高清封面地址也已失效 60 | movie.url = url 61 | movie.dvdid = dvdid 62 | movie.title = title 63 | movie.cover = cover 64 | movie.actress = actress 65 | movie.publish_date = publish_date 66 | movie.producer = producer 67 | movie.genre = genre 68 | movie.serial = serial 69 | movie.plot = plot 70 | movie.preview_pics = preview_pics 71 | movie.uncensored = False # prestige服务器在日本且面向日本国内公开发售,不会包含无码片 72 | 73 | 74 | if __name__ == "__main__": 75 | import pretty_errors 76 | pretty_errors.configure(display_link=True) 77 | logger.root.handlers[1].level = logging.DEBUG 78 | 79 | movie = MovieInfo('ABP-647') 80 | try: 81 | parse_data(movie) 82 | print(movie) 83 | except CrawlerError as e: 84 | logger.error(e, exc_info=1) 85 | -------------------------------------------------------------------------------- /web/proxyfree.py: -------------------------------------------------------------------------------- 1 | """获取各个网站的免代理地址""" 2 | import os 3 | import re 4 | import sys 5 | 6 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 7 | from web.base import * 8 | 9 | 10 | def get_proxy_free_url(site_name: str, prefer_url=None) -> str: 11 | """获取指定网站的免代理地址 12 | Args: 13 | site_name (str): 站点名称 14 | prefer_url (str, optional): 优先测试此url是否可用 15 | Returns: 16 | str: 指定站点的免代理地址(失败时为空字符串) 17 | """ 18 | if prefer_url and is_connectable(prefer_url, timeout=5): 19 | return prefer_url 20 | # 当prefer_url不可用时,尝试自动获取指定网站的免代理地址 21 | site_name = site_name.lower() 22 | func_name = f'_get_{site_name}_urls' 23 | get_funcs = [i for i in dir(sys.modules[__name__]) if i.startswith('_get_')] 24 | if func_name in get_funcs: 25 | get_urls = getattr(sys.modules[__name__], func_name) 26 | try: 27 | urls = get_urls() 28 | return _choose_one(urls) 29 | except: 30 | return '' 31 | else: 32 | raise Exception("Dont't know how to get proxy-free url for " + site_name) 33 | 34 | 35 | def _choose_one(urls) -> str: 36 | for url in urls: 37 | if is_connectable(url, timeout=5): 38 | return url 39 | return '' 40 | 41 | 42 | def _get_avsox_urls() -> list: 43 | html = get_html('https://tellme.pw/avsox') 44 | urls = html.xpath('//h4/strong/a/@href') 45 | return urls 46 | 47 | 48 | def _get_javbus_urls() -> list: 49 | html = get_html('https://www.javbus.one/') 50 | text = html.text_content() 51 | urls = re.findall(r'防屏蔽地址:(https://(?:[\d\w][-\d\w]{1,61}[\d\w]\.){1,2}[a-z]{2,})', text, re.I | re.A) 52 | return urls 53 | 54 | 55 | def _get_javlib_urls() -> list: 56 | html = get_html('https://github.com/javlibcom') 57 | text = html.xpath("//div[@class='p-note user-profile-bio mb-3 js-user-profile-bio f4']")[0].text_content() 58 | match = re.search(r'[\w\.]+', text, re.A) 59 | if match: 60 | domain = f'https://www.{match.group(0)}.com' 61 | return [domain] 62 | 63 | 64 | def _get_javdb_urls() -> list: 65 | html = get_html('https://jav521.app') 66 | js_links = html.xpath("//script[@src]/@src") 67 | for link in js_links: 68 | if '/js/index' in link: 69 | text = get_resp_text(request_get(link)) 70 | match = re.search(r'\$officialUrl\s*=\s*"(https://(?:[\d\w][-\d\w]{1,61}[\d\w]\.){1,2}[a-z]{2,})"', text, flags=re.I | re.A) 71 | if match: 72 | return [match.group(1)] 73 | 74 | 75 | if __name__ == "__main__": 76 | print('javdb:\t', _get_javdb_urls()) 77 | print('javlib:\t', _get_javlib_urls()) 78 | -------------------------------------------------------------------------------- /webui/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8.18-slim 2 | 3 | WORKDIR /app 4 | 5 | RUN apt-get update && apt-get install -y \ 6 | build-essential \ 7 | curl \ 8 | software-properties-common \ 9 | git \ 10 | && rm -rf /var/lib/apt/lists/* 11 | 12 | RUN git clone https://github.com/tetato/JavSP-Docker.git . 13 | RUN pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple -U pip 14 | RUN pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple 15 | RUN pip3 install -r requirements.txt 16 | 17 | VOLUME /video 18 | 19 | EXPOSE 8501 20 | 21 | HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health 22 | 23 | ENTRYPOINT ["streamlit", "run", "webui/scraper_setting.py", "--server.port=8501", "--server.address=0.0.0.0"] 24 | 25 | -------------------------------------------------------------------------------- /webui/config.ini: -------------------------------------------------------------------------------- 1 | [MovieID] 2 | ignore_whole_word = 144P;240P;360P;480P;720P;1080P;2K;4K 3 | ignore_regex = \w+2048\.com;Carib(beancom)?;[^a-z\d](f?hd|lt)[^a-z\d] 4 | 5 | [File] 6 | scan_dir = E:\整理\test 7 | media_ext = 3gp;avi;f4v;flv;iso;m2ts;m4v;mkv;mov;mp4;mpeg;rm;rmvb;ts;vob;webm;wmv 8 | ignore_folder = #recycle;#整理完成;不要扫描 9 | ignore_video_file_less_than = 232 10 | dir_depth = 3 11 | 12 | [Network] 13 | use_proxy = no 14 | proxy = http://127.0.0.1:1080 15 | retry = 3 16 | timeout = 10 17 | 18 | [CrawlerSelect] 19 | normal = airav,avsox,javbus,javdb,javlib,jav321,mgstage,prestige 20 | fc2 = fc2,msin,avsox,javdb,javmenu 21 | cid = fanza 22 | getchu = dl_getchu 23 | gyutto = gyutto 24 | 25 | [Crawler] 26 | required_keys = cover,title 27 | hardworking_mode = yes 28 | respect_site_avid = yes 29 | fc2fan_local_path = 30 | title__remove_actor = yes 31 | title__chinese_first = yes 32 | sleep_after_scraping = 1 33 | 34 | [ProxyFree] 35 | avsox = https://avsox.click 36 | javdb = https://javdb523.com 37 | javbus = https://www.busdmm.shop 38 | javlib = https://www.i71t.com 39 | 40 | [NamingRule] 41 | media_servers = jellyfin 42 | output_folder = E:\整理\final 43 | save_dir = $actress/[$num] $title 44 | save_type = hardlink 45 | filename = $num 46 | max_path_len = 250 47 | calc_path_len_by_byte = auto 48 | max_actress_count = 10 49 | nfo_title = $num $title 50 | text_for_censored = 有码 51 | text_for_uncensored = 无码 52 | text_for_unknown_censorship = 打码情况未知 53 | null_for_title = #未知标题 54 | null_for_actress = #未知女优 55 | null_for_serial = #未知系列 56 | null_for_director = #未知导演 57 | null_for_producer = #未知制作商 58 | null_for_publisher = #未知发行商 59 | 60 | [Picture] 61 | use_big_cover = yes 62 | use_ai_crop = no 63 | use_ai_crop_labels = no 64 | ai_engine = 65 | aip_appid = 66 | aip_api_key = 67 | aip_secret_key = 68 | 69 | [Translate] 70 | engine = 71 | translate_title = no 72 | translate_plot = no 73 | baidu_appid = 74 | baidu_key = 75 | bing_key = 76 | 77 | [NFO] 78 | add_genre_to_tag = no 79 | 80 | [Other] 81 | check_update = yes 82 | auto_update = yes 83 | 84 | [OptionAttribute] 85 | ignore_whole_word = 忽略关键字,text 86 | ignore_regex = 忽略规则,text 87 | scan_dir = 扫描目录,text 88 | media_ext = 视频格式,text 89 | ignore_folder = 忽略目录,text 90 | ignore_video_file_less_than = 视频最小Mib,num 91 | dir_depth = 可选目录深度,num 92 | use_proxy = 启用代理,box,,False 93 | proxy = 代理地址,text 94 | retry = 重试次数,num 95 | timeout = 超时时间,num 96 | normal = 默认,choices,airav/avsox/javbus/javdb/javlib/jav321/mgstage/prestige 97 | fc2 = FC2,choices,fc2/msin/avsox/javdb/javmenu 98 | cid = cid,choice,fanza/ 99 | getchu = getchu,choice,dl_getchu/ 100 | gyutto = gyutto,choice,gyutto/ 101 | required_keys = 基础字段,choices,cover/title 102 | hardworking_mode = 检索更多信息,box,,True 103 | respect_site_avid = 使用网站番号,box,,True 104 | fc2fan_local_path = FC2地址,text 105 | title__remove_actor = 演员除题,box,,True 106 | title__chinese_first = 中文标题,box,,True 107 | sleep_after_scraping = 等待时间(秒,0为禁用),num 108 | avsox = avsox,text 109 | javdb = javdb,text 110 | javbus = javbus,text 111 | javlib = javlib,text 112 | media_servers = 媒体服务器,choice,jellyfin/universal/video_station 113 | output_folder = 保存目录,text 114 | save_dir = 保存文件夹名 ,text 115 | save_type = 保存方式,choice,hardlink/copy/move 116 | filename = 命名规则,text 117 | max_path_len = 最长文件路径,num 118 | calc_path_len_by_byte = 长度计算方式,choice,auto/yes/no 119 | max_actress_count = 采集演员数,num 120 | nfo_title = 显示标题,text 121 | text_for_censored = 有码文件夹名,text 122 | text_for_uncensored = 无码文件夹名,text 123 | text_for_unknown_censorship = 未知文件夹名,text 124 | null_for_title = 未知标题,text 125 | null_for_actress = 未知演员,text 126 | null_for_serial = 未知系列,text 127 | null_for_director = 未知导演,text 128 | null_for_producer = 未知制作商,text 129 | null_for_publisher = 未知发行商,text 130 | use_big_cover = 高清封面,box,,True 131 | use_ai_crop = 裁剪海报,box,,False 132 | use_ai_crop_labels = 番号识图,box,,False 133 | ai_engine = 识图引擎,text 134 | aip_appid = 识图appid,text 135 | aip_api_key = 识图appkey,text 136 | aip_secret_key = 识图secret,text 137 | engine = 翻译引擎,text 138 | translate_title = 翻译标题,box,,False 139 | translate_plot = 翻译简介,box,,False 140 | baidu_appid = 百度appid,text 141 | baidu_key = 百度key,text 142 | bing_key = 必应key,text 143 | add_genre_to_tag = genre写入tag,box 144 | check_update = 检查更新,box,,True 145 | auto_update = 自动更新,box,,True 146 | 147 | -------------------------------------------------------------------------------- /webui/docker.yml: -------------------------------------------------------------------------------- 1 | version: "2.1" 2 | services: 3 | javsp: 4 | image: javsp_image:latest 5 | container_name: javsp 6 | environment: 7 | - PUID=${PUID} # 填写自己要指定的,不指定可以删除 8 | - PGID=${PGID} # 填写自己要指定的,不指定可以删除 9 | - TZ=${TZ} # 填写自己要指定的,不指定可以删除 10 | volumes: 11 | - ${media_disk}/pool/video:/video # 映射存放影片的目录 12 | ports: 13 | - 8501:8501 14 | restart: always 15 | --------------------------------------------------------------------------------