English 中文
一个用于获取JMComic(禁漫天堂)数据的Java API库
受限于个人能力与项目经验,当前实现仍有许多可以改进的地方,包括代码实现、架构设计和功能规划等方面。欢迎提出 Issue、建议或优化思路,帮助项目持续完善。
漫画
本子详情 — 获取包含章节元数据、标签、作者等完整信息章节阅读 — 获取章节内所有图片及下载URL搜索本子 — 支持关键词、分类、排序、时间范围等多维度筛选分类排行 — 按分类获取排行列表分类列表 — 获取分类树及标签区块
下载
并发下载 — 内置线程池调度,支持章节/本子并行下载链式API — client.download(album).withProgress(cb).withPath(path).execute()进度回调 — 注册进度回调,实时获取图片/章节完成进度任务系统 — 暂停/恢复/取消、状态机、观察者模式、任务管理器自定义路径 — 支持三层粒度的路径生成器自定义线程池 — 注入外部线程池,完全掌控并发模型
用户
登录/登出 — 用户名密码登录获取用户信息个人资料 — 查看/编辑用户资料,修改昵称等字段
评论
评论列表 — 支持漫画、小说、小说章节、博客、用户等多实体类型发表/回复 — 支持漫画评论、博客评论、小说评论的发表与回复
收藏
收藏夹 — 获取收藏列表,按文件夹/页码筛选收藏管理 — 切换收藏状态,收藏夹增/删/改/移标签管理 — 获取/添加/删除收藏标签
小说
小说列表 — 按排序条件获取小说详情 — 获取章节元数据、关联信息章节阅读 — 获取章节正文小说搜索 — 关键词搜索小说小说评论/收藏 — 点赞、收藏、发表/回复评论
创作者
作者列表 — 分页浏览创作者作品浏览 — 按语言、来源筛选作品作品详情 — 获取作品信息与详情
签到
签到状态 — 获取当日签到进度及活动信息执行签到 — 完成每日签到签到历史 — 查询历史签到记录
通知与追踪
通知列表 — 获取系统通知已读/未读 — 标记通知状态连载追踪 — 获取追踪列表,设置/查看追踪状态
发现
热门标签 — 获取热门搜索关键词最新上架 — 分页获取最新本子随机推荐 — 获取推荐列表每周必看 — 获取每周期数列表及详情首页推广 — 获取Banner内容
其他
浏览历史 — 获取/删除观看历史任务系统 — 获取任务列表
本项目采用模块化设计,将 公共接口(API) 与 核心实现(Core) 分离。
此模块定义了库的公共契约,不包含第三方网络库依赖,可独立集成。
- 领域模型: 提供一套基于Java
Record的不可变数据对象,涵盖漫画 (JmAlbum,JmPhoto,JmImage)、小说 (JmNovelDetail,JmNovelChapter)、创作者 (JmCreatorMeta,JmCreatorWorkInfo)、评论 (JmComment,JmCommentList)、通知 (JmNotification)、签到 (JmDailyCheckInStatus) 等核心实体。 - 客户端接口 (
JmClient): 抽象并统一了所有业务操作,包括实体获取、搜索、用户会话、评论互动、收藏管理、签到、通知、浏览发现等。 - 子系统接口:
JmNovelClient— 小说子系统(列表、详情、章节阅读、评论、收藏)JmCreatorClient— 创作者子系统(作者列表、作品浏览、作品详情)JmDownloadClient— 下载子系统(支持链式API、进度回调、路径和线程池注入)
- 策略接口: 定义了
IAlbumPathGenerator、IPhotoPathGenerator和IDownloadPathGenerator等策略接口,允许调用者注入自定义逻辑来控制文件存储等外部交互行为。 - 配置模型 (
JmConfiguration): 提供Builder模式用于程序化配置客户端行为,支持代理、超时、并发、域名探活、图片超时等参数设置。
此模块包含了所有功能的具体实现逻辑,处理与JMComic服务端的直接交互。
- 客户端实现:
- API客户端: 通过调用JMComic移动端API进行数据交互(推荐)。
- HTML客户端: 通过请求和解析JMComic网站的HTML页面进行数据交互。
- 网络处理:
- 动态域名: 包含在客户端初始化时自动获取最新API及HTML域名的机制。
- 请求重试: 实现了一套有状态的重试逻辑,当请求失败时能够在预设的域名列表中进行轮询。
- 域名后台探活: 定期探测域名可用性,自动切换。
- 数据处理:
- API加解密: 自动完成API请求的Header签名生成和响应体的AES解密。
- 图片重组: 实现了对JMComic特定图片切割的反混淆算法,将分块图片还原为原始图片。
- 并发下载:
- 提供了
downloadAlbum和downloadPhoto等下载方法,内置基于ExecutorService和CompletableFuture的并发调度能力。 - 新增 链式下载API (
client.download(album).withProgress(...).execute()),支持实时进度回调。 - 批量下载操作返回
DownloadResult对象,包含成功与失败任务的详细报告。
- 提供了
| 功能 | 实现情况 |
|---|---|
| Cloudflare 解决方案 | 🔴 |
| Android 支持 | ✅ |
本项目的核心是一个数据获取与管理工具,而非一个功能固化的下载应用。其设计基于以下原则:
- 控制权移交: 库本身不负责具体的线程调度和文件I/O。调用者可以通过注入
ExecutorService和PathGenerator等策略接口,完全掌控并发行为和文件存储逻辑。 - 过程透明化: API调用链 (
Album→Photo→Image) 中的每一个中间数据模型都是可访问的。这允许开发者在下载过程的任何阶段进行检查、过滤或自定义处理。 - 为集成而设计: 库的目标是作为一个可靠的底层模块,被轻松地集成到其他大型应用中,如Android App、桌面工具或后端服务。
- 安装 jmcomic-api
<dependency> <groupId>io.github.jukomu</groupId> <artifactId>jmcomic-api</artifactId> <version>1.1.7</version> </dependency>
- 安装 jmcomic-core
<dependency> <groupId>io.github.jukomu</groupId> <artifactId>jmcomic-core</artifactId> <version>1.1.7</version> </dependency>
- Android平台额外安装 jmcomic-android-support
<dependency> <groupId>io.github.jukomu</groupId> <artifactId>jmcomic-android-support</artifactId> <version>1.1.7</version> </dependency>
import io.github.jukomu.jmcomic.api.download.DownloadProgress;
import io.github.jukomu.jmcomic.api.download.DownloadResult;
import io.github.jukomu.jmcomic.api.enums.ClientType;
import io.github.jukomu.jmcomic.api.model.JmAlbum;
import io.github.jukomu.jmcomic.api.model.JmPhoto;
import io.github.jukomu.jmcomic.core.JmComic;
import io.github.jukomu.jmcomic.core.client.AbstractJmClient;
import io.github.jukomu.jmcomic.core.config.JmConfiguration;
public class QuickStart {
public static void main(String[] args) {
JmConfiguration config = new JmConfiguration.Builder()
.clientType(ClientType.API)
.build();
try (AbstractJmClient client = JmComic.newApiClient(config)) {
// 下载整个本子(含进度回调)
JmAlbum album = client.getAlbum("1064000");
DownloadResult result = client.download(album)
.withProgress(p -> System.out.printf(
"进度: 图片 %d/%d, 章节 %d/%d%n",
p.completedImages(), p.totalImages(),
p.completedPhotos(), p.totalPhotos()))
.execute();
if (result.isAllSuccess()) {
System.out.println("下载完成! 共 " + result.getSuccessfulFiles().size() + " 张");
} else {
result.getFailedTasks().forEach((img, err) ->
System.err.println("失败: " + img.getTag() + " - " + err.getMessage()));
}
}
}
}try (AbstractJmClient client = JmComic.newApiClient(config)) {
// 获取本子详情
JmAlbum album = client.getAlbum("540709");
System.out.println("标题: " + album.title() + ", 作者: " + album.authors());
// 搜索漫画
JmSearchPage result = client.search(
new SearchQuery.Builder().text("NTR").page(1).build());
result.content().forEach(m ->
System.out.printf("[%s] %s%n", m.id(), m.title()));
// 获取热门标签
List<String> hotTags = client.getHotTags();
System.out.println("热门: " + hotTags);
}JmConfiguration config = new JmConfiguration.Builder()
.clientType(ClientType.HTML) // 客户端类型
.proxy("127.0.0.1", 7890) // HTTP代理
.timeout(Duration.ofSeconds(60)) // 请求超时
.imageTimeout(Duration.ofSeconds(120)) // 图片下载超时
.retryTimes(10) // 重试次数
.downloadThreadPoolSize(12) // 下载线程池大小
.cacheSize(100 * 1024 * 1024) // 缓存大小 (Byte)
.closeTimeoutMs(30_000) // 关闭超时 (ms)
.domainProbeIntervalMs(600_000) // 域名探活间隔 (ms)
.domainProbeTimeoutMs(3000) // 域名探活超时 (ms)
.build();
concurrentPhotoDownloads和concurrentImageDownloads已废弃,由统一的downloadThreadPoolSize控制并发。
提供三种粒度的路径策略:
// 方式1: 本子级路径生成器
IAlbumPathGenerator albumPath = album ->
Path.of("downloads", album.getId());
// 方式2: 章节级路径生成器
IPhotoPathGenerator photoPath = photo ->
Path.of(String.format("%03d", photo.getSortOrder()));
// 方式3: 完整路径生成器(一次性控制 album/photo/image 三层)
IDownloadPathGenerator totalPath = (album, photo, image) ->
Path.of(album.getId(),
String.format("%03d", photo.getSortOrder()),
image.getFilename());
// 使用
client.downloadAlbum(album, totalPath, executor);ExecutorService myExecutor = Executors.newFixedThreadPool(16);
try (AbstractJmClient client = JmComic.newApiClient(config)) {
DownloadResult result = client.download(album)
.withExecutor(myExecutor)
.withProgress(p -> System.out.printf("%d/%d%n",
p.completedImages(), p.totalImages()))
.execute();
} finally {
myExecutor.shutdown();
myExecutor.awaitTermination(1, TimeUnit.MINUTES);
}基于状态机的任务管理,支持暂停/恢复/取消和观察者通知:
import io.github.jukomu.jmcomic.api.download.task.BaseDownloadTask;
import io.github.jukomu.jmcomic.api.download.IDownloadManager;
import io.github.jukomu.jmcomic.api.download.task.TaskObserver;
import io.github.jukomu.jmcomic.api.download.enums.TaskState;
// 创建下载任务(不立即开始)
BaseDownloadTask task = client.createDownloadTask(album, Path.of("downloads"));
// 注册观察者
task.addObserver(new TaskObserver() {
@Override
public void onStateChanged(BaseDownloadTask t, TaskState state) {
System.out.println("状态变更: " + state);
}
@Override
public void onProgressUpdate(BaseDownloadTask t, DownloadProgress p) {
System.out.printf("进度: %d/%d 图片%n", p.completedImages(), p.totalImages());
}
@Override
public void onFinished(BaseDownloadTask t, DownloadResult result) {
System.out.println("完成: " + result.getSuccessfulFiles().size() + " 张");
}
@Override
public void onError(BaseDownloadTask t, Exception e) {
System.err.println("错误: " + e.getMessage());
}
});
// 通过管理器提交和控制
IDownloadManager manager = client.downloadManager();
manager.submit(task);
// 暂停 / 恢复 / 取消
manager.pause(task.getTaskId());
manager.resume(task.getTaskId());
manager.cancel(task.getTaskId());
// 查询任务
BaseDownloadTask t = manager.getTask(taskId);
List<BaseDownloadTask> active = manager.getActiveTasks();// 获取本子评论
JmCommentList comments = client.getComments(
ForumQuery.album("1173049").mode(ForumMode.ALL).page(1).build());
comments.getList().forEach(c ->
System.out.println(c.getUsername() + ": " + c.getContent()));
// 发表评论(需先登录)
JmComment comment = client.postComment("1173049", "好看!");
// 回复评论
JmComment reply = client.replyToComment("1173049", "+1", parentCommentId);
// 获取小说评论
client.getComments(ForumQuery.novel("12345").page(1).build());
// 获取小说某章评论
client.getComments(ForumQuery.novelChapter("12345", "67890").page(1).build());// 获取收藏夹
JmFavoritePage favPage = client.getFavorites(
new FavoriteQuery.Builder().folderId(0).page(1).build());
// 切换收藏状态
client.toggleAlbumFavorite("1064000", "0");
// 管理收藏文件夹
client.manageFavoriteFolder(FavoriteFolderType.ADD, "0", "新文件夹", null);
client.manageFavoriteFolder(FavoriteFolderType.DEL, "folderId", null, null);
// 收藏标签
List<JmTagFavorite> tags = client.getTagsFavorite();
client.addFavoriteTags(List.of("标签1", "标签2"));
client.removeFavoriteTags(List.of("标签1"));// 登录
JmUserInfo user = client.login("username", "password");
// 获取/编辑资料
JmUserProfile profile = client.getUserProfile(user.getUid());
client.editUserProfile(user.getUid(), Map.of("nickname", "新昵称"));
// 每日签到
JmDailyCheckInStatus status = client.getDailyCheckInStatus(user.getUid());
client.doDailyCheckin(user.getUid(), status.dailyId());
// 签到历史筛选
List options = client.getDailyCheckInOptions(user.getUid());
List records = client.filterDailyCheckInList("2025");// 获取小说列表
JmNovelPage novels = client.getNovelList("mr", 1);
// 获取小说详情
JmNovelDetail detail = client.getNovelDetail("novelId");
// 获取章节内容
JmNovelChapter chapter = client.getNovelChapter("chapterId", "0");
// 搜索小说
JmNovelPage searchResult = client.searchNovels("关键词");
// 小说评论
client.postNovelComment("novelId", "精彩!", null);
client.replyToNovelComment("novelId", "同感", parentCommentId, null);// 获取作者列表
JmCreatorPage authors = client.getCreatorAuthors(1, "");
// 获取作品列表
JmCreatorWorkPage works = client.getCreatorWorks(1, "", "", "");
// 获取指定作者的作品
JmCreatorAuthorWorksPage authorWorks =
client.getCreatorAuthorWorks("creatorId", "", "", 1);
// 获取作品信息与详情
JmCreatorWorkInfo info = client.getCreatorWorkInfo("workId");
JmCreatorWorkDetail workDetail = client.getCreatorWorkDetail("workId");// 最新上架
JmSearchPage latest = client.getLatest(1);
// 随机推荐
List<JmAlbumMeta> random = client.getRandomRecommend();
// 每周必看
JmWeeklyPicksList picks = client.getWeeklyPicksList();
JmWeeklyPicksDetail pickDetail = client.getWeeklyPicksDetail(categoryId);
// 连载追踪
JmTrackingPage tracking = client.getAlbumTrackingList(1);
client.setAlbumSertracking("albumId");
boolean isTracking = client.getAlbumSertracking("albumId");
// 浏览历史
List<JmAlbumMeta> history = client.getWatchHistory(1);
client.deleteWatchHistory("albumId");| 特性 | API Client (推荐) |
HTML Client |
|---|---|---|
| 稳定性 | ✅ 高 (基于官方API) | |
| 性能 | ✅ 高 | |
| Cloudflare | 🟢 几乎不受影响 | 🔴 可能被拦截 (无内置绕过方案) |
| 数据完整性 | 🟢 大部分数据齐全 | ✅ 非常完整 (所有页面可见信息) |
| 使用场景 | 绝大多数常规的数据获取、下载 | 获取API没有的特定信息,或作为备用方案 |
JMComic 网站使用 Cloudflare 作为其安全防护,HTML Client 请求可能会被 Cloudflare 的人机验证所拦截,导致请求失败。
当前版本尚未内置自动绕过 Cloudflare 的机制。
欢迎任何形式的贡献!如果您发现了BUG或有新的功能建议,请随时提交 Issues。
如果您想贡献代码,请先 Fork 本项目,在您的分支上进行修改,然后提交 Pull Request。
本项目基于 MIT License 开源。
完整文档请访问:JMComic-Api-Java 文档