Compare commits

...

26 Commits

Author SHA1 Message Date
lzyme.dev@gmail.com 9d81f6a13a 美化资讯页面 2026-10-14 12:43:19 +08:00
lzyme.dev@gmail.com 0425ebf7c7 修复top250 2026-10-14 12:09:53 +08:00
lzyme.dev@gmail.com d636a2f683 修复鉴定记录 2026-10-12 16:32:49 +08:00
lzyme.dev@gmail.com 171ee3b9d7 修复排行榜 2026-10-12 01:30:17 +08:00
lzyme.dev@gmail.com 9d322cc76d 修复鉴定记录 2026-10-12 00:11:40 +08:00
lzyme.dev@gmail.com 89ab8bcff8 修复瀑布流加载出现错误 2026-10-11 23:49:51 +08:00
lzyme.dev@gmail.com fde1eadac9 修复高亮演员 2026-10-11 23:45:45 +08:00
lzyme.dev@gmail.com b81922103a 支持作品自定义保存目录 2026-10-11 23:12:30 +08:00
lzyme.dev@gmail.com e74f2cbdc8 修复磁力搜索获取失败刷新参数丢失 2026-09-10 16:32:00 +08:00
lzyme.dev@gmail.com 8396e3825d UI调整 2026-09-09 14:58:50 +08:00
lzyme.dev@gmail.com e4792cd55f 修复javbus离线下载按钮风格 2026-09-09 14:39:40 +08:00
lzyme.dev@gmail.com 72ec9a0e83 修复javbus下显示异常 2026-09-05 10:19:09 +08:00
lzyme.dev@gmail.com 4339f1b80f update 2026-09-03 23:45:08 +08:00
lzyme.dev@gmail.com 20d199f53a bump version 2026-09-03 21:46:01 +08:00
lzyme.dev@gmail.com a9e88cda88 移除javxy的接口依赖 2026-09-03 21:45:01 +08:00
lzyme.dev@gmail.com 032dcf1fdc 移除javxy的接口依赖 2026-09-03 19:23:21 +08:00
lzyme.dev@gmail.com 38221bc78d update 2026-09-02 23:26:37 +08:00
lzyme.dev@gmail.com 4230176ce4 修复github action发布版本号问题 2026-09-01 18:54:13 +08:00
lzyme.dev@gmail.com b5bdec2c35 更新文档 2026-09-01 15:28:19 +08:00
lzyme.dev@gmail.com 11c5650efb 新增下载高清封面 2026-09-01 15:24:40 +08:00
lzyme.dev@gmail.com 080cbb00ee 新增下载高清封面 2026-09-01 15:18:34 +08:00
lzyme.dev@gmail.com d5c59ba6ac 新增磁力列表验车按钮 2026-09-01 14:24:54 +08:00
lzyme.dev@gmail.com 6a12b45f86 Merge remote-tracking branch 'origin/main' 2026-09-01 00:53:11 +08:00
lzyme.dev@gmail.com 1e3c4d9718 更新文档 2026-09-01 00:50:56 +08:00
FireInRain 2b8c925fed Merge pull request #2 from anupamme/fix-repo-jhs-enhance-remove-hardcoded-imgur-client-id
fix: the userscript embeds imgur api credentials dir... in...
2026-08-31 21:42:16 +08:00
anupamme 7005b528a3 fix: V-001 security vulnerability
Automated security fix generated by OrbisAI Security
2026-08-31 06:14:02 +00:00
28 changed files with 27967 additions and 902 deletions
+6 -2
View File
@@ -27,11 +27,15 @@ jobs:
- name: Build
run: npm run build
- name: Get version
id: version
run: echo "VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
- name: Upload to Release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ github.run_number }}
name: Release v${{ github.run_number }}
tag_name: v${{ steps.version.outputs.VERSION }}
name: Release v${{ steps.version.outputs.VERSION }}
files: dist/jhs-enhance.user.js
draft: false
prerelease: false
+7 -6
View File
@@ -42,11 +42,12 @@
- **磁力排序与过滤(JavDB)**:按发布日期/文件大小/文件数量拖拽排序,勾选启用;高清/4K/字幕/无码多条件并集过滤,4K 磁力行高亮显示
- 磁力链接高亮匹配已收藏/已下载状态
- DMM 多画质预览视频
- **多源视频预览**:Javxy 聚合 API 独立按钮(番号/识别码右侧),多源 fallback(JavTrailers / JavDB 等),HLS 支持、播放进度记忆、画质切换
- **多源视频预览**:DMMRSC 聚合 API 独立按钮(番号/识别码右侧),HLS 支持、播放进度记忆、画质切换
- **标题翻译**:Google 翻译,支持开关多次切换不失效
- **字幕搜索**:合并迅雷 + SubTitleCat 为单一入口,支持 115 网盘直传、详情页预匹配目录、多目录选择上传、长目录名截断展示、字幕预览绿色字体
- **多源预览图**:javfree / projectjav / javstore 三源切换,来源按钮绿色高亮,sessionStorage 缓存,错误提示弹窗内显示
- 演员信息展示、相关清单、评论查看
- **下载高清封面**:获取 DMM 高清封面直链并下载,支持番号格式校验(排除 FC2/无码作品)
- 以图识图、第三方站点跳转
### 115 网盘集成
@@ -109,7 +110,7 @@ JHS-enhance/
│ │ ├── javdb.js # JavDB API 封装(签名算法、搜索、评论、磁力列表)
│ │ ├── dmm.js # DMM 预览视频获取(多画质、默认画质选择)
│ │ ├── dmm-graphql.js # DMM GraphQL API(备选视频源回退)
│ │ └── javxy.js # Javxy 聚合视频 API(多源解析、签名生成)
│ │ └── dmmrsc.js # DMMRSC API(视频多源解析、高清封面获取)
│ ├── lib/ # 第三方补丁库
│ │ ├── gm-xhr-parallel.js # Tampermonkey MV3 并行请求修复(redirect: manual)
│ │ └── hls-runtime.js # HLS 视频播放运行时(hls.js 封装、进度记忆、画质切换)
@@ -117,7 +118,7 @@ JHS-enhance/
│ ├── list-page.js # 列表页过滤与状态标签渲染
│ ├── list-page-button.js # 列表页功能按钮(待鉴定/已收藏/新作品/黑名单管理)
│ ├── detail-page.js # 详情页数据加载与基础增强
│ ├── detail-page-button.js# 详情页操作按钮(115 目录预加载 + 磁力排序过滤面板)
│ ├── detail-page-button.js# 详情页操作按钮(下载高清封面 + 115 目录预加载 + 磁力排序过滤面板)
│ ├── setting.js # 设置面板(Tab 页:基础/任务/域名/快捷键/屏蔽/缓存/标记删除/打赏作者;含 115 自动重命名开关 + 范围选择)
│ ├── translate.js # 标题翻译(Google 翻译 API,原始标题缓存)
│ ├── preview-video.js # DMM 预览视频播放器
@@ -152,7 +153,7 @@ JHS-enhance/
│ ├── magnet-hub.js # MagnetHub 磁力搜索(6 引擎聚合、质量标签、验车截图、115 离线)
│ ├── other-site.js # 第三方站点跳转链接
│ ├── jav-trailers.js # JavTrailers 站点处理
│ ├── javxy-preview-video.js# Javxy 多源视频预览(独立按钮、聚合 API、HLS 支持)
│ ├── javxy-preview-video.js# DMMRSC 多源视频预览(独立按钮、聚合 API、HLS 支持)
│ ├── subtitle-cat.js # SubTitleCat 字幕搜索(已整合,保留原文件)
│ ├── filter-title-keyword.js # 标题关键词过滤
│ └── want-watched.js # 想看/已看视频列表
@@ -317,11 +318,11 @@ window.show.ok('操作成功'); // Toast 通知
- 优化:`searchFiles` 增加 `type:4` 参数,搜索仅返回视频文件,避免目录占用 30 条槽位
- **v3.3.8**
- 新增:Javxy 多源视频预览独立按钮(番号/识别码右侧),基于 Javxy 聚合 API,支持 JavTrailers / JavDB 等多源回退
- 新增:DMMRSC 多源视频预览独立按钮(番号/识别码右侧),基于 DMMRSC 聚合 API
- 新增:HLS 视频播放运行时(hls.js 封装),支持 .m3u8 格式、播放进度记忆、画质切换
- 新增:DMM GraphQL API 模块,作为备选视频源回退方案
- 优化:`bus-preview-video.js` 和 `preview-video.js` 清理移植代码,保持原有播放逻辑不变
- 优化:`dmm.js` 回退至仅 DMM Affiliate API + GraphQL API 双通道,移除 Javxy 耦合
- 优化:`dmm.js` 回退至仅 DMM Affiliate API + GraphQL API 双通道
- **v3.3.7**
- 新增:JavDB 磁力排序与过滤(拖拽排序、多条件并集过滤、4K 高亮)
-251
View File
@@ -1,251 +0,0 @@
好的,让我先全面分析这个 11000 行的脚本。我会分段读取,理解它的整体结构和功能模块。
The user wants me to analyze the JHS-origin.js file (about 11,000 lines) and provide a detailed report on how to modularize it. Let me first read through the entire file to understand its structure, then provide a comprehensive refactoring plan.
Since the file is 10,962 lines, I need to read it in chunks. Let me start by reading several sections to understand the code structure.
Let me read the file in multiple chunks to understand:
1. The beginning (already read some)
2. Key sections throughout
3. The end
I'll read multiple sections in parallel.
Let me continue reading more sections to get a complete picture of the codebase.
Let me also read the end of the file to see the initialization/entry point.
Now I have a very comprehensive understanding of the codebase. Let me also read the very end to see the initialization.
Now I have a comprehensive understanding of the entire codebase. Let me write a detailed analysis report.
The code is approximately 10,962 lines, with a well-defined plugin architecture:
- PluginManager: manages plugins
- BasePlugin: base class for all plugins
- ~40 plugins extending BasePlugin
The code also contains:
- Utility classes (Utils, gmHttp, StorageManager, show, clog, etc.)
- CSS styles
- Constants and configuration
- API interfaces (JavDbApi, etc.)
- A plugin system with registration and execution
Let me now write a comprehensive refactoring report.
现在我已经完整分析了整个代码库。以下是详细的结构分析报告:
---
## 🔍 JHS-enhance 代码结构分析报告
### 📊 总体概况
| 指标 | 数据 |
|------|------|
| 总行数 | **10,962 行** |
| 核心类/插件 | **40+ 个** |
| 支持站点 | JavDB、JavBus、JavTrailers、SubTitleCat、阿里云盘、115网盘 |
| 外部依赖 | jQuery、Tabulator、layui-layer、MD5、Toastify、localForage、ViewerJS、QRCodeJS |
---
### 🏗️ 现有架构
代码已经有一个**不错的插件架构**,只是全部写在一个文件里:
```
入口(最后100行)
├── 初始化 pluginManager
├── 检测当前页面类型(isDetailPage / isListPage / isFc2Page)
├── 按站点注册插件
├── pluginManager.processCss() → 加载所有插件CSS
└── pluginManager.processPlugins() → 执行所有插件
```
插件基类 `BasePlugin` 提供了:
- `getName()` — 插件名
- `initCss()` — 返回CSS字符串
- `handle()` — 主逻辑入口
- `getPageInfo()` — 获取当前页面信息
- `getBean(name)` — 获取其他插件实例
---
### 📦 代码功能模块划分
我将 11000 行代码按功能分为以下 **8 大模块**,建议拆分为独立文件:
---
#### 模块一:🛠️ 基础设施层(~1500 行)
**建议目录:`src/core/`**
| 文件 | 内容 | 说明 |
|------|------|------|
| `utils.js` | `Utils` 类(~300行) | 通用工具函数:下载、滚动、剪贴板、URL解析、排序等 |
| `gmHttp.js` | `gmHttp` 类(~200行) | 基于 `GM_xmlhttpRequest` 的 HTTP 封装 |
| `storage.js` | `StorageManager` 类(~500行) | IndexedDB 存储管理:收藏、屏蔽、设置、演员等 |
| `show.js` | `show` / `clog` 对象(~100行) | Toast 消息提示、控制台日志 |
| `constants.js` | 所有常量(~200行) | 状态枚举、画质选项、翻译映射表、标签样式等 |
| `styles.js` | CSS 样式(~300行) | JavDB/JavBus 站点样式、全局样式、滚动条样式 |
---
#### 模块二:🔌 插件系统(~200 行)
**建议目录:`src/core/`**
| 文件 | 内容 | 说明 |
|------|------|------|
| `plugin-manager.js` | `PluginManager` 类 | 插件注册、CSS加载、插件执行 |
| `base-plugin.js` | `BasePlugin` 类 | 插件基类,提供 `getName()`、`handle()`、`initCss()` 等 |
---
#### 模块三:🌐 API 接口层(~200 行)
**建议目录:`src/api/`**
| 文件 | 内容 | 说明 |
|------|------|------|
| `javdb-api.js` | `javDbApi` 对象 | JavDB API:获取评论、搜索电影、电影详情、磁力链接 |
---
#### 模块四:📋 列表页插件(~2500 行)
**建议目录:`src/plugins/list/`**
| 文件 | 类名 | 功能 |
|------|------|------|
| `list-page.js` | `ListPagePlugin` | 列表页核心:状态标签渲染、屏蔽过滤、演员屏蔽、排序 |
| `list-page-button.js` | `ListPageButtonPlugin` | 列表页工具栏按钮:一键屏蔽、收藏、随机打开等 |
| `auto-page.js` | `AutoPagePlugin` | 自动翻页 |
| `fold-category.js` | `FoldCategoryPlugin` | 折叠/展开分类标签区域 |
| `cover-button.js` | `CoverButtonPlugin` | 封面图上的操作按钮(屏蔽、收藏等) |
| `history.js` | `HistoryPlugin` | 鉴定记录面板 |
| `filter-title-keyword.js` | `FilterTitleKeywordPlugin` | 标题关键词屏蔽 |
| `blacklist.js` | `BlacklistPlugin` | 黑名单管理(屏蔽演员/标签) |
| `hit-show.js` | `HitShowPlugin` | 热播榜 |
| `top250.js` | `TOP250Plugin` | TOP250 排行榜 |
---
#### 模块五:🎬 详情页插件(~2000 行)
**建议目录:`src/plugins/detail/`**
| 文件 | 类名 | 功能 |
|------|------|------|
| `detail-page.js` | `DetailPagePlugin` | 详情页核心:FC2/普通影片信息展示 |
| `detail-page-button.js` | `DetailPageButtonPlugin` | 详情页按钮:屏蔽、收藏、已下载、已观看、磁力搜索、字幕 |
| `bus-detail-page.js` | `BusDetailPagePlugin` | JavBus 详情页 |
| `review.js` | `ReviewPlugin` | 评论查看 |
| `related.js` | `RelatedPlugin` | 相关清单 |
| `highlight-magnet.js` | `HighlightMagnetPlugin` | 磁力链接高亮过滤 |
| `translate.js` | `TranslatePlugin` | 标题翻译(日→中) |
| `want-watched.js` | `WantAndWatchedVideosPlugin` | 想看/已看视频 |
---
#### 模块六:🎥 视频与媒体插件(~1500 行)
**建议目录:`src/plugins/media/`**
| 文件 | 类名 | 功能 |
|------|------|------|
| `preview-video.js` | `PreviewVideoPlugin` | JavDB 预览视频播放 |
| `bus-preview-video.js` | `BusPreviewVideoPlugin` | JavBus 预览视频播放 |
| `screen-shot.js` | `ScreenShotPlugin` | 缩略图加载 |
| `bus-img.js` | `BusImgPlugin` | JavBus 图片处理 |
| `image-recognition.js` | `ImageRecognitionPlugin` | 以图识图 |
---
#### 模块七:🔗 外部站点与工具插件(~2000 行)
**建议目录:`src/plugins/external/`**
| 文件 | 类名 | 功能 |
|------|------|------|
| `magnet-hub.js` | `MagnetHubPlugin` | 多站点磁力搜索聚合 |
| `other-site.js` | `OtherSitePlugin` | 其他站点入口(JavStore、MissAV等) |
| `fc2.js` | `Fc2Plugin` | FC2 影片页面 |
| `fc2-123av.js` | `Fc2By123AvPlugin` | 123AV 的 FC2 页面 |
| `wangpan-115.js` | `WangPan115Plugin` / `WangPan115TaskPlugin` / `WangPan115MatchPlugin` | 115网盘离线下载 |
| `aliyun-pan.js` | `AliyunPanPlugin` | 阿里云盘 |
| `jav-trailers.js` | `JavTrailersPlugin` | JavTrailers 站点 |
| `subtitle-cat.js` | `SubTitleCatPlugin` | SubTitleCat 字幕搜索 |
---
#### 模块八:👤 演员与任务插件(~2000 行)
**建议目录:`src/plugins/actress/`**
| 文件 | 类名 | 功能 |
|------|------|------|
| `actress-info.js` | `ActressInfoPlugin` | 演员信息面板 |
| `favorite-actresses.js` | `FavoriteActressesPlugin` | 收藏演员管理 |
| `new-video.js` | `NewVideoPlugin` | 新作品检测 |
| `task.js` | `TaskPlugin` | 定时任务:检测黑名单、同步演员、新作品检测 |
| `nav-bar.js` | `NavBarPlugin` / `BusNavBarPlugin` | 导航栏增强 |
| `setting.js` | `SettingPlugin` | 设置面板 |
---
### 🎯 建议的项目目录结构
```
JHS-enhance/
├── src/
│ ├── main.js # 入口:初始化 + 插件注册
│ ├── core/
│ │ ├── utils.js # 通用工具函数
│ │ ├── gmHttp.js # GM_xmlhttpRequest 封装
│ │ ├── storage.js # IndexedDB 存储管理
│ │ ├── show.js # Toast 消息提示
│ │ ├── constants.js # 所有常量
│ │ ├── styles.js # CSS 样式
│ │ ├── plugin-manager.js # 插件管理器
│ │ └── base-plugin.js # 插件基类
│ ├── api/
│ │ └── javdb-api.js # JavDB API 接口
│ ├── plugins/
│ │ ├── list/ # 列表页插件 (10个)
│ │ ├── detail/ # 详情页插件 (8个)
│ │ ├── media/ # 视频/媒体插件 (5个)
│ │ ├── external/ # 外部站点插件 (8个)
│ │ └── actress/ # 演员/任务插件 (6个)
│ └── styles/
│ └── ... (可选,如果CSS拆出来)
├── vite.config.mjs
├── package.json
└── JHS-origin.js # 保留原始文件作为参考
```
---
### ⚠️ 拆分时的注意事项
1. **全局变量依赖**:当前代码大量使用 `unsafeWindow.xxx = window.xxx`,拆分为 ES Module 后需要改为 `export`/`import`,同时通过 `window` 暴露给油猴全局作用域
2. **插件间依赖**:插件通过 `this.getBean(name)` 互相调用,拆分后需确保 `PluginManager` 先初始化
3. **CDN 外部依赖**:jQuery、Tabulator 等通过 `@require` 加载,在模块中直接使用 `$`、`Tabulator` 等全局变量即可
4. **CSS 注入**:`insertStyle()` 和 `initCss()` 需要 `document.head` 可用后才能执行
5. **循环依赖**:注意 `TaskPlugin` ↔ `BlacklistPlugin`、`ListPagePlugin` ↔ `BlacklistPlugin` 等之间的双向引用
---
### 🚀 建议的实施顺序
| 优先级 | 步骤 | 预计工作量 |
|--------|------|-----------|
| P0 | 拆分 `core/` 基础设施(utils, gmHttp, storage, show, constants, styles) | 1-2小时 |
| P1 | 拆分插件系统(plugin-manager, base-plugin)和入口 `main.js` | 30分钟 |
| P2 | 拆分 API 层(javdb-api) | 15分钟 |
| P3 | 分批拆分插件,每批 3-5 个,逐个验证构建 | 3-5小时 |
| P4 | 清理全局变量,统一为 ES Module 导入导出 | 1小时 |
---
需要我开始执行拆分吗?建议从 **P0(基础设施层)** 开始,这是最独立、最不容易出错的模块。
+25037 -543
View File
File diff suppressed because it is too large Load Diff
+993
View File
@@ -0,0 +1,993 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>NIMA-086 実写版!ムラムラしたので通勤中にお持ち帰りされてみた 彩月七緒 - JavBus</title>
<meta name="keywords" content="NIMA-086,肉漫,潮吹,中出,高畫質,原作コラボ,巨乳,アクメ・オーガズム,單體作品,DMM獨家">
<meta name="description"
content="【發行日期】2026-09-25,【長度】120分鐘,(NIMA-086)実写版!ムラムラしたので通勤中にお持ち帰りされてみた 彩月七緒">
<link rel="alternate" href="https://www.javbus.com/en/NIMA-086" hreflang="en"/>
<link rel="alternate" href="https://www.javbus.com/ja/NIMA-086" hreflang="ja"/>
<link rel="alternate" href="https://www.javbus.com/ko/NIMA-086" hreflang="ko"/>
<link rel="alternate" href="https://www.javbus.com/NIMA-086" hreflang="zh"/>
<link rel="alternate" href="https://www.javbus.com/NIMA-086" hreflang="x-default"/>
<link rel="canonical" href="https://www.javbus.com/NIMA-086"/>
<link rel="preconnect" href="https://poweredby.jads.co" crossorigin>
<link rel="dns-prefetch" href="https://poweredby.jads.co">
<link rel="stylesheet" href="https://www.javbus.com/css/bootstrap.min.css">
<link rel="stylesheet" href="https://www.javbus.com/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="https://www.javbus.com/css/css-slider.css?v=9.3">
<link rel="stylesheet" href="https://www.javbus.com/css/nav.overlay.css?v=3.9.8">
<link rel="stylesheet" href="https://www.javbus.com/css/magnific-popup.css">
<link rel="stylesheet" href="https://www.javbus.com/css/base.css?v=7.15">
<script src="https://www.javbus.com/js/jquery.min.js"></script>
<script src="https://www.javbus.com/js/jquery.magnific-popup.min.js"></script>
<script src="https://www.javbus.com/js/bootstrap.min.js" defer></script>
<script src="https://www.javbus.com/js/jquery.cookie.min.js" defer></script>
<script src="https://www.javbus.com/js/base.js" defer></script>
<script src="https://www.javbus.com/js/bootstrap-hover-dropdown.js" defer></script>
<script src="https://www.javbus.com/js/nav.overlay.js?v=10.30.3" defer></script>
<!--[if lt IE 9]>
<script src="https://www.javbus.com/js/html5shiv.min.js"></script>
<script src="https://www.javbus.com/js/respond.min.js"></script><![endif]-->
</head>
<body>
<script language="JavaScript">
var mod = 0;
var lang = 'zh';
var info = '搜尋 識別碼, 影片, 演員';
function searchs(obj) {
var searchinput = $("#" + obj);
if (searchinput.val() == '') {
$('#magnet-url-post').trigger("click");
return false;
} else {
//$('#search-loading').show();str.replace(/ /g, "-")
window.open("https://www.javbus.com/search/" + encodeURIComponent($.trim(searchinput.val()).replace(/ /g, "+")) + "&type=&parent=ce");
}
}
$(function () {
var url = 'https://www.javbus.com/ajax/search-modal.php?floor=' + Math.floor(Math.random() * 1000 + 1) + '&lang=' + lang;
$.ajax({
url: url, type: 'GET', success: function (msg) {
$("#searchModal").append(msg);
}
});
});
</script>
<div id="search-loading">
<table class="search-loading-box" border="0" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td align="center">
<table height="80" width="100%" border="0" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td height="40" align="center">
<div class="search-loading-text">搜尋中...</div>
</td>
</tr>
<tr>
<td height="40" align="center">
<img src="https://www.javbus.com/images/search_loading.gif" border="0">
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
<!-- Modal Search -->
<div id="searchModal" class="modal fade" tabindex="-1" role="dialog"></div>
<nav class="navbar navbar-default navbar-fixed-top top-bar" style="z-index:900">
<div class="container-fluid">
<div class="navbar-header mh50">
<a href="https://www.javbus.com/">
<img class="hidden-xs" height="50" alt="JavBus" src="https://www.javbus.com/images/logo.png"
style="height:40px; margin-top:5px;">
<img class="visible-xs-inline" height="50" alt="JavBus" src="https://www.javbus.com/images/logo.png">
</a>
<div class="btn-group pull-right visible-xs-inline" role="group" style="margin:8px 8px 0 0;"
onclick="window.location.href= '/forum/member.php?mod=logging&action=login&referer=%2F%2Fwww.javbus.com%2FNIMA-086'">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"
aria-expanded="false">
<img class="avatar" style="width:22px; height:22px;"
src="https://uc.javbus22.com/uc/avatar.php?uid=0&size=small"></button>
</div>
<div class="btn-group pull-right visible-xs-inline" role="group" style="margin:8px 8px 0 0;">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"
aria-expanded="false">
<span class="glyphicon glyphicon-globe"></span> <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li><a href="https://www.javbus.com/en/NIMA-086">English</a></li>
<li><a href="https://www.javbus.com/ja/NIMA-086">日本语</a></li>
<li><a href="https://www.javbus.com/ko/NIMA-086">한국의</a></li>
<li><a href="https://www.javbus.com/NIMA-086">中文</a></li>
</ul>
</div>
</div>
<div id="navbar" class="collapse navbar-collapse">
<div class="navbar-form navbar-left fullsearch-form">
<div class="input-group">
<input id="search-input" type="text" class="form-control" placeholder="搜尋 識別碼, 影片, 演員">
<span class="input-group-btn">
<button class="btn btn-default" type="submit" onClick="searchs('search-input')">搜尋</button>
</span>
</div>
</div>
<ul class="nav navbar-nav">
<li class="active"><a href="https://www.javbus.com/">有碼</a></li>
<li><a href="https://www.javbus.com/uncensored">無碼</a></li>
<li class="hidden-md hidden-sm"><!-- <div class="icon-new"></div> --><a href="https://www.javbus.hair/">歐美</a>
</li>
<li class="hidden-sm"><a href="https://www.javbus.com/forum/">論壇</a></li>
<li class="dropdown hidden-sm ">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" data-hover="dropdown" role="button"
aria-expanded="false">類別 <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="https://www.javbus.com/genre">有碼類別</a></li>
<li><a href="https://www.javbus.com/uncensored/genre">無碼類別</a></li>
</ul>
</li>
<li class="dropdown hidden-sm ">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" data-hover="dropdown" role="button"
aria-expanded="false">女優 <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="https://www.javbus.com/actresses">有碼女優</a></li>
<li><a href="https://www.javbus.com/uncensored/actresses">無碼女優</a></li>
</ul>
</li>
<li class="dropdown"><a href="https://www.javbus.com/" class="dropdown-toggle" data-toggle="dropdown"
data-hover="dropdown" role="button" aria-expanded="false"><span
class="glyphicon glyphicon-menu-hamburger"></span></a>
<ul class="dropdown-menu" role="menu">
<li class="visible-md-block visible-sm-block"><a href="https://www.javbus.hair/">歐美</a></li>
<li class="visible-sm-block"><a href="https://www.javbus.com/forum/">論壇</a></li>
<li class="visible-sm-block"><a href="https://www.javbus.com/genre">有碼類別</a></li>
<li class="visible-sm-block"><a href="https://www.javbus.com/uncensored/genre">無碼類別</a></li>
<li class="visible-sm-block"><a href="https://www.javbus.com/actresses">有碼女優</a></li>
<li class="visible-sm-block"><a href="https://www.javbus.com/uncensored/actresses">無碼女優</a>
</li>
<li><a href="https://www.javbus.com/genre/hd">高清</a></li>
<li><a href="https://www.javbus.com/genre/sub">字幕</a></li>
</ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-right"
onclick="window.location.href= '/forum/member.php?mod=logging&action=login&referer=%2F%2Fwww.javbus.com%2FNIMA-086'">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" data-hover="dropdown" role="button"
aria-expanded="false"><img class="avatar"
src="https://uc.javbus22.com/uc/avatar.php?uid=0&size=small"><span
class="hidden-md hidden-sm ml5">登入</span> </a>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" data-hover="dropdown" role="button"
aria-expanded="false"><span class="glyphicon glyphicon-globe" style="font-size:12px;"></span>
<span class="hidden-md hidden-sm">English</span> <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="https://www.javbus.com/en/NIMA-086">English</a></li>
<li><a href="https://www.javbus.com/ja/NIMA-086">日本语</a></li>
<li><a href="https://www.javbus.com/ko/NIMA-086">한국의</a></li>
<li><a href="https://www.javbus.com/NIMA-086">中文</a></li>
</ul>
</li>
</ul>
</div>
<!--/.nav-collapse -->
</div>
</nav>
<div class="row visible-xs-inline footer-bar">
<div class="col-xs-3 text-center">
<a id="menu" class="btn btn-default trigger-overlay"><span class="glyphicon glyphicon-align-justify"></span></a>
</div>
<div class="col-xs-3 text-center">
</div>
<div class="col-xs-3 text-center">
</div>
<div class="col-xs-3 text-center">
<a id="back" class="btn btn-default" href="javascript:window.history.back()"><span
class="glyphicon glyphicon-share-alt flipx"></span></a>
</div>
</div>
<script src='https://www.javbus.com/js/focus.js?v=8.7'></script>
<link rel='stylesheet' type='text/css' href='https://www.javbus.com/css/movie.css?v=2.8'>
<link rel='stylesheet' type='text/css' href='https://www.javbus.com/css/movie-box.css'>
<script>
var gid = 70147716068;
var uc = 0;
var img = '/pics/cover/ckd7_b.jpg';
</script>
<div class="ad-box all-top">
<div class="ad-item">
<div class="ad-juicy">
<script type="text/javascript" data-cfasync="false" async
src="https://poweredby.jads.co/js/jads.js"></script>
<ins id="1024549" data-width="728" data-height="90"></ins>
<script type="text/javascript" data-cfasync="false"
async>(adsbyjuicy = window.adsbyjuicy || []).push({"adzone": 1024549});</script>
</div>
</div>
<div class="ad-item">
<div class="ad-juicy">
<script type="text/javascript" data-cfasync="false" async
src="https://poweredby.jads.co/js/jads.js"></script>
<ins id="1024550" data-width="728" data-height="90"></ins>
<script type="text/javascript" data-cfasync="false"
async>(adsbyjuicy = window.adsbyjuicy || []).push({"adzone": 1024550});</script>
</div>
</div>
<div id="ky" class="ad-item">
<div class='csslider1 autoplay'>
<input name="cs_anchor1" id='cs_play1' type="radio" class='cs_anchor' checked>
<ul>
<li class="cs_skeleton"><img src="/forum/data/attachment/forum/ads/ky_20260923_3.jpg"
style="width: 100%;"></li>
<li class="num0 img slide"><a href="https://www.3jnbdg.vip:9553/?i_code=14698475" target="_blank"><img
src="/forum/data/attachment/forum/ads/ky_20260923_3.jpg"/></a></li>
<li class="num1 img slide"><a href="https://www.3jnbdg.vip:9553/?i_code=14698475" target="_blank"><img
src="/forum/data/attachment/forum/ads/ky_20260923_5.jpg"/></a></li>
<li class="num2 img slide"><a href="https://www.3jnbdg.vip:9553/?i_code=14698475" target="_blank"><img
src="/forum/data/attachment/forum/ads/ky_20260923_1.jpg"/></a></li>
<li class="num3 img slide"><a href="https://www.3jnbdg.vip:9553/?i_code=14698475" target="_blank"><img
src="/forum/data/attachment/forum/ads/ky_20260923_2.jpg"/></a></li>
<li class="num4 img slide"><a href="https://www.3jnbdg.vip:9553/?i_code=14698475" target="_blank"><img
src="/forum/data/attachment/forum/ads/ky_20260923_4.jpg"/></a></li>
</ul>
</div>
</div>
<div id="kok" class="ad-item">
<div class='csslider1 autoplay'>
<input name="cs_anchor2" id='cs_play2' type="radio" class='cs_anchor' checked>
<ul>
<li class="cs_skeleton"><img src="/forum/data/attachment/forum/ads/zo_20260923_2.jpg"
style="width: 100%;"></li>
<li class="num0 img slide"><a href="https://120406.zoty2508.com:30501/?i_code=3354655"
target="_blank"><img
src="/forum/data/attachment/forum/ads/zo_20260923_2.jpg"/></a></li>
<li class="num1 img slide"><a href="https://120406.zoty2508.com:30501/?i_code=3354655"
target="_blank"><img
src="/forum/data/attachment/forum/ads/zo_20260923_1.jpg"/></a></li>
<li class="num2 img slide"><a href="https://120406.zoty2508.com:30501/?i_code=3354655"
target="_blank"><img
src="/forum/data/attachment/forum/ads/zo_20260923_5.jpg"/></a></li>
<li class="num3 img slide"><a href="https://120406.zoty2508.com:30501/?i_code=3354655"
target="_blank"><img
src="/forum/data/attachment/forum/ads/zo_20260923_3.jpg"/></a></li>
<li class="num4 img slide"><a href="https://120406.zoty2508.com:30501/?i_code=3354655"
target="_blank"><img
src="/forum/data/attachment/forum/ads/zo_20260923_4.jpg"/></a></li>
</ul>
</div>
</div>
</div>
<input id="token" type="hidden" name="token"
value="c738pomVJo1m19OjPoW3yyugBlnubpVPF1MM7fQqG5T34oAOtWShxw11DbmlgOiBYN4XPjFHM+hZU4ePIlpK3osjclQs/ULQEZbJ">
<div class="container">
<h3>NIMA-086 実写版!ムラムラしたので通勤中にお持ち帰りされてみた 彩月七緒</h3>
<div class="row movie">
<div class="col-md-9 screencap">
<a class="bigImage" href="/pics/cover/ckd7_b.jpg"><img src="/pics/cover/ckd7_b.jpg"
title="実写版!ムラムラしたので通勤中にお持ち帰りされてみた 彩月七緒"></a>
</div>
<div class="col-md-3 info">
<p><span class="header">識別碼:</span> <span style="color:#CC0000;">NIMA-086</span>
</p>
<p><span class="header">發行日期:</span> 2026-09-25</p>
<p><span class="header">長度:</span> 120分鐘</p>
<p><span class="header">導演:</span> <a href="https://www.javbus.com/director/a3">ドラゴン西川</a></p>
<p><span class="header">製作商:</span> <a href="https://www.javbus.com/studio/dk">Fitch</a>
</p>
<p><span class="header">發行商:</span> <a href="https://www.javbus.com/label/1oe">肉漫</a>
</p>
<p class="header">類別:<span id="genre-toggle" class="glyphicon glyphicon-plus"
style="cursor: pointer;"></span></p>
<p><span class="genre"><label><input type="checkbox" name="gr_sel" value="1z"><a
href="https://www.javbus.com/genre/1z">潮吹</a></label></span>
<span class="genre"><label><input type="checkbox" name="gr_sel" value="4"><a
href="https://www.javbus.com/genre/4">中出</a></label></span>
<span class="genre"><label><input type="checkbox" name="gr_sel" value="4o"><a
href="https://www.javbus.com/genre/4o">高畫質</a></label></span>
<span class="genre"><label><input type="checkbox" name="gr_sel" value="6l"><a
href="https://www.javbus.com/genre/6l">原作コラボ</a></label></span>
<span class="genre"><label><input type="checkbox" name="gr_sel" value="e"><a
href="https://www.javbus.com/genre/e">巨乳</a></label></span>
<span class="genre"><label><input type="checkbox" name="gr_sel" value="e3"><a
href="https://www.javbus.com/genre/e3">アクメ・オーガズム</a></label></span>
<span class="genre"><label><input type="checkbox" name="gr_sel" value="f"><a
href="https://www.javbus.com/genre/f">單體作品</a></label></span>
<span class="genre"><label><input type="checkbox" name="gr_sel" value="g"><a
href="https://www.javbus.com/genre/g">DMM獨家</a></label></span>
<span class="genre"><button id="gr_btn" type="button" class="btn">多選提交</button></span>
</p>
<p class="star-show"><span class="header" style="cursor: pointer;">演員</span>:<span id="star-toggle"
class="glyphicon glyphicon-minus"
style="cursor: pointer;"></span>
</p>
<ul>
<div id="star_11wm" class="star-box star-box-common star-box-up idol-box">
<li>
<a href="https://www.javbus.com/star/11wm"><img src="/pics/actress/11wm_a.jpg" title="彩月七緒"></a>
<div class="star-name"><a href="https://www.javbus.com/star/11wm" title="彩月七緒">彩月七緒</a>
</div>
</li>
</div>
</ul>
<p>
<span class="genre" onmouseover="hoverdiv(event,'star_11wm')"
onmouseout="hoverdiv(event,'star_11wm')">
<a href="https://www.javbus.com/star/11wm">彩月七緒</a>
</span>
</p>
</div>
</div>
<div id="star-div">
<h4 id="star-hide" style="cursor: pointer;">演員 <span class="glyphicon glyphicon-minus"></span></h4>
<div id="avatar-waterfall">
<a class="avatar-box" href="https://www.javbus.com/star/11wm">
<div class="photo-frame">
<img src="/pics/actress/11wm_a.jpg" title="彩月七緒">
</div>
<span>彩月七緒</span>
</a>
</div>
</div>
<h4 id="mag-submit-show" style="cursor: pointer;">磁力連結投稿 <span id="mag-submit-toggle"
class="glyphicon glyphicon-plus"></span></h4>
<div id="mag-submit" class="movie" style="padding:30px 20px 30px 5px;">
<div id="mag-submit-hide" class="close" style="margin:-25px -13px 0 0;">&times;</div>
<div class="col-md-11 col-xs-10">
<div class="input-group">
<div class="input-group-addon">magnet地址:</div>
<input type="text" class="form-control" id="appendedInputButton">
</div>
</div>
<button type="button" class="btn btn-default col-md-1 col-xs-2" onClick="checktxt()" data-toggle="modal"
data-target="#magneturlpost">分享
</button>
</div>
<!-- Magnet Verify Modal -->
<div id="magneturlpost" class="modal fade" tabindex="-1" role="dialog"></div>
<div class="movie" style="padding:12px; margin-top:15px">
<table id="magnet-table" class="table table-condensed table-striped table-hover" style="margin-bottom:0;">
<tr style="font-weight:bold;">
<td>磁力名稱 <span class="glyphicon glyphicon-magnet"></span></td>
<td style="text-align:center;white-space:nowrap">檔案大小</td>
<td style="text-align:center;white-space:nowrap">分享日期</td>
</tr>
</table>
<div id="movie-loading">
<table width="120" border="0" cellpadding="5" cellspacing="0">
<tbody>
<tr>
<td align="center">
<font class="ajax-text"><img src="https://www.javbus.com/images/movie_loading.gif"
border="0"></font>
</td>
<td align="center">
<font class="ajax-text">讀取中...</font>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<h4>樣品圖像</h4>
<div id="sample-waterfall">
<a class="sample-box" href="https://awsimgsrc.dmm.co.jp/pics_dig/digital/video/nima00086/nima00086jp-1.jpg">
<div class="photo-frame"><img src="/pics/sample/ckd7_1.jpg"
title="NIMA-086 実写版!ムラムラしたので通勤中にお持ち帰りされてみた 彩月七緒 - 樣品圖像 - 1">
</div>
</a> <a class="sample-box"
href="https://awsimgsrc.dmm.co.jp/pics_dig/digital/video/nima00086/nima00086jp-2.jpg">
<div class="photo-frame"><img src="/pics/sample/ckd7_2.jpg"
title="NIMA-086 実写版!ムラムラしたので通勤中にお持ち帰りされてみた 彩月七緒 - 樣品圖像 - 2">
</div>
</a> <a class="sample-box" href="https://awsimgsrc.dmm.co.jp/pics_dig/digital/video/nima00086/nima00086jp-3.jpg">
<div class="photo-frame"><img src="/pics/sample/ckd7_3.jpg"
title="NIMA-086 実写版!ムラムラしたので通勤中にお持ち帰りされてみた 彩月七緒 - 樣品圖像 - 3">
</div>
</a> <a class="sample-box" href="https://awsimgsrc.dmm.co.jp/pics_dig/digital/video/nima00086/nima00086jp-4.jpg">
<div class="photo-frame"><img src="/pics/sample/ckd7_4.jpg"
title="NIMA-086 実写版!ムラムラしたので通勤中にお持ち帰りされてみた 彩月七緒 - 樣品圖像 - 4">
</div>
</a> <a class="sample-box" href="https://awsimgsrc.dmm.co.jp/pics_dig/digital/video/nima00086/nima00086jp-5.jpg">
<div class="photo-frame"><img src="/pics/sample/ckd7_5.jpg"
title="NIMA-086 実写版!ムラムラしたので通勤中にお持ち帰りされてみた 彩月七緒 - 樣品圖像 - 5">
</div>
</a> <a class="sample-box" href="https://awsimgsrc.dmm.co.jp/pics_dig/digital/video/nima00086/nima00086jp-6.jpg">
<div class="photo-frame"><img src="/pics/sample/ckd7_6.jpg"
title="NIMA-086 実写版!ムラムラしたので通勤中にお持ち帰りされてみた 彩月七緒 - 樣品圖像 - 6">
</div>
</a> <a class="sample-box" href="https://awsimgsrc.dmm.co.jp/pics_dig/digital/video/nima00086/nima00086jp-7.jpg">
<div class="photo-frame"><img src="/pics/sample/ckd7_7.jpg"
title="NIMA-086 実写版!ムラムラしたので通勤中にお持ち帰りされてみた 彩月七緒 - 樣品圖像 - 7">
</div>
</a> <a class="sample-box" href="https://awsimgsrc.dmm.co.jp/pics_dig/digital/video/nima00086/nima00086jp-8.jpg">
<div class="photo-frame"><img src="/pics/sample/ckd7_8.jpg"
title="NIMA-086 実写版!ムラムラしたので通勤中にお持ち帰りされてみた 彩月七緒 - 樣品圖像 - 8">
</div>
</a> <a class="sample-box" href="https://awsimgsrc.dmm.co.jp/pics_dig/digital/video/nima00086/nima00086jp-9.jpg">
<div class="photo-frame"><img src="/pics/sample/ckd7_9.jpg"
title="NIMA-086 実写版!ムラムラしたので通勤中にお持ち帰りされてみた 彩月七緒 - 樣品圖像 - 9">
</div>
</a> <a class="sample-box" href="https://awsimgsrc.dmm.co.jp/pics_dig/digital/video/nima00086/nima00086jp-10.jpg">
<div class="photo-frame"><img src="/pics/sample/ckd7_10.jpg"
title="NIMA-086 実写版!ムラムラしたので通勤中にお持ち帰りされてみた 彩月七緒 - 樣品圖像 - 10">
</div>
</a> <a class="sample-box" href="https://awsimgsrc.dmm.co.jp/pics_dig/digital/video/nima00086/nima00086jp-11.jpg">
<div class="photo-frame"><img src="/pics/sample/ckd7_11.jpg"
title="NIMA-086 実写版!ムラムラしたので通勤中にお持ち帰りされてみた 彩月七緒 - 樣品圖像 - 11">
</div>
</a> <a class="sample-box" href="https://awsimgsrc.dmm.co.jp/pics_dig/digital/video/nima00086/nima00086jp-12.jpg">
<div class="photo-frame"><img src="/pics/sample/ckd7_12.jpg"
title="NIMA-086 実写版!ムラムラしたので通勤中にお持ち帰りされてみた 彩月七緒 - 樣品圖像 - 12">
</div>
</a> <a class="sample-box" href="https://awsimgsrc.dmm.co.jp/pics_dig/digital/video/nima00086/nima00086jp-13.jpg">
<div class="photo-frame"><img src="/pics/sample/ckd7_13.jpg"
title="NIMA-086 実写版!ムラムラしたので通勤中にお持ち帰りされてみた 彩月七緒 - 樣品圖像 - 13">
</div>
</a> <a class="sample-box" href="https://awsimgsrc.dmm.co.jp/pics_dig/digital/video/nima00086/nima00086jp-14.jpg">
<div class="photo-frame"><img src="/pics/sample/ckd7_14.jpg"
title="NIMA-086 実写版!ムラムラしたので通勤中にお持ち帰りされてみた 彩月七緒 - 樣品圖像 - 14">
</div>
</a> <a class="sample-box" href="https://awsimgsrc.dmm.co.jp/pics_dig/digital/video/nima00086/nima00086jp-15.jpg">
<div class="photo-frame"><img src="/pics/sample/ckd7_15.jpg"
title="NIMA-086 実写版!ムラムラしたので通勤中にお持ち帰りされてみた 彩月七緒 - 樣品圖像 - 15">
</div>
</a> <a class="sample-box" href="https://awsimgsrc.dmm.co.jp/pics_dig/digital/video/nima00086/nima00086jp-16.jpg">
<div class="photo-frame"><img src="/pics/sample/ckd7_16.jpg"
title="NIMA-086 実写版!ムラムラしたので通勤中にお持ち帰りされてみた 彩月七緒 - 樣品圖像 - 16">
</div>
</a> <a class="sample-box" href="https://awsimgsrc.dmm.co.jp/pics_dig/digital/video/nima00086/nima00086jp-17.jpg">
<div class="photo-frame"><img src="/pics/sample/ckd7_17.jpg"
title="NIMA-086 実写版!ムラムラしたので通勤中にお持ち帰りされてみた 彩月七緒 - 樣品圖像 - 17">
</div>
</a> <a class="sample-box" href="https://awsimgsrc.dmm.co.jp/pics_dig/digital/video/nima00086/nima00086jp-18.jpg">
<div class="photo-frame"><img src="/pics/sample/ckd7_18.jpg"
title="NIMA-086 実写版!ムラムラしたので通勤中にお持ち帰りされてみた 彩月七緒 - 樣品圖像 - 18">
</div>
</a></div>
<div class="clearfix"></div>
<h4 style="position:relative">推薦 <a href="javascript:bootstr(1);"><span id="urad2"
class="label label-default"><span
class="left-urad2">投放廣告</span><span class="glyphicon glyphicon-envelope"></span></span></a></h4>
<div class="row">
<div class="col-xs-12 col-md-4 text-center ptb10">
<script type="text/javascript" data-cfasync="false" async
src="https://poweredby.jads.co/js/jads.js"></script>
<ins id="1024556" data-width="300" data-height="250"></ins>
<script type="text/javascript" data-cfasync="false"
async>(adsbyjuicy = window.adsbyjuicy || []).push({"adzone": 1024556});</script>
</div>
<div class="col-xs-12 col-md-4 text-center ptb10">
<script type="text/javascript" data-cfasync="false" async
src="https://poweredby.jads.co/js/jads.js"></script>
<ins id="1024558" data-width="300" data-height="250"></ins>
<script type="text/javascript" data-cfasync="false"
async>(adsbyjuicy = window.adsbyjuicy || []).push({"adzone": 1024558});</script>
</div>
<div class="col-xs-12 col-md-4 text-center ptb10">
<script type="text/javascript" data-cfasync="false" async
src="https://poweredby.jads.co/js/jads.js"></script>
<ins id="1024557" data-width="300" data-height="250"></ins>
<script type="text/javascript" data-cfasync="false"
async>(adsbyjuicy = window.adsbyjuicy || []).push({"adzone": 1024557});</script>
</div>
</div>
<h4>同類影片</h4>
<div id="related-waterfall" class="mb20">
<a title="FANZA同人ランキング1位シリーズ累計10万部突破 実写版 パパ活セーブデータ タイムリープ能力で塩対応女子をデレさせイキ壊れるまで無限攻略! 北岡果林"
class="movie-box" href="https://www.javbus.com/MIMK-291" style="display:inline-block; margin:5px;">
<div class="photo-frame">
<img src="/pics/thumb/cj4u.jpg">
</div>
<div class="photo-info" style="height:36px; overflow:hidden; text-align:center;">
<span>FANZA同人ランキング1位シリーズ累計10万部突破 実写版 パパ活セーブデータ タイムリープ能力で塩対応女子をデレさせイキ壊れるまで無限攻略! 北岡果林</span>
</div>
</a>
<a title="スペンス乳腺開発クリニックSpecial あんづ杏" class="movie-box" href="https://www.javbus.com/PPPE-442"
style="display:inline-block; margin:5px;">
<div class="photo-frame">
<img src="/pics/thumb/cj74.jpg">
</div>
<div class="photo-info" style="height:36px; overflow:hidden; text-align:center;">
<span>スペンス乳腺開発クリニックSpecial あんづ杏</span>
</div>
</a>
<a title="ドコでも即ハメおねだり中出しOKレンタル彼女~恋人代行なのに利用時間が過ぎても帰してくれない性欲ビースト24時~ 美園和花"
class="movie-box" href="https://www.javbus.com/MNGS-076" style="display:inline-block; margin:5px;">
<div class="photo-frame">
<img src="/pics/thumb/cgx2.jpg">
</div>
<div class="photo-info" style="height:36px; overflow:hidden; text-align:center;">
<span>ドコでも即ハメおねだり中出しOKレンタル彼女~恋人代行なのに利用時間が過ぎても帰してくれない性欲ビースト24時~ 美園和花</span>
</div>
</a>
<a title="12時間ノンストップ性感開発モニタリング痙攣・潮吹き・エビ反り失神アクメフルコース 楪カレン"
class="movie-box" href="https://www.javbus.com/PPPE-433" style="display:inline-block; margin:5px;">
<div class="photo-frame">
<img src="/pics/thumb/cgxy.jpg">
</div>
<div class="photo-info" style="height:36px; overflow:hidden; text-align:center;">
<span>12時間ノンストップ性感開発モニタリング痙攣・潮吹き・エビ反り失神アクメフルコース 楪カレン</span>
</div>
</a>
<a title="新・世界一媚薬でキマる変態女のトランス失禁キメセク大絶頂FUCK!! 響蓮" class="movie-box"
href="https://www.javbus.com/RKI-747" style="display:inline-block; margin:5px;">
<div class="photo-frame">
<img src="/pics/thumb/c8dw.jpg">
</div>
<div class="photo-info" style="height:36px; overflow:hidden; text-align:center;">
<span>新・世界一媚薬でキマる変態女のトランス失禁キメセク大絶頂FUCK!! 響蓮</span>
</div>
</a>
<a title="勝手に住みついてる陰キャ幽霊のカラダを好き勝手する話1・2 幽霊だからいくら中出ししたって妊娠しない! それなら毎日、生ハメ・中出しヤリまくり! いいことしかないグラマラス幽霊さんとの同居生活! さつき芽衣"
class="movie-box" href="https://www.javbus.com/RKI-736" style="display:inline-block; margin:5px;">
<div class="photo-frame">
<img src="/pics/thumb/c8dy.jpg">
</div>
<div class="photo-info" style="height:36px; overflow:hidden; text-align:center;">
<span>勝手に住みついてる陰キャ幽霊のカラダを好き勝手する話1・2 幽霊だからいくら中出ししたって妊娠しない! それなら毎日、生ハメ・中出しヤリまくり! いいことしかないグラマラス幽霊さんとの同居生活! さつき芽衣</span>
</div>
</a>
</div>
<script language="JavaScript">
(function ($) {
$('img.img').each(function () {
var image = new Image();
image.src = this.src;
if (image.complete) {
cutImgz(this);
} else {
this.onload = function () {
cutImgz(this);
}
}
});
})(jQuery);
function cutImgz(obj) {
var image = new Image();
image.src = obj.src;
$this = $(obj);
var w = image.width;
var h = image.height;
var p = w / h;
if (p > 1.38 && p < 1.40) {
$this.addClass("m1000giri");
}
if (p > 0.99 && p < 1.01) {
$this.addClass("mpacopacomama");
}
if (p > 1.11 && p < 1.13) {
$this.addClass("mcaribbeancom");
}
if (p > 1.77 && p < 1.78) {
$this.addClass("mcaribbeancom2");
}
if (p > 0.59 && p < 0.61) {
$this.addClass("m1pondo");
}
if (p > 0.65 && p < 0.67) {
$this.addClass("mtokyohot");
}
if (p > 1.78 && p < 1.79) {
$this.addClass("mheyzo");
}
}
</script>
<h4>論壇熱帖</h4>
<div id="related-waterfall" class="mb20">
<a title="极品骚浪人妻张燕不雅视频被曝超美孕妇骚劲" class="movie-box"
href="https://www.javbus.com/forum/forum.php?mod=viewthread&tid=176676"
style="display:inline-block; margin:5px;">
<div class="photo-frame bforum">
<img class="mforum" src="https://www.javbus.com/forum/data/attachment/forum/202609/XFHWAI9.jpeg">
</div>
<div class="photo-info" style="height:36px; overflow:hidden; text-align:center;">
<span>极品骚浪人妻张燕不雅视频被曝超美孕妇骚劲</span>
</div>
</a>
<a title="好东西不分享给大家我的良心会痛 四" class="movie-box"
href="https://www.javbus.com/forum/forum.php?mod=viewthread&tid=176691"
style="display:inline-block; margin:5px;">
<div class="photo-frame bforum">
<img class="mforum" src="https://www.javbus.com/forum/data/attachment/forum/202609/NuKOXOl.jpeg">
</div>
<div class="photo-info" style="height:36px; overflow:hidden; text-align:center;">
<span>好东西不分享给大家我的良心会痛 四</span>
</div>
</a>
<a title="求一欧美片,必须实用,曾经我的珍藏,丢失" class="movie-box"
href="https://www.javbus.com/forum/forum.php?mod=viewthread&tid=176678"
style="display:inline-block; margin:5px;">
<div class="photo-frame bforum">
<img class="mforum" src="https://www.javbus.com/forum/data/attachment/forum/202609/MI4O4K8.jpg">
</div>
<div class="photo-info" style="height:36px; overflow:hidden; text-align:center;">
<span>求一欧美片,必须实用,曾经我的珍藏,丢失</span>
</div>
</a>
<a title="看到一张图想知道名字" class="movie-box"
href="https://www.javbus.com/forum/forum.php?mod=viewthread&tid=176686"
style="display:inline-block; margin:5px;">
<div class="photo-frame bforum">
<img class="mforum" src="https://www.javbus.com/forum/data/attachment/forum/202609/wvxEc6L.png">
</div>
<div class="photo-info" style="height:36px; overflow:hidden; text-align:center;">
<span>看到一张图想知道名字</span>
</div>
</a>
<a title="广佛中秋国庆双节出击出击第二弹 长腿御姐" class="movie-box"
href="https://www.javbus.com/forum/forum.php?mod=viewthread&tid=176692"
style="display:inline-block; margin:5px;">
<div class="photo-frame bforum">
<img class="mforum" src="https://www.javbus.com/forum/data/attachment/forum/202609/AX1wxZU.jpeg">
</div>
<div class="photo-info" style="height:36px; overflow:hidden; text-align:center;">
<span>广佛中秋国庆双节出击出击第二弹 长腿御姐</span>
</div>
</a>
<a title="求这完整的套图" class="movie-box"
href="https://www.javbus.com/forum/forum.php?mod=viewthread&tid=176647"
style="display:inline-block; margin:5px;">
<div class="photo-frame bforum">
<img class="mforum" src="https://www.javbus.com/forum/data/attachment/forum/202609/eblYOrO.jpeg">
</div>
<div class="photo-info" style="height:36px; overflow:hidden; text-align:center;">
<span>求这完整的套图</span>
</div>
</a>
</div>
<script>
(function ($) {
$('.bigImage').magnificPopup({
type: 'image',
closeOnContentClick: true,
closeBtnInside: false,
fixedContentPos: true,
mainClass: 'mfp-no-margins mfp-with-zoom',
image: {
verticalFit: true,
titleSrc: function (item) {
return "NIMA-086 実写版!ムラムラしたので通勤中にお持ち帰りされてみた 彩月七緒";
}
},
zoom: {
enabled: true,
duration: 300
}
});
$('#sample-waterfall').magnificPopup({
delegate: 'a',
type: 'image',
closeOnContentClick: false,
closeBtnInside: false,
mainClass: 'mfp-with-zoom mfp-img-mobile',
image: {
verticalFit: true,
titleSrc: function (item) {
return "NIMA-086 実写版!ムラムラしたので通勤中にお持ち帰りされてみた 彩月七緒";
}
},
gallery: {
enabled: true
},
zoom: {
enabled: true,
duration: 300,
opener: function (element) {
return element.find('img');
}
}
});
})(jQuery);
$("#gr_btn").click(function () {
var t = "";
$("input[name='gr_sel']:checkbox").each(function () {
$(this).is(":checked") && (t += $(this).val() + "-")
}), "" != t && (t = t.substring(0, t.length - 1), window.location.href = "genre/" + t)
});
</script>
</div>
<div class="ad-box">
<div class="ad-item">
<div class="ad-juicy">
<script type="text/javascript" data-cfasync="false" async
src="https://poweredby.jads.co/js/jads.js"></script>
<ins id="1024551" data-width="728" data-height="90"></ins>
<script type="text/javascript" data-cfasync="false"
async>(adsbyjuicy = window.adsbyjuicy || []).push({"adzone": 1024551});</script>
</div>
</div>
<div class="ad-item"><a href="https://hgdj.rezluatfbfvgwjhzfhrkovblo.com/chan/ak0072/acXm7" target="_blank"
rel="nofollow"><img src="/ads/hjll_728x90_39.gif" width="728" height="90"></a></div>
<div class="ad-item"><a href="https://xn--fwza.168good.cc/#/?ai=157284" target="_blank" rel="nofollow"><img
src="/ads/51fl_728x90_2.jpg" width="728" height="90"></a></div>
<div class="ad-item"><a href="https://91p.khgnhlzvcisaddwfehlvtnhel.com/chan/GS4670/aBhGW" target="_blank"
rel="nofollow"><img src="/ads/hjll_728x90_18.gif" width="728" height="90"></a></div>
</div>
<script src='https://www.javbus.com/js/gallery.js?v=2.9'></script>
<footer class="footer hidden-xs">
<div class="container-fluid">
<p><a href="https://www.javbus.com/doc/terms">Terms</a> / <a
href="https://www.javbus.com/doc/privacy">Privacy</a> / <a
href="https://www.javbus.com/doc/usc">2257</a> / <a href="http://www.rtalabel.org/" target="_blank"
rel="external nofollow">RTA</a> / <a
href="javascript:bootstr(1);" r>廣告投放</a> / <a href="javascript:bootstr(2);">聯絡我們</a><br/><a
href="#formModal" id="adscontact" data-toggle="modal"></a>
Copyright © 2013 JavBus. All Rights Reserved. All other trademarks and copyrights are the property of their
respective holders. The reviews and comments expressed at or through this website are the opinions of the
individual author and do not reflect the opinions or views of JavBus. JavBus is not responsible for the
accuracy of any of the information supplied here.</p>
</div>
</footer>
<div class="visible-xs-block footer-bar-placeholder"></div>
<script language=javascript>
function bootstr(type) {
ads = "廣告投放";
contact = "聯絡我們";
translate = "翻譯";
$("#adstype").val(type);
if (type == 1) {
$("#contactModalLab").html(ads);
$("#qqskype").show();
$("#transinfo").hide();
$("#translanguage").hide();
$("#mailcontent").show();
} else if (type == 2) {
$("#contactModalLab").html(contact);
$("#qqskype").show();
$("#transinfo").hide();
$("#translanguage").hide();
$("#mailcontent").show();
} else if (type == 3) {
$("#contactModalLab").html(translate);
$("#qqskype").hide();
$("#transinfo").show();
$("#translanguage").show();
$("#mailcontent").hide();
}
$("#adscontact").trigger("click");
getverifycode();
};
function getverifycode() {
$('#verify').attr("src", "/post/verify?" + Math.random() * 10000);
};
function IsMail(mail) {
var remail = /^([a-zA-Z0-9_-])+(\.)?([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/;
return (remail.test(mail));
};
function checkform() {
var post = true;
if ($("#verifycode").val().length != 5) {
alert("驗證碼輸入錯誤!")
$("#verifycode").focus();
post = false;
}
if ($("#contact").val().length > 255) {
alert("聯繫方式字數過多!")
$("#contact").focus();
post = false;
}
if (!IsMail($("#mail").val())) {
alert("請輸入正確的電郵地址!")
$("#mail").focus();
post = false;
}
if ($("#intention").val().length > 25500) {
alert("投放意向字數過多!")
$("#intention").focus();
post = false;
}
if ($("#trans").val().length > 255) {
alert("Too many words in your language textbox!")
$("#intention").focus();
post = false;
}
if (post == true) {
$("#modalclose").trigger("click");
$("#postform").attr("action", "/post/contact");
$("#postform").submit();
}
return post;
};
</script>
<script>
$("#showmag,#cellshowmag,#resultshowmag").click(function () {
$.cookie("existmag", "mag", {expires: 365, path: '/'});
location.reload()
});
$("#showall,#cellshowall,#resultshowall").click(function () {
$.cookie("existmag", "all", {expires: 365, path: '/'});
location.reload()
});
$("#showonline").click(function () {
$.cookie("existmag", "online", {expires: 365, path: '/'});
location.reload()
});
$(".info .mypointer").click(function () {
var obj = $(this);
var code = obj.attr('value');
var token = $("#token").val();
var e = "../ajax/addfavorite.php?code=" + encodeURIComponent(code) + "&token=" + encodeURIComponent(token) + "&floor=" + Math.floor(Math.random() * 1e3 + 1);
$.ajax({
url: e,
type: "POST",
//dataType: "json",
cache: false,
success: function (json) {
//obj.html(json);
ajaxobj = eval("(" + json + ")");
if (ajaxobj.act == 'err') {
alert('收藏次數達上限,請稍候再試');
} else {
obj.html(ajaxobj.act);
obj.attr('value', ajaxobj.code);
obj.attr('title', ajaxobj.title);
$("#token").val(ajaxobj.token);
}
}
});
});
$(".glyphicon-heart-empty").hover(function () {
$(this).removeClass('glyphicon-heart-empty');
$(this).addClass('glyphicon-heart');
}, function () {
$(this).removeClass('glyphicon-heart');
$(this).addClass('glyphicon-heart-empty');
});
$(".glyphicon-heart").hover(function () {
$(this).removeClass('glyphicon-heart');
$(this).addClass('glyphicon-heart-empty');
}, function () {
$(this).removeClass('glyphicon-heart-empty');
$(this).addClass('glyphicon-heart');
});
</script>
<!-- Modal Forms -->
<div id="formModal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="formModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button id="modalclose" type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="contactModalLab">聯絡我們</h4>
</div>
<div class="modal-body">
<form class="form-horizontal" name="postform" method="post" id="postform" enctype="multipart/form-data">
<fieldset>
<div class="form-group" id="qqskype">
<label class="col-sm-4 control-label" for="contact">Telegram / 電報</label>
<div class="col-sm-6">
<input id="contact" name="contact" type="text" placeholder="" class="form-control">
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label" for="mail">Email</label>
<div class="col-sm-6">
<input id="mail" name="mail" type="text" placeholder="" class="form-control">
</div>
</div>
<div class="form-group" id="translanguage">
<label class="col-sm-4 control-label" for="trans">Your Language</label>
<div class="col-sm-6">
<input id="trans" name="trans" type="text" placeholder="" class="form-control">
</div>
</div>
<div class="form-group" id="mailcontent">
<label class="col-sm-4 control-label" for="intention" id="inten-trans">內容</label>
<div class="col-sm-6">
<textarea id="intention" name="intention" rows="9" class="form-control"></textarea>
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label" for="verify">驗證碼</label>
<div class="col-sm-6">
<input type="text" id="verifycode" name="verifycode" style="width:50px"/>
<img id="verify" src="" style="cursor: pointer; vertical-align:middle;"
onclick="getverifycode()"/>
</div>
</div>
<input type="hidden" id="adstype" name="adstype" value="1"/>
</fieldset>
</form>
</div>
<div class="modal-footer">
<button type="button" button class="btn btn-primary" onclick="checkform()">送出</button>
<button type="button" button class="btn btn-default" data-dismiss="modal">關閉</button>
</div>
</div>
</div>
</div>
<!-- ////////////////////////////////////////////////// -->
<div class="overlay overlay-contentscale">
<div class="row">
<div class="col-xs-12 text-center ptb20">
<div class="input-group col-xs-offset-2 col-xs-8">
<input id="search-input-mobile" type="text" class="form-control" placeholder="搜尋 識別碼, 影片, 演員">
<span class="input-group-btn">
<button class="btn btn-default" type="submit"
onClick="searchs('search-input-mobile')">搜尋</button>
</span>
</div>
</div>
<div class="col-xs-6 text-center"><a href="https://www.javbus.com/">有碼</a></div>
<div class="col-xs-6 text-center"><a href="https://www.javbus.com/uncensored">無碼</a></div>
<div class="col-xs-6 text-center"><a href="https://www.javbus.com/genre">有碼類別</a></div>
<div class="col-xs-6 text-center"><a href="https://www.javbus.com/uncensored/genre">無碼類別</a></div>
<div class="col-xs-6 text-center"><a href="https://www.javbus.com/actresses">有碼女優</a></div>
<div class="col-xs-6 text-center"><a href="https://www.javbus.com/uncensored/actresses">無碼女優</a></div>
<div class="col-xs-6 text-center"><a href="https://www.javbus.hair/">歐美</a></div>
<div class="col-xs-6 text-center"><a href="https://www.javbus.com/forum/">論壇</a></div>
<div class="col-xs-12 text-center overlay-close">
<i class="glyphicon glyphicon-remove"></i>
</div>
</div>
</div>
</body>
</html>
+1248
View File
File diff suppressed because it is too large Load Diff
+5 -3
View File
@@ -1,17 +1,16 @@
{
"name": "jhs-enhance",
"version": "3.3.6",
"version": "3.3.10",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "jhs-enhance",
"version": "3.3.6",
"version": "3.3.10",
"license": "MIT",
"dependencies": {
"blueimp-md5": "^2.19.0",
"jquery": "^4.0.0",
"layui-layer": "^1.0.9",
"localforage": "^1.10.0",
"qrcodejs": "^1.0.0",
"tabulator-tables": "^6.5.2",
@@ -19,6 +18,7 @@
"viewerjs": "^1.12.0"
},
"devDependencies": {
"layui-layer": "^1.0.9",
"vite": "^6.0.0",
"vite-plugin-monkey": "^5.0.0"
}
@@ -1204,6 +1204,7 @@
"version": "1.0.9",
"resolved": "https://registry.npmmirror.com/layui-layer/-/layui-layer-1.0.9.tgz",
"integrity": "sha512-CzPVtG0D4IvmISUyLTzzbz2Uxfaoqch+87lHqVmO53ie8ZKOfOkENbBl7ssiLna1lAnv4M1SycmH62NbtndtDQ==",
"dev": true,
"license": "ISC",
"dependencies": {
"jquery": "^3.1.1"
@@ -1213,6 +1214,7 @@
"version": "3.7.1",
"resolved": "https://registry.npmmirror.com/jquery/-/jquery-3.7.1.tgz",
"integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==",
"dev": true,
"license": "MIT"
},
"node_modules/lie": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "jhs-enhance",
"version": "3.3.6",
"version": "3.3.18",
"description": "Jav-鉴黄师 油猴脚本 - 工程化版本",
"type": "module",
"scripts": {
+61
View File
@@ -0,0 +1,61 @@
const COVER_API_URL = [77, 81, 81, 85, 86, 31, 10, 10, 68, 85, 76, 8, 65, 72, 72, 87, 86, 70, 11, 20, 22, 20, 17, 22, 22, 11, 93, 92, 95, 10, 68, 85, 76, 10, 70, 74, 83, 64, 87]
.map(v => String.fromCharCode(v ^ 0x25)).join('');
const TRAILER_API_URL = [77, 81, 81, 85, 86, 31, 10, 10, 68, 85, 76, 8, 65, 72, 72, 87, 86, 70, 11, 20, 22, 20, 17, 22, 22, 11, 93, 92, 95, 10, 68, 85, 76, 10, 81, 87, 68, 76, 73, 64, 87]
.map(v => String.fromCharCode(v ^ 0x25)).join('');
const BASE_URL = [77, 81, 81, 85, 86, 31, 10, 10, 68, 85, 76, 8, 65, 72, 72, 87, 86, 70, 11, 20, 22, 20, 17, 22, 22, 11, 93, 92, 95]
.map(v => String.fromCharCode(v ^ 0x25)).join('');
const COVER_API_TOKEN = [18, 28, 19, 19, 65, 65, 70, 28, 64, 29, 23, 21, 17, 29, 67, 70, 18, 64, 21, 22, 64, 16, 65, 29, 22, 19, 16, 28, 16, 17, 64, 29]
.map(v => String.fromCharCode(v ^ 0x25)).join('');
const getCover = async (code) => {
const query = String(code || '').trim();
if (!query) return null;
try {
const r = await gmHttp.get(`${COVER_API_URL}/${encodeURIComponent(query)}`, null, {
'Authorization': `Bearer ${COVER_API_TOKEN}`,
}, {timeout: 15000});
if (r?.cover?.large) {
//clog.log('高清封面地址:', r.cover.large);
return r.cover.large;
}
return null;
} catch (e) {
clog.error('获取封面失败:', e.message);
return null;
}
};
const getTrailer = async (code) => {
const query = String(code || '').trim();
if (!query) return null;
try {
const r = await gmHttp.get(`${TRAILER_API_URL}/${encodeURIComponent(query)}`, null, {
'Authorization': `Bearer ${COVER_API_TOKEN}`,
}, {timeout: 15000});
if (r?.trailers && Array.isArray(r.trailers) && r.trailers.length > 0) {
const qualityMap = {};
r.trailers.forEach((item) => {
if (item.proxy && item.quality) {
qualityMap[item.quality] = `${BASE_URL}${item.proxy}`;
}
});
if (Object.keys(qualityMap).length > 0) {
clog.log('预告片播放地址:', JSON.stringify(qualityMap));
return qualityMap;
}
}
return null;
} catch (e) {
clog.error('获取预告片失败:', e.message);
return null;
}
};
export {getCover, getTrailer};
+26
View File
@@ -211,10 +211,36 @@ const getJavxyVideoUrls = async (code, failedSources = []) => {
return null;
};
const getJavxyCover = async (code) => {
const query = String(code || '').trim();
if (!query) return null;
for (const endpoint of JAVXY_ENDPOINTS) {
const params = new URLSearchParams({client: 'laosiji-new'});
const apiUrl = `https://${endpoint.host}/covers/${encodeURIComponent(query)}?${params}`;
let r;
try {
r = await gmHttp.get(apiUrl, null, {
'Accept': 'application/json,text/plain,*/*',
'X-Javxy-Token': JAVXY_TOKEN,
}, {timeout: 15000});
} catch (e) {
continue;
}
if (!r) continue;
if (r?.found && (r.url || r.cover || r.highCover)) return r;
return null;
}
return null;
};
export {
fromJavxyCcCd,
fallbackJavxyResult,
getJavxyVideoUrls,
getJavxyCover,
normalizeJavxySource,
isJpSourceTemporarilyFailed,
markJpSourceTemporarilyFailed,
+1 -4
View File
@@ -145,10 +145,7 @@ class BasePlugin {
carNum2 = dateElements.find((i => !isDate(i))) || null;
}
if (!carNum2) {
const msg = "提取番号信息失败: carNum 为空";
console.error("Error in getBoxCarInfo:", msg, "Box Element:", $box2.get(0));
show.error(msg);
throw new Error(msg);
return null;
}
return {
carNum: carNum2,
+38
View File
@@ -63,6 +63,16 @@ const currentHref = window.location.href,
quality: "mmb",
text: "中画质 (432p)",
canSelect: !0
}, {
id: "video-dm",
quality: "dm",
text: "低画质 (1000kbps)",
canSelect: !0
}, {
id: "video-dmb",
quality: "dmb",
text: "中画质 (1500kbps)",
canSelect: !0
}, {
id: "video-mhb",
quality: "mhb",
@@ -577,6 +587,23 @@ const mainCss = `
color: #f40 !important;
}
#magnets-content .item[data-rank] {
position: relative !important;
}
#magnets-content .item[data-rank] .buttons {
position: absolute !important;
bottom: 0;
right: 0;
margin: 0 !important;
display: flex;
gap: 4px;
align-items: center;
}
#magnets-content .item[data-rank] .name {
display: block;
padding-right: 260px;
}
.msf-overlay {
position: fixed;
inset: 0;
@@ -1043,6 +1070,16 @@ insertStyle(
insertStyle(mainCss);
insertStyle(subtitleModalCss);
const isEligibleDmmCoverCode = (value) => {
const raw = String(value || '').trim().toUpperCase();
if (!/^[A-Z0-9]{2,10}-\d{1,6}$/.test(raw)) return false;
const code = raw.replace(/\s+/g, '-').toUpperCase();
if (!/^[A-Z0-9]{2,10}-\d{1,6}$/.test(code) || /^FC2-/.test(code)) return false;
if (/^\d{6}[-_]\d{2,3}$/.test(code)) return false;
if (/^(DUGA|MYWIFE|HEYZO|PACO|10MU|1PONDO|CARIBBEAN|CARIB|TOKYO|GACHI|REAL|JUKU|AKA|NTR)-/.test(code)) return false;
return true;
};
export {
__defProp,
__typeError,
@@ -1052,6 +1089,7 @@ export {
isJavDb,
isJavBus,
isSearchPage,
isEligibleDmmCoverCode,
Status_RUNNING,
Status_SUCCESS,
Status_FAIL,
+10 -3
View File
@@ -6,6 +6,9 @@ import { Utils } from './core/utils.js';
import { GmHttp } from './core/gmHttp.js';
import { PluginManager } from './core/plugin-manager.js';
import { BasePlugin } from './core/base-plugin.js';
import {TabulatorFull as Tabulator} from 'tabulator-tables';
window.Tabulator = Tabulator;
import { javDbApi, buildSignature, apiUrl } from './api/javdb.js';
window.javDbApi = javDbApi;
@@ -29,7 +32,7 @@ import { TOP250Plugin } from './plugins/top250.js';
import { NavBarPlugin } from './plugins/nav-bar.js';
import { BusDetailPagePlugin } from './plugins/bus-detail-page.js';
import { BusPreviewVideoPlugin } from './plugins/bus-preview-video.js';
import {JavxyPreviewVideoPlugin} from './plugins/javxy-preview-video.js';
import {JavopenPreviewVideoPlugin} from './plugins/javopen-preview-video.js';
import { ImageRecognitionPlugin } from './plugins/image-recognition.js';
import { BusNavBarPlugin } from './plugins/bus-nav-bar.js';
import { RelatedPlugin } from './plugins/related.js';
@@ -52,6 +55,7 @@ import { DetailPageButtonPlugin } from './plugins/detail-page-button.js';
import { BlacklistPlugin } from './plugins/blacklist.js';
import { ListPagePlugin } from './plugins/list-page.js';
import { AutoPagePlugin } from './plugins/auto-page.js';
import {ArticlePagePlugin} from './plugins/article-page.js';
const _unsafeWindow = (() => typeof unsafeWindow !== 'undefined' ? unsafeWindow : window)();
@@ -861,6 +865,7 @@ const pluginManager = function() {
if (isJavDb) {
pluginManager2.register(ListPagePlugin);
pluginManager2.register(AutoPagePlugin);
pluginManager2.register(ArticlePagePlugin);
pluginManager2.register(Fc2Plugin);
pluginManager2.register(FoldCategoryPlugin);
pluginManager2.register(ListPageButtonPlugin);
@@ -879,7 +884,7 @@ const pluginManager = function() {
pluginManager2.register(DetailPageButtonPlugin);
pluginManager2.register(HighlightMagnetPlugin);
pluginManager2.register(PreviewVideoPlugin);
pluginManager2.register(JavxyPreviewVideoPlugin);
pluginManager2.register(JavopenPreviewVideoPlugin);
pluginManager2.register(FilterTitleKeywordPlugin);
pluginManager2.register(ActressInfoPlugin);
pluginManager2.register(OtherSitePlugin);
@@ -910,13 +915,15 @@ const pluginManager = function() {
pluginManager2.register(FilterTitleKeywordPlugin);
pluginManager2.register(HighlightMagnetPlugin);
pluginManager2.register(BusPreviewVideoPlugin);
pluginManager2.register(JavxyPreviewVideoPlugin);
pluginManager2.register(JavopenPreviewVideoPlugin);
pluginManager2.register(MagnetHubPlugin);
pluginManager2.register(ScreenShotPlugin);
pluginManager2.register(OtherSitePlugin);
pluginManager2.register(WangPan115TaskPlugin);
pluginManager2.register(TranslatePlugin);
pluginManager2.register(BlacklistPlugin);
pluginManager2.register(FavoriteActressesPlugin);
pluginManager2.register(FoldCategoryPlugin);
pluginManager2.register(TaskPlugin);
}
hostname.includes("javtrailers") && pluginManager2.register(JavTrailersPlugin);
+178
View File
@@ -0,0 +1,178 @@
import {BasePlugin} from '../core/base-plugin.js';
import {insertStyle} from '../core/constants.js';
class ArticlePagePlugin extends BasePlugin {
getName() {
return "ArticlePagePlugin";
}
handle() {
const href = window.location.href;
if (!href.includes("/articles")) {
return;
}
this.optimizeArticleLayout();
this.bindClick();
}
bindClick() {
// 使用 MutationObserver 监听 DOM 变化,尽早绑定事件
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.addedNodes.length > 0) {
this.attachClickHandler();
break;
}
}
});
// 立即尝试绑定一次
this.attachClickHandler();
// 监听整个 document 的 DOM 变化
observer.observe(document.body, {
childList: true,
subtree: true
});
}
attachClickHandler() {
const $articleList = $('.article-list');
if ($articleList.length === 0) return;
// 检查是否已经绑定过
if ($articleList.data('click-bound')) return;
$articleList.data('click-bound', true);
// 方法1:移除所有 a 标签的 href 属性,从根本上阻止跳转
$articleList.find('a').each(function () {
const $a = $(this);
const href = $a.attr('href');
if (href) {
$a.attr('data-href', href).removeAttr('href');
$a.css('cursor', 'pointer');
}
});
// 方法2:使用事件捕获阶段绑定(优先级最高)
document.querySelector('.article-list').addEventListener('click', (e) => {
// 简化选择器,closest 不支持 > div 这样的子选择器
let target = e.target.closest('.article-item, a, [data-href]');
// 如果没找到,尝试向上找 .article-list 的直接子元素
if (!target) {
let el = e.target;
const articleList = document.querySelector('.article-list');
while (el && el !== articleList) {
if (el.parentElement === articleList) {
target = el;
break;
}
el = el.parentElement;
}
}
if (!target) return;
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
const $target = $(target);
const $link = $target.find('a[data-href]').first().length > 0
? $target.find('a[data-href]').first()
: $target.closest('a[data-href]');
const articleUrl = $link.attr('data-href') || $target.attr('data-href');
if (!articleUrl) return;
layer.open({
type: 2,
title: $target.text().trim().substring(0, 50) || '文章详情',
content: articleUrl,
area: ['80%', '85%'],
shadeClose: true,
maxmin: true,
scrollbar: false
});
return false;
}, true); // true = 捕获阶段,优先级最高
}
optimizeArticleLayout() {
const css = `
/* 文章列表容器改为 grid 布局,每行3个 */
.article-list {
display: grid !important;
grid-template-columns: repeat(3, 1fr) !important;
gap: 20px !important;
}
/* 每个文章卡片改为纵向布局 */
.article-list > div {
display: flex !important;
flex-direction: column !important;
overflow: hidden !important;
border-radius: 8px !important;
box-shadow: 0 2px 8px rgba(0,0,0,0.1) !important;
transition: transform 0.2s, box-shadow 0.2s !important;
background: #fff !important;
}
.article-list > div:hover {
transform: translateY(-4px) !important;
box-shadow: 0 4px 16px rgba(0,0,0,0.15) !important;
}
/* 封面图片 */
.article-list > div img {
width: 100% !important;
height: auto !important;
aspect-ratio: 3/4 !important;
object-fit: cover !important;
border-radius: 8px 8px 0 0 !important;
display: block !important;
}
/* 内容区域 */
.article-list > div > div:not(:first-child) {
padding: 12px !important;
display: flex !important;
flex-direction: column !important;
gap: 8px !important;
}
/* 标题完全显示,不截断 */
.article-list > div > a,
.article-list > div > h3,
.article-list > div > p {
font-size: 14px !important;
line-height: 1.5 !important;
display: block !important;
overflow: visible !important;
text-overflow: clip !important;
-webkit-line-clamp: unset !important;
-webkit-box-orient: unset !important;
min-height: unset !important;
color: #333 !important;
word-break: break-word !important;
}
/* 底部信息 */
.article-list > div > div:last-child {
margin-top: auto !important;
font-size: 12px !important;
color: #999 !important;
display: flex !important;
justify-content: space-between !important;
align-items: center !important;
}
/* 标签 */
.article-list > div span[class*="tag"],
.article-list > div span[class*="label"] {
background: #f0f0f0 !important;
padding: 2px 8px !important;
border-radius: 4px !important;
font-size: 11px !important;
}
`;
insertStyle(css);
}
}
export {ArticlePagePlugin};
+8 -7
View File
@@ -55,9 +55,6 @@ class AutoPagePlugin extends BasePlugin {
const nextLink = document.querySelector(selector.nextPageSelector);
this.nextUrl = null == nextLink ? void 0 : nextLink.href;
this.hasMore = !!this.nextUrl;
setTimeout((() => {
this.checkLoad();
}), 1e3);
this.hasMore || this.setState("waterfall-no-more", "已经到底了");
}
async loadNextPage() {
@@ -128,7 +125,9 @@ class AutoPagePlugin extends BasePlugin {
clog.error("加载失败:", e);
this.setState("waterfall-error", "加载失败,点击重试");
} finally {
this.isLoading = !1;
setTimeout((() => {
this.isLoading = !1;
}), 600);
}
}
checkScrollPosition() {
@@ -145,13 +144,15 @@ class AutoPagePlugin extends BasePlugin {
}
}
checkLoad() {
if (!this.loader) return;
this.loader.getBoundingClientRect().top < window.innerHeight + this.preloadDistance && this.loadNextPage().then();
if (!this.loader || this.isLoading || !this.hasMore) return;
if (window.scrollY < 100) return;
const triggerDistance = 200;
this.loader.getBoundingClientRect().top < window.innerHeight + triggerDistance && this.loadNextPage().then();
}
async shouldDisablePaging() {
if (!window.isListPage) return !0;
await storageManager.getSetting("autoPage", YES);
return [ "search?q", "handlePlayback=1", "handleTop=1", "/want_watch_videos", "/watched_videos", "/advanced_search?type=100" ].some((path => currentHref.includes(path)));
return ["search?q", "/rankings/playback", "/rankings/top", "/want_watch_videos", "/watched_videos", "/advanced_search?type=100"].some((path => currentHref.includes(path)));
}
updatePageUrl_old(href) {
window.history.pushState({}, "", href);
+59 -2
View File
@@ -1,6 +1,19 @@
import { BasePlugin } from '../core/base-plugin.js';
import { __publicField, isJavBus, isJavDb, Status_FILTER, Status_FAVORITE, Status_HAS_DOWN, Status_HAS_WATCH, NO, YES } from '../core/constants.js';
import {
__publicField,
isJavBus,
isJavDb,
isEligibleDmmCoverCode,
Status_FILTER,
Status_FAVORITE,
Status_HAS_DOWN,
Status_HAS_WATCH,
NO,
YES
} from '../core/constants.js';
import { HotkeyManager } from './preview-video.js';
import {GM_xmlhttpRequest} from 'vite-plugin-monkey/dist/client';
import {getCover} from '../api/dmmrsc.js';
class DetailPageButtonPlugin extends BasePlugin {
getName() {
@@ -23,7 +36,7 @@ class DetailPageButtonPlugin extends BasePlugin {
}
async createMenuBtn() {
const pageInfo = this.getPageInfo(), carNum2 = pageInfo.carNum,
buttonsHtml = '\n <div style="margin: 10px auto; display: flex; justify-content: space-between; align-items: center; flex-wrap:wrap;gap: 20px;">\n <div style="display: flex; gap: 10px; flex-wrap:wrap;">\n <a id="filterBtn" class="menu-btn" style="width: 120px; background-color:#de3333; color: white; text-align: center; padding: 8px 0;">\n <span>🚫 屏蔽</span>\n </a>\n <a id="favoriteBtn" class="menu-btn" style="width: 120px; background-color:#25b1dc; color: white; text-align: center; padding: 8px 0;">\n <span>⭐ 收藏</span>\n </a>\n <a id="hasDownBtn" class="menu-btn" style="width: 120px; background-color:#7bc73b; color: white; text-align: center; padding: 8px 0;">\n <span>📥️ 已下载</span>\n </a>\n <a id="hasWatchBtn" class="menu-btn" style="width: 120px; background-color:#d7a80c; color: white; text-align: center; padding: 8px 0;">\n <span>🔍 已观看</span>\n </a>\n </div>\n \n <div style="display: flex; gap: 10px; flex-wrap:wrap;">\n <a id="enable-magnets-filter" class="menu-btn" style="width: 140px; background-color: #c2bd4c; color: white; text-align: center; padding: 8px 0;">\n <span id="magnets-span">排序与过滤</span>\n </a>\n <a id="magnetSearchBtn" class="menu-btn" style="width: 120px; background: linear-gradient(to right, rgb(245,140,1), rgb(84,161,29)); color: white; text-align: center; padding: 8px 0;">\n <span>磁力搜索</span>\n </a>\n <a id="subtitleSearchBtn" class="menu-btn" style="width: 120px; background: linear-gradient(to left, #375f7c, #2196F3); color: white; text-align: center; padding: 8px 0;">\n <span>字幕搜索</span>\n </a>\n <!--\n <a id="xunLeiSubtitleBtn" class="menu-btn" style="width: 120px; background: linear-gradient(to left, #375f7c, #2196F3); color: white; text-align: center; padding: 8px 0;">\n <span>字幕 (迅雷)</span>\n </a>\n <a id="search-subtitle-btn" class="menu-btn" style="width: 160px; background: linear-gradient(to bottom, #8d5656, rgb(196,159,91)); color: white; text-align: center; padding: 8px 0;">\n <span>字幕 (SubTitleCat)</span>\n </a>\n -->\n </div>\n </div>\n ';
buttonsHtml = '\n <div style="margin: 10px auto; display: flex; justify-content: space-between; align-items: center; flex-wrap:wrap;gap: 20px;">\n <div style="display: flex; gap: 10px; flex-wrap:wrap;">\n <a id="filterBtn" class="menu-btn" style="width: 120px; background-color:#de3333; color: white; text-align: center; padding: 8px 0;">\n <span>🚫 屏蔽</span>\n </a>\n <a id="favoriteBtn" class="menu-btn" style="width: 120px; background-color:#25b1dc; color: white; text-align: center; padding: 8px 0;">\n <span>⭐ 收藏</span>\n </a>\n <a id="hasDownBtn" class="menu-btn" style="width: 120px; background-color:#7bc73b; color: white; text-align: center; padding: 8px 0;">\n <span>📥️ 已下载</span>\n </a>\n <a id="hasWatchBtn" class="menu-btn" style="width: 120px; background-color:#d7a80c; color: white; text-align: center; padding: 8px 0;">\n <span>🔍 已观看</span>\n </a>\n </div>\n \n <div style="display: flex; gap: 10px; flex-wrap:wrap;">\n <a id="downloadCoverBtn" class="menu-btn" style="width: 140px; background-color: #9b59b6; color: white; text-align: center; padding: 8px 0;">\n <span>下载高清封面</span>\n </a>\n <a id="enable-magnets-filter" class="menu-btn" style="width: 140px; background-color: #c2bd4c; color: white; text-align: center; padding: 8px 0;">\n <span id="magnets-span">排序与过滤</span>\n </a>\n <a id="magnetSearchBtn" class="menu-btn" style="width: 120px; background: linear-gradient(to right, rgb(245,140,1), rgb(84,161,29)); color: white; text-align: center; padding: 8px 0;">\n <span>磁力搜索</span>\n </a>\n <a id="subtitleSearchBtn" class="menu-btn" style="width: 120px; background: linear-gradient(to left, #375f7c, #2196F3); color: white; text-align: center; padding: 8px 0;">\n <span>字幕搜索</span>\n </a>\n <!--\n <a id="xunLeiSubtitleBtn" class="menu-btn" style="width: 120px; background: linear-gradient(to left, #375f7c, #2196F3); color: white; text-align: center; padding: 8px 0;">\n <span>字幕 (迅雷)</span>\n </a>\n <a id="search-subtitle-btn" class="menu-btn" style="width: 160px; background: linear-gradient(to bottom, #8d5656, rgb(196,159,91)); color: white; text-align: center; padding: 8px 0;">\n <span>字幕 (SubTitleCat)</span>\n </a>\n -->\n </div>\n </div>\n ';
isJavDb && $(".tabs").after(buttonsHtml);
isJavBus && $("#mag-submit-show").before(buttonsHtml);
$("#favoriteBtn").on("click", (() => this.favoriteOne()));
@@ -87,6 +100,50 @@ class DetailPageButtonPlugin extends BasePlugin {
$("#subtitleSearchBtn").on("click", (() => {
window.JavPackSubtitle.openSearchModal({details: {code: carNum2}});
}));
$("#downloadCoverBtn").on("click", (async () => {
if (!isEligibleDmmCoverCode(carNum2)) {
show.error("当前番号不支持下载高清封面");
return;
}
const $btn = $("#downloadCoverBtn");
$btn.addClass("is-loading").prop("disabled", true);
try {
const coverUrl = await getCover(carNum2);
if (!coverUrl) {
show.error("未找到高清封面");
return;
}
const fileName = `${carNum2}-cover.jpg`;
GM_xmlhttpRequest({
method: 'GET',
url: coverUrl,
responseType: 'blob',
onload: (response) => {
if (response.status === 200) {
const blobUrl = URL.createObjectURL(response.response);
const a = document.createElement('a');
a.href = blobUrl;
a.download = fileName;
document.body.appendChild(a);
a.click();
setTimeout(() => {
document.body.removeChild(a);
URL.revokeObjectURL(blobUrl);
}, 100);
show.ok("封面下载已开始");
} else {
show.error(`封面下载失败: ${response.status}`);
}
},
onerror: () => show.error("封面下载网络错误"),
ontimeout: () => show.error("封面下载超时"),
});
} catch (e) {
show.error("获取封面信息失败");
} finally {
$btn.removeClass("is-loading").prop("disabled", false);
}
}));
window.JavPackSubtitle.preload115Matches(carNum2);
// $("#search-subtitle-btn").on("click", (event => utils.openPage(`https://subtitlecat.com/index.php?search=${carNum2}`, carNum2, !1, event)));
// $("#xunLeiSubtitleBtn").on("click", (() => this.searchXunLeiSubtitle(carNum2)));
+7
View File
@@ -5,6 +5,13 @@ class FavoriteActressesPlugin extends BasePlugin {
getName() {
return "FavoriteActressesPlugin";
}
async initCss() {
const settingObj = await storageManager.getSetting();
const tagNumber = settingObj.highlightedTagNumber || 1;
const tagColor = settingObj.highlightedTagColor || "#ce2222";
return `\n <style>\n .highlighted {\n border: ${tagNumber}px solid ${tagColor} !important;\n }\n </style>\n `;
}
async handle() {
this.bindEvent();
await this.highlightActress();
+5 -2
View File
@@ -7,7 +7,10 @@ class FoldCategoryPlugin extends BasePlugin {
}
async initCss() {
const settingObj = await storageManager.getSetting();
return `\n <style>\n #tags a.tag, .tags a.tag {\n position:relative;\n }\n .highlight-btn {\n position: absolute;\n top: -10px;\n right: -10px;\n background-color: #4CAF50;\n color: white;\n border: none;\n border-radius: 50%;\n width: 24px;\n height: 24px;\n font-size: 14px;\n line-height: 24px;\n text-align: center;\n cursor: pointer;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);\n display: none;\n z-index: 999;\n }\n /* 当父元素被高亮时,按钮变为其他颜色 */\n .highlighted .highlight-btn {\n background-color: #FF5722;\n }\n /* 高亮状态下的标签样式 */\n .highlighted {\n /* 浅黄色 */\n border: ${settingObj.highlightedTagNumber || 1}px solid ${settingObj.highlightedTagColor || "#ce2222"};\n }\n </style>\n `;
return `\n <style>\n #tags a.tag, .tags a.tag {\n position:relative;\n }\n .highlight-btn {\n position: absolute;\n top: -10px;\n right: -10px;\n background-color: #4CAF50;\n color: white;\n border: none;\n border-radius: 50%;\n width: 24px;\n height: 24px;\n font-size: 14px;\n line-height: 24px;\n text-align: center;\n cursor: pointer;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);\n display: none;\n z-index: 999;\n }\n /* 当父元素被高亮时,按钮变为其他颜色 */\n .highlighted .highlight-btn {\n background-color: #FF5722;\n }\n /* 高亮状态下的标签样式 */\n .highlighted {
/* 浅黄色 */
border: ${settingObj.highlightedTagNumber || 1}px solid ${settingObj.highlightedTagColor || "#ce2222"} !important;
}\n </style>\n `;
}
async handle() {
if (window.isListPage && !currentHref.includes("advanced_search")) {
@@ -82,4 +85,4 @@ class FoldCategoryPlugin extends BasePlugin {
}
export { FoldCategoryPlugin };
export { FoldCategoryPlugin };
+4 -2
View File
@@ -222,7 +222,9 @@ class HistoryPlugin extends BasePlugin {
ajaxURL: "queryRealm",
dataLoader: !1,
ajaxRequestFunc: async (url, ajaxConfig, params) => {
const page = params.page, size = params.size, sort = params.sort;
const page = (null == params ? void 0 : params.page) || 1,
size = (null == params ? void 0 : params.size) || 50,
sort = (null == params ? void 0 : params.sort) || [];
return await this.getDataList(page, size, sort);
},
dataReceiveParams: {
@@ -476,4 +478,4 @@ class HistoryPlugin extends BasePlugin {
}
export { HistoryPlugin };
export { HistoryPlugin };
+4 -3
View File
@@ -14,7 +14,7 @@ class HitShowPlugin extends BasePlugin {
$('a[href*="rankings/playback"]').on("click", (event => {
event.preventDefault();
event.stopPropagation();
window.location.href = "/advanced_search?handlePlayback=1&period=daily";
window.location.href = "/rankings/playback";
}));
this.handlePlayback().then();
}
@@ -29,7 +29,7 @@ class HitShowPlugin extends BasePlugin {
this.$contentBox.append('<div class="movie-list h cols-4 vcols-8" style="margin-top: 10px"></div>');
}
async handlePlayback() {
if (!window.location.href.includes("handlePlayback=1")) return;
if (!window.location.href.includes("rankings/playback")) return;
let period = new URLSearchParams(window.location.search).get("period");
this.toolBar(period);
this.hookPage();
@@ -40,6 +40,7 @@ class HitShowPlugin extends BasePlugin {
const movies = await javDbApi.playback(period);
let moviesHtml = javDbApi.markDataListHtml(movies);
$movieBox.html(moviesHtml);
this.pluginManager.getBean("ListPagePlugin").doFilter();
} catch (e) {
clog.error("发生错误:", e);
} finally {
@@ -47,7 +48,7 @@ class HitShowPlugin extends BasePlugin {
}
}
toolBar(period) {
let conditionHtml = `\n <div class="button-group" style="margin-top:18px">\n <div class="buttons has-addons" id="conditionBox">\n <a style="padding:18px 18px !important;" class="button is-small ${"daily" === period ? "is-info" : ""}" href="/advanced_search?handlePlayback=1&period=daily">日榜</a>\n <a style="padding:18px 18px !important;" class="button is-small ${"weekly" === period ? "is-info" : ""}" href="/advanced_search?handlePlayback=1&period=weekly">周榜</a>\n <a style="padding:18px 18px !important;" class="button is-small ${"monthly" === period ? "is-info" : ""}" href="/advanced_search?handlePlayback=1&period=monthly">月榜</a>\n </div>\n </div>\n `;
let conditionHtml = `\n <div class="button-group" style="margin-top:18px">\n <div class="buttons has-addons" id="conditionBox">\n <a style="padding:18px 18px !important;" class="button is-small ${"daily" === period ? "is-info" : ""}" href="/rankings/playback?period=daily">日榜</a>\n <a style="padding:18px 18px !important;" class="button is-small ${"weekly" === period ? "is-info" : ""}" href="/rankings/playback?period=weekly">周榜</a>\n <a style="padding:18px 18px !important;" class="button is-small ${"monthly" === period ? "is-info" : ""}" href="/rankings/playback?period=monthly">月榜</a>\n </div>\n </div>\n `;
this.$contentBox.append(conditionHtml);
}
getStarRating(score) {
+8 -1
View File
@@ -162,10 +162,17 @@ class ImageRecognitionPlugin extends BasePlugin {
type: mimeType
}), formData = new FormData;
formData.append("image", blob);
const idKey = "jhs_imgurClientId";
let clientId = localStorage.getItem(idKey);
if (!clientId) {
clientId = window.prompt("请输入您自己的Imgur Client-ID(可前往 https://api.imgur.com/oauth2/addclient 免费申请)用于图片上传搜索:");
if (!clientId) throw new Error("未提供Imgur Client-ID,无法上传图片");
localStorage.setItem(idKey, clientId);
}
const response = await fetch("https://api.imgur.com/3/image", {
method: "POST",
headers: {
Authorization: "Client-ID d70305e7c3ac5c6"
Authorization: `Client-ID ${clientId}`
},
body: formData
}), data = await response.json();
@@ -1,11 +1,11 @@
import {BasePlugin} from '../core/base-plugin.js';
import {__publicField, isJavBus, isJavDb, qualityOptions} from '../core/constants.js';
import {isM3U8Url, attachHlsToVideo, destroyHls, loadHlsLibrary, getHlsClass} from '../lib/hls-runtime.js';
import {getJavxyVideoUrls} from '../api/javxy.js';
import {getTrailer} from '../api/dmmrsc.js';
class JavxyPreviewVideoPlugin extends BasePlugin {
class JavopenPreviewVideoPlugin extends BasePlugin {
getName() {
return "JavxyPreviewVideoPlugin";
return "JavopenPreviewVideoPlugin";
}
_attachVideoSrc(videoEl, src) {
@@ -17,20 +17,13 @@ class JavxyPreviewVideoPlugin extends BasePlugin {
if (HlsClass) {
attachHlsToVideo(videoEl, src).catch((err) => {
clog.error('视频预览 HLS 加载失败:', err);
videoEl.src = src;
videoEl.load && videoEl.load();
});
} else {
loadHlsLibrary().then((HlsClass) => {
if (videoEl && videoEl.isConnected && HlsClass) {
attachHlsToVideo(videoEl, src).catch((err) => {
clog.error('视频预览 HLS 延迟加载失败:', err);
videoEl.src = src;
videoEl.load && videoEl.load();
});
} else if (videoEl && videoEl.isConnected) {
videoEl.src = src;
videoEl.load && videoEl.load();
}
});
}
@@ -51,38 +44,38 @@ class JavxyPreviewVideoPlugin extends BasePlugin {
async initCss() {
return `
.jhs-javxy-video-modal {
.jhs-javopen-video-modal {
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.95); z-index: 12345700;
display: flex; justify-content: center; align-items: center;
opacity: 0; visibility: hidden; transition: opacity 0.2s;
}
.jhs-javxy-video-modal.is-open { opacity: 1; visibility: visible; }
.jhs-javxy-video-modal-inner {
.jhs-javopen-video-modal.is-open { opacity: 1; visibility: visible; }
.jhs-javopen-video-modal-inner {
display: flex; flex-direction: column; align-items: center;
gap: 12px; max-width: 90vw; max-height: 90vh;
}
.jhs-javxy-video-wrapper {
.jhs-javopen-video-wrapper {
width: 80vw; max-height: 80vh; aspect-ratio: 16/9;
background: #000; position: relative;
}
.jhs-javxy-video-wrapper video {
.jhs-javopen-video-wrapper video {
position: absolute; top: 0; left: 0; width: 100%; height: 100%;
}
.jhs-javxy-quality-bar {
.jhs-javopen-quality-bar {
display: flex; gap: 8px; flex-wrap: wrap; justify-content: center;
}
.jhs-javxy-quality-btn {
.jhs-javopen-quality-btn {
min-width: 60px; padding: 5px 10px; font-size: 13px;
background: rgba(255,255,255,0.2); color: #fff;
border: 1px solid rgba(255,255,255,0.5); border-radius: 4px;
cursor: pointer; transition: background 0.2s;
}
.jhs-javxy-quality-btn:hover { background: rgba(255,255,255,0.4); }
.jhs-javxy-quality-btn.active {
.jhs-javopen-quality-btn:hover { background: rgba(255,255,255,0.4); }
.jhs-javopen-quality-btn.active {
background: #1890ff; border-color: #096dd9; font-weight: bold;
}
.jhs-javxy-loading {
.jhs-javopen-loading {
color: #fff; font-size: 16px; text-align: center;
}
`;
@@ -94,10 +87,10 @@ class JavxyPreviewVideoPlugin extends BasePlugin {
const carNum = this.getPageInfo().carNum;
if (!carNum) return;
this._addJavxyButton(carNum);
this._addJavopenButton(carNum);
}
_addJavxyButton(carNum) {
_addJavopenButton(carNum) {
let $target = null;
if (isJavDb) {
@@ -128,7 +121,7 @@ class JavxyPreviewVideoPlugin extends BasePlugin {
e.stopPropagation();
$btn.css("pointer-events", "none").find("span").text("...");
try {
await this._openJavxyPlayer(carNum);
await this._openJavopenPlayer(carNum);
} finally {
$btn.css("pointer-events", "").find("span").text("视频预览");
}
@@ -136,23 +129,23 @@ class JavxyPreviewVideoPlugin extends BasePlugin {
$target.after($btn);
}
async _openJavxyPlayer(carNum) {
if ($("#jhs-javxy-video-modal").length === 0) {
async _openJavopenPlayer(carNum) {
if ($("#jhs-javopen-video-modal").length === 0) {
$("body").append(`
<div id="jhs-javxy-video-modal" class="jhs-javxy-video-modal">
<div class="jhs-javxy-video-modal-inner">
<div class="jhs-javxy-video-wrapper">
<video id="jhs-javxy-video" controls playsinline></video>
<div id="jhs-javopen-video-modal" class="jhs-javopen-video-modal">
<div class="jhs-javopen-video-modal-inner">
<div class="jhs-javopen-video-wrapper">
<video id="jhs-javopen-video" controls playsinline></video>
</div>
<div class="jhs-javxy-quality-bar"></div>
<div class="jhs-javopen-quality-bar"></div>
</div>
</div>
`);
const $modal = $("#jhs-javxy-video-modal");
const $modal = $("#jhs-javopen-video-modal");
$modal.on("click", (e) => {
if (e.target.id === "jhs-javxy-video-modal") {
if (e.target.id === "jhs-javopen-video-modal") {
$modal.removeClass("is-open");
const videoEl = document.getElementById("jhs-javxy-video");
const videoEl = document.getElementById("jhs-javopen-video");
if (videoEl) {
destroyHls(videoEl);
videoEl.pause();
@@ -162,7 +155,7 @@ class JavxyPreviewVideoPlugin extends BasePlugin {
$(document).on("keydown", (e) => {
if (e.key === "Escape" && $modal.hasClass("is-open")) {
$modal.removeClass("is-open");
const videoEl = document.getElementById("jhs-javxy-video");
const videoEl = document.getElementById("jhs-javopen-video");
if (videoEl) {
destroyHls(videoEl);
videoEl.pause();
@@ -171,23 +164,23 @@ class JavxyPreviewVideoPlugin extends BasePlugin {
});
}
const $modal = $("#jhs-javxy-video-modal");
const $wrapper = $modal.find(".jhs-javxy-video-wrapper");
const $qualityBar = $modal.find(".jhs-javxy-quality-bar");
$wrapper.html('<div class="jhs-javxy-loading">视频预览加载中...</div>');
const $modal = $("#jhs-javopen-video-modal");
const $wrapper = $modal.find(".jhs-javopen-video-wrapper");
const $qualityBar = $modal.find(".jhs-javopen-quality-bar");
$wrapper.html('<div class="jhs-javopen-loading">视频预览加载中...</div>');
$qualityBar.empty();
$modal.addClass("is-open");
let videoMap = null;
try {
videoMap = await getJavxyVideoUrls(carNum);
videoMap = await getTrailer(carNum);
} catch (err) {
$wrapper.html(`<div class="jhs-javxy-loading">视频预览请求失败: ${err.message}</div>`);
$wrapper.html(`<div class="jhs-javopen-loading">视频预览请求失败: ${err.message}</div>`);
return;
}
if (!videoMap || Object.keys(videoMap).length === 0) {
$wrapper.html('<div class="jhs-javxy-loading">未找到可用的视频源</div>');
$wrapper.html('<div class="jhs-javopen-loading">未找到可用的视频源</div>');
return;
}
@@ -195,10 +188,10 @@ class JavxyPreviewVideoPlugin extends BasePlugin {
const defaultQuality = this._selectDefaultQuality(qualityList, '720p');
const defaultUrl = videoMap[defaultQuality];
$wrapper.html(`<video id="jhs-javxy-video" controls playsinline>
$wrapper.html(`<video id="jhs-javopen-video" controls playsinline>
<source src="${defaultUrl}" />
</video>`);
const videoEl = document.getElementById("jhs-javxy-video");
const videoEl = document.getElementById("jhs-javopen-video");
if (!videoEl) return;
if (isM3U8Url(defaultUrl)) {
@@ -209,13 +202,13 @@ class JavxyPreviewVideoPlugin extends BasePlugin {
qualityOptions.forEach((option) => {
const url = videoMap[option.quality];
if (url) {
buttonsHtml += `<button class="jhs-javxy-quality-btn${option.quality === defaultQuality ? ' active' : ''}"
buttonsHtml += `<button class="jhs-javopen-quality-btn${option.quality === defaultQuality ? ' active' : ''}"
data-quality="${option.quality}" data-video-src="${url}">${option.text}</button>`;
}
});
$qualityBar.html(buttonsHtml);
$qualityBar.off("click").on("click", ".jhs-javxy-quality-btn", (e) => {
$qualityBar.off("click").on("click", ".jhs-javopen-quality-btn", (e) => {
const $btn = $(e.currentTarget);
if ($btn.hasClass("active")) return;
const src = $btn.attr("data-video-src");
@@ -228,7 +221,7 @@ class JavxyPreviewVideoPlugin extends BasePlugin {
}
videoEl.play().catch(() => {
});
$qualityBar.find(".jhs-javxy-quality-btn").removeClass("active");
$qualityBar.find(".jhs-javopen-quality-btn").removeClass("active");
$btn.addClass("active");
});
@@ -236,4 +229,4 @@ class JavxyPreviewVideoPlugin extends BasePlugin {
}
}
export {JavxyPreviewVideoPlugin};
export {JavopenPreviewVideoPlugin};
+14 -1
View File
@@ -151,7 +151,20 @@ class ListPagePlugin extends BasePlugin {
await Promise.all(movieList.map((async ele => {
let $box2 = $(ele);
if (isJavBus && $box2.find(".avatar-box").length > 0) return;
const {carNum: carNum2, title: title} = this.getBoxCarInfo($box2), {filter: filter, favorite: favorite, hasDown: hasDown, hasWatch: hasWatch} = carNumSets, isFavorite = favorite.has(carNum2), isHasDown = hasDown.has(carNum2), isHasWatch = hasWatch.has(carNum2), isFiltered = filter.has(carNum2), isFilterActorMale = actorCarNumToNameMap.has(carNum2), isFilterActorFemale = actressCarNumToNameMap.has(carNum2), isFilterActor = isFilterActorMale || isFilterActorFemale, foundKeyword = filterKeywordList.find((kw => title.includes(kw) || carNum2.startsWith(kw))), isFilterKeyword = !!foundKeyword;
const boxInfo = this.getBoxCarInfo($box2);
if (!boxInfo) return;
const {carNum: carNum2, title: title} = boxInfo, {
filter: filter,
favorite: favorite,
hasDown: hasDown,
hasWatch: hasWatch
} = carNumSets, isFavorite = favorite.has(carNum2), isHasDown = hasDown.has(carNum2),
isHasWatch = hasWatch.has(carNum2), isFiltered = filter.has(carNum2),
isFilterActorMale = actorCarNumToNameMap.has(carNum2),
isFilterActorFemale = actressCarNumToNameMap.has(carNum2),
isFilterActor = isFilterActorMale || isFilterActorFemale,
foundKeyword = filterKeywordList.find((kw => title.includes(kw) || carNum2.startsWith(kw))),
isFilterKeyword = !!foundKeyword;
if (!isSearchPage) {
let shouldHide = settings.showFavoriteItem === NO && isFavorite || settings.showHasDownItem === NO && isHasDown || settings.showHasWatchItem === NO && isHasWatch || settings.showFilterItem === NO && isFiltered && !(isFavorite || isHasDown || isHasWatch) || settings.showFilterActorItem === NO && isFilterActor || settings.showFilterKeywordItem === NO && isFilterKeyword;
if ($box2.attr("data-movieShowType") !== settings.movieShowType) {
+15 -4
View File
@@ -580,6 +580,8 @@ class MagnetHubPlugin extends BasePlugin {
}
searchEngine($container, $sortSelect, engine, keyword) {
this.currentEngine = engine;
this.currentKeyword = keyword;
$container.html(`<div class="magnet-loading">正在从 ${engine.name} 搜索 "${keyword}"...</div>`);
const cacheKey = `${engine.name}_${keyword}`;
sessionStorage.getItem(cacheKey);
@@ -597,11 +599,11 @@ class MagnetHubPlugin extends BasePlugin {
$sortSelect.prop("disabled", results.length === 0);
this.displayResults($container, results, engine.name);
} catch (e) {
$container.html(`<div class="magnet-error">解析 ${engine.name} 结果失败: ${e.message}</div>`);
this.showError($container, `解析 ${engine.name} 结果失败: ${e.message}`);
}
},
onerror: error => {
$container.html(`<div class="magnet-error">从 ${engine.name} 获取数据失败: ${error.statusText}</div>`);
this.showError($container, `从 ${engine.name} 获取数据失败: ${error.statusText}`);
}
}));
}
@@ -611,6 +613,15 @@ class MagnetHubPlugin extends BasePlugin {
}));
}
showError($container, message) {
$container.html(`<div class="magnet-error">${message} <a id="retrySearch" href="#">🔄 刷新</a></div>`);
const $select = $container.closest(".magnet-container").find(".magnet-sort-select");
$container.find("#retrySearch").on("click", (e => {
e.preventDefault();
this.searchEngine($container, $select, this.currentEngine, this.currentKeyword);
}));
}
displayResults($container, results, engineName) {
$container.empty();
const sortMode = localStorage.getItem("jhs_magnetSort") || "size";
@@ -1000,11 +1011,11 @@ class MagnetHubPlugin extends BasePlugin {
$sortSelect.prop("disabled", results.length === 0);
_this.displayResults($container, results, engine.name);
} catch (e) {
$container.html(`<div class="magnet-error">解析 ${engine.name} 结果失败: ${e.message}</div>`);
_this.showError($container, `解析 ${engine.name} 结果失败: ${e.message}`);
}
},
onerror: error => {
$container.html(`<div class="magnet-error">从 ${engine.name} 获取数据失败: ${error.statusText}</div>`);
_this.showError($container, `从 ${engine.name} 获取数据失败: ${error.statusText}`);
}
});
}
+16 -2
View File
File diff suppressed because one or more lines are too long
+19 -6
View File
@@ -29,6 +29,9 @@ class TOP250Plugin extends BasePlugin {
$("h2.section-title").contents().first().replaceWith("Top250");
$(".empty-message").remove();
$(".section .container .box").remove();
$(".section .container .movie-list").remove();
$(".section .container .pagination").remove();
$(".section .container select").remove();
$("#sort-toggle-btn").remove();
this.$contentBox.append('<div class="tool-box" style="margin-top: 10px"></div>');
this.$contentBox.append('<div class="movie-list h cols-4 vcols-8" style="margin-top: 10px"></div>');
@@ -56,9 +59,16 @@ class TOP250Plugin extends BasePlugin {
}));
}
async handleTop() {
if (!window.location.href.includes("handleTop=1")) return;
if (!window.location.href.includes("/rankings/top")) return;
const urlParams = new URLSearchParams(window.location.search);
let type = urlParams.get("handleType") || "all", type_value = urlParams.get("type_value") || "";
let t = urlParams.get("t") || "", type = "all", type_value = "";
if (/^y\d+$/.test(t)) {
type = "year";
type_value = t.substring(1);
} else if ("" !== t) {
type = "video_type";
type_value = t;
}
this.has_cnsub = urlParams.get("has_cnsub") || "";
let page = urlParams.get("page") || 1;
this.toolBar(type, type_value, page);
@@ -80,6 +90,7 @@ class TOP250Plugin extends BasePlugin {
const filter_movies = dataList.filter((item => "1" === this.has_cnsub ? item.has_cnsub : "0" !== this.has_cnsub || !item.has_cnsub));
let moviesHtml = javDbApi.markDataListHtml(filter_movies);
$movieBox.html(moviesHtml);
this.pluginManager.getBean("ListPagePlugin").doFilter();
} else {
clog.error(res);
$movieBox.html(`<h3>${message}</h3>`);
@@ -101,9 +112,10 @@ class TOP250Plugin extends BasePlugin {
$(".pagination-list li a").each((function() {
parseInt($(this).text()) > 5 && $(this).closest("li").remove();
}));
const currentTParam = "all" === type ? "" : "year" === type ? `y${type_value}` : type_value;
let yearHtml = "";
for (let year = (new Date).getFullYear(); year >= 2008; year--) yearHtml += `\n <a style="padding:18px 18px !important;" \n class="button is-small ${type_value === year.toString() ? "is-info" : ""}" \n href="/advanced_search?handleTop=1&handleType=year&type_value=${year}&has_cnsub=${this.has_cnsub}">\n ${year}\n </a>\n `;
let conditionHtml = `\n <div class="button-group">\n <div class="buttons has-addons" id="conditionBox" style="margin-bottom: 0!important;">\n <a style="padding:18px 18px !important;" class="button is-small ${"all" === type ? "is-info" : ""}" href="/advanced_search?handleTop=1&handleType=all&type_value=&has_cnsub=${this.has_cnsub}">全部</a>\n <a style="padding:18px 18px !important;" class="button is-small ${"0" === type_value ? "is-info" : ""}" href="/advanced_search?handleTop=1&handleType=video_type&type_value=0&has_cnsub=${this.has_cnsub}">有码</a>\n <a style="padding:18px 18px !important;" class="button is-small ${"1" === type_value ? "is-info" : ""}" href="/advanced_search?handleTop=1&handleType=video_type&type_value=1&has_cnsub=${this.has_cnsub}">无码</a>\n <a style="padding:18px 18px !important;" class="button is-small ${"2" === type_value ? "is-info" : ""}" href="/advanced_search?handleTop=1&handleType=video_type&type_value=2&has_cnsub=${this.has_cnsub}">欧美</a>\n <a style="padding:18px 18px !important;" class="button is-small ${"3" === type_value ? "is-info" : ""}" href="/advanced_search?handleTop=1&handleType=video_type&type_value=3&has_cnsub=${this.has_cnsub}">Fc2</a>\n \n <a style="padding:18px 18px !important;margin-left: 50px" class="button is-small ${"1" === this.has_cnsub ? "is-info" : ""}" data-cnsub-value="1">含中字磁鏈</a>\n <a style="padding:18px 18px !important;" class="button is-small ${"0" === this.has_cnsub ? "is-info" : ""}" data-cnsub-value="0">无字幕</a>\n <a style="padding:18px 18px !important;" class="button is-small" data-cnsub-value="">重置</a>\n </div>\n \n <div class="buttons has-addons" id="conditionBox">\n ${yearHtml}\n </div>\n </div>\n `;
for (let year = (new Date).getFullYear(); year >= 2008; year--) yearHtml += `\n <a style="padding:18px 18px !important;" \n class="button is-small ${type_value === year.toString() ? "is-info" : ""}" \n href="/rankings/top?t=y${year}&has_cnsub=${this.has_cnsub}">\n ${year}\n </a>\n `;
let conditionHtml = `\n <div class="button-group">\n <div class="buttons has-addons" id="conditionBox" style="margin-bottom: 0!important;">\n <a style="padding:18px 18px !important;" class="button is-small ${"all" === type ? "is-info" : ""}" href="/rankings/top?has_cnsub=${this.has_cnsub}">全部</a>\n <a style="padding:18px 18px !important;" class="button is-small ${"0" === type_value ? "is-info" : ""}" href="/rankings/top?t=0&has_cnsub=${this.has_cnsub}">有码</a>\n <a style="padding:18px 18px !important;" class="button is-small ${"1" === type_value ? "is-info" : ""}" href="/rankings/top?t=1&has_cnsub=${this.has_cnsub}">无码</a>\n <a style="padding:18px 18px !important;" class="button is-small ${"2" === type_value ? "is-info" : ""}" href="/rankings/top?t=2&has_cnsub=${this.has_cnsub}">欧美</a>\n <a style="padding:18px 18px !important;" class="button is-small ${"3" === type_value ? "is-info" : ""}" href="/rankings/top?t=3&has_cnsub=${this.has_cnsub}">Fc2</a>\n \n <a style="padding:18px 18px !important;margin-left: 50px" class="button is-small ${"1" === this.has_cnsub ? "is-info" : ""}" data-cnsub-value="1">含中字磁鏈</a>\n <a style="padding:18px 18px !important;" class="button is-small ${"0" === this.has_cnsub ? "is-info" : ""}" data-cnsub-value="0">无字幕</a>\n <a style="padding:18px 18px !important;" class="button is-small" data-cnsub-value="">重置</a>\n </div>\n \n <div class="buttons has-addons" id="conditionBox">\n ${yearHtml}\n </div>\n </div>\n `;
this.$contentBox.append(conditionHtml);
$("a[data-cnsub-value]").on("click", (event => {
const cnsubValue = $(event.currentTarget).data("cnsub-value");
@@ -134,7 +146,8 @@ class TOP250Plugin extends BasePlugin {
type = "video_type";
type_value = t;
}
let url = `/advanced_search?handleTop=1&handleType=${type}&type_value=${type_value}`;
let tParam = "year" === type ? `y${type_value}` : "all" === type ? "" : type_value;
let url = `/rankings/top${tParam ? `?t=${tParam}` : ""}`;
event && (event.ctrlKey || event.metaKey) ? GM_openInTab(window.location.origin + url, {
insert: 0
}) : window.location.href = url;
@@ -167,7 +180,7 @@ class TOP250Plugin extends BasePlugin {
await localStorage.setItem("jhs_appAuthorization", token);
show.ok("登录成功");
layer.close(index);
window.location.href = "/advanced_search?handleTop=1&period=daily";
window.location.href = "/rankings/top";
}
}
})).catch((err => {
+154 -7
View File
@@ -18,30 +18,49 @@ class WangPan115TaskPlugin extends BasePlugin {
}
async handle() {
$(".buttons button[data-clipboard-text*='magnet:']").each(((i, el) => {
$(el).parent().append($("<button>").text("115离线下载").addClass("button is-info is-small").click((async event => {
const $copyBtn = $(el);
const magnet = $copyBtn.attr("data-clipboard-text");
$copyBtn.css("margin-right", (parseInt($copyBtn.css("margin-right")) - 4) + "px");
const $downloadBtn = $copyBtn.siblings("a.button, button").first();
if ($downloadBtn.length) {
$downloadBtn.css("margin-right", (parseInt($downloadBtn.css("margin-right")) + 3) + "px");
}
$("<button>").text("验车").addClass("button is-small").attr("style", "background-color:#2b6cb0 !important;color:#fff !important;border:none !important;cursor:pointer;border-radius:4px;font-size:12px;padding:2px 8px;").click((event => {
event.stopPropagation();
event.preventDefault();
this.getBean("MagnetHubPlugin").checkWhatslink(magnet);
})).appendTo($copyBtn.parent());
$("<button>").text("115离线下载").addClass("button is-small").attr("style", "background-color:#4a90e2 !important;color:#fff !important;border:none !important;cursor:pointer;border-radius:4px;font-size:12px;padding:2px 8px;").click((async event => {
event.stopPropagation();
event.preventDefault();
let loadObj = loading();
try {
await this.handleAddTask($(el).attr("data-clipboard-text"));
await this.handleAddTask(magnet);
} catch (e) {
show.error("发生错误:" + e);
console.error(e);
} finally {
loadObj.close();
}
})));
})).appendTo($copyBtn.parent());
}));
isJavBus && window.isDetailPage && utils.loopDetector((() => $("#magnet-table td a").length > 0), (() => {
this.bus115Down();
}));
}
async bus115Down() {
const $headerRow = $("#magnet-table tr").first();
$("#magnet-table tr").each(((i, row) => {
if (row === $headerRow[0]) return;
const magnetLink = $(row).find("td:nth-child(1) a").attr("href");
if (magnetLink && magnetLink.includes("magnet:")) {
const actionCell = $("<td>").addClass("action-cell");
$("<button>").text("115离线下载").addClass("button is-info is-small").click((async event => {
$("<button>").text("验车").addClass("button is-small").attr("style", "background-color:#2b6cb0 !important;color:#fff !important;border:none !important;margin-right:4px;cursor:pointer;border-radius:4px;font-size:12px;padding:2px 8px;").click((event => {
event.stopPropagation();
event.preventDefault();
this.getBean("MagnetHubPlugin").checkWhatslink(magnetLink);
})).appendTo(actionCell);
$("<button>").text("115离线下载").addClass("button is-small").attr("style", "background-color:#4a90e2 !important;color:#fff !important;border:none !important;cursor:pointer;border-radius:4px;font-size:12px;padding:2px 8px;").click((async event => {
event.stopPropagation();
event.preventDefault();
let loadObj = loading();
@@ -57,9 +76,109 @@ class WangPan115TaskPlugin extends BasePlugin {
$(row).append(actionCell);
}
}));
$("#magnet-table tbody").length > 0 && $("#magnet-table tbody tr").append($("<td>").text("操作"));
if ($headerRow.find("td, th").last().text().trim() !== "操作") {
$headerRow.append($("<td>").text("操作"));
}
}
static sanitizeDirName(str) {
if (!str) return "";
return str.replace(/[\\/:*?"<>|]/g, "").replace(/\s+/g, " ").trim();
}
static parseOfflineDirTemplate(str, params) {
if (!str || !params) return str;
return str.replace(/\$\{(\w+)\}/g, (match, key) => {
if (!params.hasOwnProperty(key)) return match;
const value = params[key] || "";
return WangPan115TaskPlugin.sanitizeDirName(String(value));
});
}
static async ensureDir(dirName, pid = 0) {
const result = await gmHttp.postFileFormData("https://webapi.115.com/files/add", {
pid: pid,
cname: dirName
});
if (result && result.file_id) return result.file_id;
const searchRes = await searchFiles(dirName, 0, 1, {fc: 1});
if (searchRes && searchRes.data && searchRes.data.length > 0) {
const match = searchRes.data.find(d => d.pid === pid || d.pid === String(pid));
if (match) return match.fid || match.file_id;
}
return null;
}
static async registerDirAsDownPath(fileId, dirName) {
await gmHttp.postFileFormData("https://webapi.115.com/offine/downpath", {
file_id: fileId
});
const list = await getDownPathList();
const created = list ? list.find(d => d.file_id === fileId || String(d.file_id) === String(fileId)) : null;
return created ? created.id : null;
}
static async createMultiLevelDownPath(templatePath) {
const segments = templatePath.split("/").filter(s => s.trim());
if (segments.length === 0) return null;
let pid = 0;
let fileId = null;
for (const seg of segments) {
fileId = await WangPan115TaskPlugin.ensureDir(seg.trim(), pid);
if (!fileId) {
console.error("[115离线] 创建目录失败:", seg);
return null;
}
pid = fileId;
}
const lastDirName = segments[segments.length - 1].trim();
const downPathId = await WangPan115TaskPlugin.registerDirAsDownPath(fileId, lastDirName);
console.log("[115离线] 多级目录创建完成:", templatePath, "→ downPathId=" + downPathId);
return downPathId;
}
getPageInfoForOffline() {
const baseInfo = this.getPageInfo();
const carNum = baseInfo.carNum || "";
let title = "", maker = "", actor = "";
if (isJavBus) {
title = $("h3").first().text().trim();
maker = $('span.header:contains("製作商:")').parent("p").text().replace("製作商:", "").trim() ||
$('span.header:contains("制作商:")').parent("p").text().replace("制作商:", "").trim();
const actorEl = $('span.genre a[href*="/star/"]').first();
actor = actorEl.length ? actorEl.text().trim() : "";
} else {
title = $(".current-title").first().text().trim() || $("h2.title.is-4 .current-title").first().text().trim() || $(".movie-title").first().text().trim();
maker = $('strong:contains("片商:")').parent().find(".value").text().trim() ||
$('strong:contains("片商:")').parent().find(".value").text().trim();
actor = $('.actor-female').first().text().trim() || $('strong:contains("演員:"), strong:contains("演员:")').parent().find('.value a').first().text().trim() || "";
}
const now = new Date();
if (carNum && title) {
const upperTitle = title.toUpperCase();
const upperCode = carNum.toUpperCase();
if (upperTitle.startsWith(upperCode)) {
title = title.substring(carNum.length).replace(/^[\s\-_]+/, "").trim();
}
}
console.log("[115离线] 页面信息提取:", {code: carNum, title, maker, actor});
return {
code: carNum,
title: title,
maker: maker,
actor: actor,
date: utils.formatDate(now).split(" ")[0]
};
}
async getSavePathId(nyName) {
const enableCustom = await storageManager.getSetting("enable115OfflineFormat", NO);
if (enableCustom === YES) {
const template = await storageManager.getSetting("offlineFolderFormat", "");
if (template && template.includes("${")) {
const pageInfo = this.getPageInfoForOffline();
return WangPan115TaskPlugin.parseOfflineDirTemplate(template, pageInfo);
}
}
let savePath115 = await storageManager.getSetting("savePath115", "云下载");
nyName && (savePath115 = savePath115.replaceAll("{ny}", nyName));
savePath115 = savePath115.replaceAll("{date}", utils.formatDate(new Date));
@@ -80,7 +199,35 @@ class WangPan115TaskPlugin extends BasePlugin {
});
return;
}
const sign = singInfo.sign, time = singInfo.time, userId = this.getUserId(), result = await (async (magnet, wp_path_id = "", uid, sign, time) => {
const sign = singInfo.sign, time = singInfo.time, userId = this.getUserId();
let wp_path_id = savePath || "";
if (!wp_path_id) {
const fullPath = await this.getSavePathId();
console.log("[115离线] 目录模板解析结果:", fullPath);
if (fullPath) {
if (fullPath.includes("/")) {
console.log("[115离线] 检测到多级目录,逐级创建:", fullPath);
const newId = await WangPan115TaskPlugin.createMultiLevelDownPath(fullPath);
if (newId) wp_path_id = newId;
} else {
const downPathList = await getDownPathList();
if (downPathList && downPathList.length > 0) {
const matched = downPathList.find((d => d.name === fullPath));
if (matched) {
wp_path_id = matched.id;
console.log("[115离线] 目录匹配结果: 匹配到 \"" + matched.name + "\" -> id=" + matched.id);
} else {
console.log("[115离线] 目录匹配结果: 未匹配到 \"" + fullPath + "\",自动创建并注册");
const newId = await WangPan115TaskPlugin.createMultiLevelDownPath(fullPath);
if (newId) wp_path_id = newId;
}
}
}
}
}
const result = await (async (magnet, wp_path_id = "", uid, sign, time) => {
const data = {
url: encodeURIComponent(magnet),
wp_path_id: wp_path_id,
@@ -89,7 +236,7 @@ class WangPan115TaskPlugin extends BasePlugin {
time: time
};
return await gmHttp.postForm("https://115.com/web/lixian/?ct=lixian&ac=add_task_url", data);
})(magnetLink, savePath || "", userId, sign, time);
})(magnetLink, wp_path_id, userId, sign, time);
console.log("离线下载返回值:", result);
let infoHash = result.info_hash, fileId = await this.getFileId(userId, sign, time, infoHash), openUrl = "https://115.com/?tab=offline&mode=wangpan";
fileId && (openUrl = `https://115.com/?cid=${fileId}&offset=0&mode=wangpan`);
+3 -5
View File
@@ -6,9 +6,9 @@ export default defineConfig({
monkey({
entry: 'src/main.js',
userscript: {
name: 'JHS-enhance',
namespace: 'JHS-enhance',
version: '3.3.10',
name: 'JHS-enhance',
namespace: 'JHS-enhance',
version: '3.3.18',
author: 'xiebro,fireinrain',
description: 'Jav-鉴黄师 增强脚本。列表页:作品状态标签、一键筛选、新作品检测、演员黑名单过滤;详情页:磁力链接高亮、DMM 多画质预览视频、标题翻译、整合字幕搜索(迅雷+SubTitleCat)并支持 115 直传、多源预览图(javfree/projectjav/javstore)含来源切换;数据:115 网盘目录匹配与多目录选择、云盘备份/恢复、跨 Tab 同步;其他:自动翻页、分类折叠、Top250、以图识图、热门榜单、评论查看、相关清单。支持 JavDB / JavBus / JavSee / SeeJav / FC2 / JavTrailers',
license: 'MIT',
@@ -38,7 +38,6 @@ export default defineConfig({
],
require: [
'https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js',
'https://cdn.jsdelivr.net/npm/tabulator-tables@6.5.2/dist/js/tabulator.min.js',
'https://cdn.jsdelivr.net/npm/layui-layer@1.0.9/dist/layer.min.js',
'https://cdn.jsdelivr.net/npm/blueimp-md5@2.19.0/js/md5.min.js',
'https://cdn.jsdelivr.net/npm/toastify-js@1.12.0/src/toastify.min.js',
@@ -96,7 +95,6 @@ export default defineConfig({
'localforage': 'localforage',
'toastify-js': 'Toastify',
'blueimp-md5': 'md5',
'tabulator-tables': 'Tabulator',
'viewerjs': 'Viewer',
'qrcodejs': 'QRCode',
},