Renew Projects

This commit is contained in:
e2hang
2025-12-31 13:22:56 +08:00
parent 4b60ced553
commit d143bbc65c
1753 changed files with 841 additions and 0 deletions

View File

@@ -0,0 +1,298 @@
# 常用类和方法
## 下载本子/章节
```python
from jmcomic import *
# 下载id为438696的本子 (https://18comic.vip/album/438696)
download_album(438696)
# 下载章节 (https://18comic.vip/photo/438696)
download_photo(438696)
# 同时下载多个本子
download_album([123, 456, 789])
```
## 使用option定制化下载本子
如果你在下载本子时有一些定制化需求,
例如指定禁漫域名,使用代理,登录禁漫,图片格式转换等等,
那么你可以试试看jmcomic提供的option机制
```python
from jmcomic import *
# 1. 在调用下载api前通过创建和使用option对象可以定制化下载行为。
# 推荐使用配置文件的方式来创建option对象
# 你可以配置很多东西比如代理、cookies、下载规则等等。
# 配置文件的语法参考: https://jmcomic.readthedocs.io/en/latest/option_file_syntax/
option = create_option_by_file('op.yml') # 通过配置文件来创建option对象
# 2. 调用下载api把option作为参数传递
download_album(123, option)
# 也可以使用下面这种面向对象的方式,是一样的
option.download_album(123)
```
## 获取本子/章节/图片的实体类,下载图片
```python
from jmcomic import *
# 客户端
client = JmOption.default().new_jm_client()
# 本子实体类
album: JmAlbumDetail = client.get_album_detail('427413')
def fetch(photo: JmPhotoDetail):
# 章节实体类
photo = client.get_photo_detail(photo.photo_id, False)
print(f'章节id: {photo.photo_id}')
# 图片实体类
image: JmImageDetail
for image in photo:
print(f'图片url: {image.img_url}')
# 下载单个图片
client.download_by_image_detail(image, './a.jpg')
# 如果是已知未混淆的图片也可以直接使用url来下载
random_image_domain = JmModuleConfig.DOMAIN_IMAGE_LIST
client.download_image(f'https://{random_image_domain}/media/albums/416130.jpg', './a.jpg')
# 多线程发起请求
multi_thread_launcher(
iter_objs=album,
apply_each_obj_func=fetch
)
```
## jmcomic异常处理示例
```python
from jmcomic import *
# 客户端
client = JmOption.default().new_jm_client()
# 捕获jmcomic可能出现的异常
try:
# 请求本子实体类
album: JmAlbumDetail = client.get_album_detail('427413')
except MissingAlbumPhotoException as e:
print(f'id={e.error_jmid}的本子不存在')
except JsonResolveFailException as e:
print(f'解析json失败')
# 响应对象
resp = e.resp
print(f'resp.text: {resp.text}, resp.status_code: {resp.status_code}')
except RequestRetryAllFailException as e:
print(f'请求失败,重试次数耗尽')
except JmcomicException as e:
# 捕获所有异常,用作兜底
print(f'jmcomic遇到异常: {e}')
```
## 搜索本子
```python
from jmcomic import *
client = JmOption.default().new_jm_client()
# 分页查询search_site就是禁漫网页上的【站内搜索】
page: JmSearchPage = client.search_site(search_query='+MANA +无修正', page=1)
# page默认的迭代方式是page.iter_id_title(),每次迭代返回 albun_id, title
for album_id, title in page:
print(f'[{album_id}]: {title}')
# 直接搜索禁漫车号
page = client.search_site(search_query='427413')
album: JmAlbumDetail = page.single_album
print(album.tags)
```
## 搜索并下载本子
```python
from jmcomic import *
option = JmOption.default()
client = option.new_jm_client()
tag = '無修正'
# 搜索标签可以使用search_tag。
# 搜索第一页。
page: JmSearchPage = client.search_tag(tag, page=1)
aid_list = []
for aid, atitle, tag_list in page.iter_id_title_tag(): # 使用page的iter_id_title_tag迭代器
if tag in tag_list:
print(f'[标签/{tag}] 发现目标: [{aid}]: [{atitle}]')
aid_list.append(aid)
download_album(aid_list, option)
```
## 获取收藏夹
可参考discussions: https://github.com/hect0x7/JMComic-Crawler-Python/discussions/235
```python
from jmcomic import *
option = JmOption.default()
client = option.new_jm_client()
client.login('用户名', '密码') # 也可以使用login插件/配置cookies
# 遍历全部收藏的所有页
for page in cl.favorite_folder_gen(): # 如果你只想获取特定收藏夹需要添加folder_id参数
# 遍历每页结果
for aid, atitle in page.iter_id_title():
# aid: 本子的album_id
# atitle: 本子的名称
print(aid)
# 打印当前帐号的所有收藏夹信息
for folder_id, folder_name in page.iter_folder_id_name():
print(f'收藏夹id: {folder_id}, 收藏夹名称: {folder_name}')
# 获取特定收藏夹的单页使用favorite_folder方法
page = client.favorite_folder(page=1,
order_by=JmMagicConstants.ORDER_BY_LATEST,
folder_id='0' # 收藏夹id
)
```
## 分类 / 排行榜
禁漫的分类是一个和搜索有些类似的功能。
搜索是按某一条件进行过滤。
分类没有过滤就是把某一类别category下的本子全都调出来。
禁漫的排行榜就是分类的一种形式
下面演示调用分类api
```python
from jmcomic import *
# 创建客户端
op = JmOption.default()
cl = op.new_jm_client()
# 调用分类接口
# 根据下面的参数,这个调用的意义就是:
# 在全部分类下,选择所有时间范围,按观看数排序后,获取第一页的本子
page: JmCategoryPage = cl.categories_filter(
page=1,
time=JmMagicConstants.TIME_ALL, # 时间选择全部具体可以写什么请见JmMagicConstants
category=JmMagicConstants.CATEGORY_ALL, # 分类选择全部具体可以写什么请见JmMagicConstants
order_by=JmMagicConstants.ORDER_BY_LATEST, # 按照观看数排序具体可以写什么请见JmMagicConstants
)
# 月排行底层实现也是调的categories_filter
page: JmCategoryPage = cl.month_ranking(1)
# 周排行
page: JmCategoryPage = cl.week_ranking(1)
# 循环获取分页,使用 cl.categories_filter_gen
for page in cl.categories_filter_gen(page=1, # 起始页码
# 下面是分类参数
time=JmMagicConstants.TIME_WEEK,
category=JmMagicConstants.CATEGORY_ALL,
order_by=JmMagicConstants.ORDER_BY_VIEW,
):
for aid, atitle in page:
print(aid, atitle)
```
## 高级搜索(分类/副分类)
禁漫网页端的搜索除了常规条件,还支持【分类】和【副分类】的搜索。
在任一搜索页面,你会看到本子图的右上方有两个标签。左边的是【分类】,右边的是【副分类】。
下面演示代码如何编写。
* **注意!!禁漫移动端没有提供如下功能,以下代码仅对网页端生效。**
```python
# 在编写代码前,建议先熟悉禁漫网页的搜本功能,下面的代码都是对照网页编写的。
# 网页搜索示例https://18comic.vip/search/photos/doujin/sub/CG?main_tag=0&search_query=mana&page=1&o=mr&t=a
from jmcomic import *
op = create_option_by_file('op.yml')
# 创建网页端client
html_cl = op.new_jm_client(impl='html')
# 使用站内搜索,指定【分类】和【副分类】
# 分类 = JmMagicConstants.CATEGORY_DOUJIN = 同人本
# 副分类 = JmMagicConstants.SUB_DOUJIN_CG = CG本
# 实际URLhttps://18comic.vip/search/photos/doujin/sub/CG?main_tag=0&search_query=mana&page=1&o=mr&t=a
page = html_cl.search_site(search_query='mana',
category=JmMagicConstants.CATEGORY_DOUJIN,
sub_category=JmMagicConstants.SUB_DOUJIN_CG,
page=1,
)
# 打印page内容
for aid, atitle in page.iter_id_title():
print(aid, atitle)
# 循环获取分页
for page in html_cl.search_gen(search_query='mana',
category=JmMagicConstants.CATEGORY_DOUJIN,
sub_category=JmMagicConstants.SUB_DOUJIN_CG,
page=1, # 起始页码
):
# 打印page内容
for aid, atitle in page.iter_id_title():
print(aid, atitle)
```
## 手动创建Client
```python
# 默认的使用方式是先创建optionoption封装了所有配置然后由option.new_jm_client() 创建客户端client使用client可以访问禁漫接口
# 下面演示直接构造client的方式
from jmcomic import *
"""
创建JM客户端
:param postman: 负责实现HTTP请求的对象持有cookies、headers、proxies等信息
:param domain_list: 禁漫域名
:param retry_times: 重试次数
"""
# 网页端
cl = JmHtmlClient(
postman=JmModuleConfig.new_postman(),
domain_list=['18comic.vip'],
retry_times=1
)
# API端APP
cl = JmApiClient(
postman=JmModuleConfig.new_postman(),
domain_list=JmModuleConfig.DOMAIN_API_LIST,
retry_times=1
)
```

View File

@@ -0,0 +1,75 @@
# 导出并下载你的禁漫收藏夹数据
一共需要三步:
1. fork一份我的代码仓库。
2. 配置你的禁漫帐号密码。
3. 运行工作流,下载结果。
下面截图解析这三步的详细过程。
## 1. fork一份我的代码仓库
访问下面这个网址:
`https://github.com/hect0x7/JMComic-Crawler-Python/fork`
直接拉到页面最底部,如下所示:
最新提示下图的1可以不做即直接点绿色的Create fork按钮
![1](../images/1.png)
## 2. 配置你的禁漫帐号密码
在开始下面的步骤之前你需要先启用你的repo的Actions开启方式如下
![6](../images/6.png)
**注意事项:
最好使用secrets保证不会泄漏信息。
虽然也支持通过工作流dispatch输入帐号密码但是那种方式会让账号密码明文显示在Actions日志中。
如果你打算采用后者,最好下载完就及时删除工作流。**
然后访问下面这个网址:
`https://github.com/你的用户名/JMComic-Crawler-Python/settings/secrets/actions`
按下图步骤进行操作:
![7](../images/7.png)
然后来到填写页面
![8](../images/8.png)
你需要填入下面的内容并点【Add secret】保存
`JM_USERNAME`: 你的禁漫帐号用户名
同样的方式再配置如下内容:
`JM_PASSWORD`: 你的禁漫帐号密码
`ZIP_PASSWORD`: 压缩文件密码,防止别人下载使用你的文件
## 3. 运行工作流,下载结果。
访问如下网址:
`https://github.com/你的用户名/JMComic-Crawler-Python/actions/workflows/export_favorites.yml`
按照下图步骤点击,即可运行工作流。
* 如果你在步骤2已经配置了secrets无需在意输入框。
* 如果你没有配置secrets你可以在输入框中输入账号 密码 压缩密码,但要记得及时删除工作流来避免信息泄漏。
![9](../images/9.png)
待工作流变绿,表示已完成,点击去拉到页面底部,即可下载文件。
![10](../images/10.png)

View File

@@ -0,0 +1,55 @@
# 日志自定义
本文档缘起于 GitHub Discussions: [discussions/195](https://github.com/hect0x7/JMComic-Crawler-Python/discussions/195)
下面是这个问题的解决方法:
## 1. 日志完全开启/关闭
使用代码:
```
from jmcomic import disable_jm_log
disable_jm_log()
```
使用配置:
```yaml
log: false
```
## 2. 日志过滤只保留特定topic
使用插件配置
```yaml
log: true
plugins:
after_init:
- plugin: log_topic_filter # 日志topic过滤插件
kwargs:
whitelist: [ # 只保留api和html这两个是Client发请求时会打的日志topic
'api',
'html',
]
```
## 3. 屏蔽插件的日志
给插件配置加上一个`log`配置项即可
```yaml
plugins:
after_init:
- plugin: client_proxy # 提高移动端的请求效率的插件
log: false # 插件自身不打印日志
kwargs:
proxy_client_key: photo_concurrent_fetcher_proxy
whitelist: [ api, ]
```
## 4. 完全自定义 jmcomic 日志
你可以自定义jmcomic的模块日志打印函数参考文档[模块自定义](./4_module_custom.md#自定义log)

View File

@@ -0,0 +1,74 @@
# GitHub Actions使用教程
一共需要三步:
1. fork一份我的代码仓库。
2. 填写你需要下载的本子id。
3. 等待GitHub Actions下载完成下载成品zip文件。
下面截图解析这三步的详细过程。
## 1. fork一份我的代码仓库
访问下面这个网址:
`https://github.com/hect0x7/JMComic-Crawler-Python/fork`
直接拉到页面最底部,如下所示:
最新提示下图的1可以不做即直接点绿色的Create fork按钮
![1](../images/1.png)
## 2. 填写你需要下载的本子id
在开始下面的步骤之前你需要先启用你的repo的Actions开启方式如下
![6](../images/6.png)
### 2.1. 方式一(最新、简单、推荐)
访问下面这个网址:
`https://github.com/你的用户名/JMComic-Crawler-Python/actions/workflows/download_dispatch.yml`
按下图步骤进行操作:
![5](../images/5.png)
### 2.2. 方式二
访问下面这个网址:
`https://github.com/你的用户名/JMComic-Crawler-Python/edit/master/usage/workflow_download.py`
其实就是编辑 `usage/workflow_download.py`文件,任意分支皆可。
按下图步骤进行操作:
![2](../images/2.png)
看到上面的绿色框里的代码了吗?(位置可能有些变化)
上面有注释把你要下载的本子的id填入一行一个id前面可以带`JM`
填完点提交会自动触发GitHub Actions下载这些本子。
## 3. 等待GitHub Actions下载完成下载成品zip文件
来到Actions页面选择最新的一次记录等待它完成。
![3](../images/3.png)
完成以后,在页面最底部,点击下载你的成品即可。
![4](../images/4.png)
如果你发现GitHub Actions显示❌表明出现了问题运行失败。
下面是问题的排查步骤:
1. 检查你在步骤2中填写是否有误。
2. 点进build查看运行流程找到`运行下载脚本`这个步骤,查看报错的具体信息
3. 来到我的项目仓库点进Issue先根据报错信息搜索Issue如果能搜到类似问题自行参考解决。
4. 如果还是不知道如何解决提一个Issue详细描述下你的问题。

View File

@@ -0,0 +1,25 @@
# 命令行教程
## 1. 基本用法
```
# 下载album 123 456下载photo 333。彼此之间使用空格间隔
jmcomic 123 456 p333
```
## 2. 自定义option
### 2.1. 通过命令行
使用 --option 参数指定option配置文件路径
```
jmcomic 123 --option="D:/a.yml"
```
### 2.2. 使用环境变量
配置环境变量 `JM_OPTION_PATH` 为option配置文件路径
```
set JM_OPTION_PATH="D:/a.yml"
jmcomic 123
```

View File

@@ -0,0 +1,194 @@
# 模块自定义
下方所有函数都省略了如下的导包和准备代码
```python
from jmcomic import *
option = JmOption.default()
client: JmcomicClient = option.build_jm_client()
```
## 自定义下载前后的回调函数
```python
def custom_download_callback():
"""
该函数演示自定义下载时的回调函数
"""
# jmcomic的下载功能由 JmModuleConfig.CLASS_DOWNLOADER 这个类来负责执行
# 这个类默认是 JmDownloader继承了DownloadCallback
# 你可以写一个自定义类继承JmDownloader覆盖属于DownloadCallback的方法来实现自定义回调
class MyDownloader(JmDownloader):
# 覆盖 album 下载完成后的回调
def after_album(self, album: JmAlbumDetail):
print(f'album下载完毕: {album}')
pass
# 最后,让你的自定义类生效
JmModuleConfig.CLASS_DOWNLOADER = MyDownloader
```
## 自定义option类
```python
def custom_option_class():
"""
该函数演示自定义option类
"""
# jmcomic模块支持自定义Option类
# 你可以写一个自己的类继承JmOption然后覆盖其中的一些方法。
class MyOption(JmOption):
def __init__(self, *args, **kwargs):
print('MyOption 初始化开始')
super().__init__(*args, **kwargs)
@classmethod
def default(cls):
print('调用了MyOption.default()')
return super().default()
# 最后替换默认Option类即可
JmModuleConfig.CLASS_OPTION = MyOption
```
## 自定义client类
```python
def custom_client_class():
"""
该文件演示自定义client类
"""
# 默认情况下JmOption使用client类是根据配置项 `client.impl` 决定的
# JmOption会根据`client.impl`到 JmModuleConfig.CLASS_CLIENT_IMPL 中查找
# 自定义client的步骤如下
# 1. 自定义Client类
class MyClient(JmHtmlClient):
client_key = 'myclient'
pass
# 2. 让MyClient生效
JmModuleConfig.register_client(MyClient)
# 3. 在配置文件中使用你定义的client.impl后续使用这个option即可
"""
client:
impl: myclient
"""
```
## 自定义实体类(本子/章节/图片)
```python
def custom_album_photo_image_detail_class():
"""
该函数演示自定义实体类(本子/章节/图片)
在使用路径规则 DirRule 时,可能会遇到需要自定义实体类属性的情况,例如:
dir_rule:
base_dir: ${workspace}
rule: Bd_Acustom_Pcustom
上面的AcustomPcustom都是自定义字段
如果你想要使用这种自定义字段,你就需要替换默认的实体类,方式如下
"""
# 自定义本子实体类
class MyAlbum(JmAlbumDetail):
# 自定义 custom 属性
@property
def custom(self):
return f'custom_{self.title}'
# 自定义章节实体类
class MyPhoto(JmPhotoDetail):
# 自定义 custom 属性
@property
def custom(self):
return f'custom_{self.title}'
"""
v2.3.3: 支持更灵活的自定义方式,可以使用函数,效果同上,示例见下
"""
class MyAlbum2(JmAlbumDetail):
def get_dirname(self, ref: str) -> str:
if ref == 'custom':
return f'custom_{self.name}'
return super().get_dirname(ref)
# 最后,替换默认实体类来让你的自定义类生效
JmModuleConfig.CLASS_ALBUM = MyAlbum
JmModuleConfig.CLASS_PHOTO = MyPhoto
```
## 自定义log
```python
def custom_jm_log():
"""
该函数演示自定义log
"""
# jmcomic模块在运行过程中会使用 jm_log() 这个函数进行打印信息
# jm_log() 这个函数 最后会调用 JmModuleConfig.log_executor 函数
# 你可以写一个自己的函数,替换 JmModuleConfig.log_executor实现自定义log
# 1. 自定义log函数
def my_log(topic: str, msg: str):
"""
这个log函数的参数列表必须包含两个参数topic和msg
@param topic: log主题例如 'album.before', 'req.error', 'plugin.error'
@param msg: 具体log的信息
"""
pass
# 2. 让my_log生效
JmModuleConfig.log_executor = my_log
```
## 自定义异常监听器/回调
```python
def custom_exception_listener():
"""
该函数演示jmcomic的异常监听器机制
"""
# 1. 选一个可能会发生的、你感兴趣的异常
etype = ResponseUnexpectedException
def listener(e):
"""
你的监听器方法
该方法无需返回值
:param e: 异常实例
"""
print(f'my exception listener invoke !!! exception happened: {e}')
# 注册监听器/回调
# 这个异常类或者这个异常的子类的实例将要被raise前你的listener方法会被调用
JmModuleConfig.register_exception_listener(etype, listener)
```

View File

@@ -0,0 +1,78 @@
# Filter - 下载过滤器
filter(过滤器)是v2.1.12新引入的机制,
利用filter你可以实现下载时过滤本子/章节/图片,完全控制你要下载的内容。
使用filter的步骤如下
```
1. 自定义class继承JmDownloader重写do_filter方法即:
class MyDownloader(JmDownloader):
def do_filter(self, detail):
# 如何重写参考JmDownloader.do_filter和下面的示例
...
2. 让你的class生效使用如下代码
JmModuleConfig.CLASS_DOWNLOADER = MyDownloader
3. 照常使用下载api:
download_album(xxx, option)
```
* 下面的示例只演示步骤1
## 示例1只下载章节的前三张图
```python
from jmcomic import *
class First3ImageDownloader(JmDownloader):
def do_filter(self, detail):
if detail.is_photo():
photo: JmPhotoDetail = detail
# 支持[start,end,step]
return photo[:3]
return detail
```
## 示例2只下载本子的特定章节以后的章节
```python
from jmcomic import *
# 参考https://github.com/hect0x7/JMComic-Crawler-Python/issues/95
class FindUpdateDownloader(JmDownloader):
album_after_photo = {
'xxx': 'yyy'
}
def do_filter(self, detail):
if not detail.is_album():
return detail
return self.find_update(detail)
# 带入漫画id, 章节id(第x章)寻找该漫画下第x章节後的所有章节Id
def find_update(self, album: JmAlbumDetail):
if album.album_id not in self.album_after_photo:
return album
photo_ls = []
photo_begin = self.album_after_photo[album.album_id]
is_new_photo = False
for photo in album:
if is_new_photo:
photo_ls.append(photo)
if photo.photo_id == photo_begin:
is_new_photo = True
return photo_ls
```

View File

@@ -0,0 +1,121 @@
# Plugin - 插件
plugin(扩展/插件)是v2.2.0新引入的机制,使用插件可以实现灵活无感知的功能增强。
目前jmcomic已经内置了一些插件源码位于 src/jmcomic/jm_plugin.py。
你可以在这里查看这些插件的配置→ [option_file_syntax](../option_file_syntax.md#3-option插件配置项)
## 1. 插件机制介绍
你可以在option配置文件中配置插件插件会特定的<u>`事件`</u>发生时自动执行。
jmcomic里的内置事件如下
- `after_init`: option对象创建后
- `before_image`: 下载图片前
- `before_album`: 下载本子前
- `before_photo`: 下载章节前
- `after_image`: 下载图片后
- `after_album`: 下载本子后
- `after_photo`: 下载章节后
举例你可以配置一个login插件在option对象创建后`after_init`)时,调用`login`插件,执行登录禁漫的功能。
这样你后续使用option下载本子时都会处于已登录状态。已登录状态可以让你访问所有禁漫本子。
## 2. 示例在after_init时调用login插件
`login`插件功能登录JM获取并保存cookies到option内。后续option创建的Client都会携带cookies即都是登录状态。
配置方式将以下配置写入option文件
```yaml
plugins: # 插件配置项
after_init: # 在after_init事件时自动执行插件
- plugin: login # 插件的key
kwargs: # 下面是给插件的参数 (kwargs),由插件类自定义
username: un # 禁漫帐号
password: pw # 密码
```
有了上述option配置当你调用下面的代码时login插件就会执行。
```python
import jmcomic
option = jmcomic.create_option_by_file('xxx.yml') # 创建option对象
# 程序走到这里login插件已经调用完毕了
# 后续下载本子就都是已登录状态里了
option.download_album(123)
```
## 3. 怎么手动调用插件
你可以使用下面的代码触发某个事件
```python
# 假设你已经创建了option对象
from jmcomic import JmOption
option: JmOption
# 手动调用after_init事件下的插件
option.call_all_plugin('after_init')
# 手动调用一个特定事件的插件(如果你没有配置这个事件的插件,那么无事发生。)
option.call_all_plugin('my_event')
```
## 4. 示例:自定义插件
* 如果你有好的plugin想法也欢迎向我提PR将你的plugin内置到jmcomic模块中
下面演示自定义插件分为3步:
1. 自定义plugin类
2. 让plugin类生效
3. 使用plugin的key
```python
# 1. 自定义plugin类
from jmcomic import JmOptionPlugin, JmModuleConfig
# 自定义一个类继承JmOptionPlugin
class MyPlugin(JmOptionPlugin):
# 指定你的插件的key
plugin_key = 'myplugin'
# 实现invoke方法
# 方法的参数可以自定义,这里假设方法只有一个参数 word
def invoke(self, word) -> None:
print(word)
# 2. 让plugin类生效
JmModuleConfig.register_plugin(MyPlugin)
```
接下来在option中配置如下内容来使用你的插件
```yaml
# 3. 在配置文件中使用plugin
plugins:
after_init: # 事件
- plugin: myplugin # 你自定义的插件key
kwargs:
word: hello jmcomic # 你自定义的插件的参数
```
完成上述步骤后每当你使用下面的代码你的plugin会被调用控制台就会打印出 `hello jmcomic`
```python
from jmcomic import create_option
option = create_option('xxx')
```

View File

@@ -0,0 +1,54 @@
# 综合使用实例
* 基于v2.2.2+内置插件
## 功能需求
1. 下载id为145504的本子
2. 只下载章节在290266之后的新章
3. 最后要把图片转为jpg格式
4. 要使用登录状态去下载;
5. 压缩文件,按照章节压缩,一个章节一个压缩文件,压缩文件的命名: 章节标题.zip
6. 自动把下载的文件全部删除,最后只要保留压缩文件。
## 实现方案
#### 1. 写2行python代码
```python
from jmcomic import create_option
create_option('myoption.yml')
```
#### 2. 配置option调用1的脚本即可
```yaml
dir_rule: # 下载路径规则
rule: Bd_Aid
base_dir: D:/jmcomic
download:
image:
suffix: .jpg # 转为jpg格式的图片
client:
domain:
- 18comic.vip # 指定域名
plugins:
after_init:
- plugin: login # 登录插件
kwargs:
username: un
password: pw
- plugin: find_update # 只下载新章插件
kwargs:
145504: 290266 # 下载本子145504的章节290266以后的新章
after_album:
- plugin: zip # 压缩文件插件
kwargs:
level: photo # 按照章节,一个章节一个压缩文件
filename_rule: Ptitle # 压缩文件的命名规则
zip_dir: D:/jmcomic # 压缩文件存放的文件夹
delete_original_file: true # 压缩成功后,删除所有原文件和文件夹
```

View File

@@ -0,0 +1,81 @@
# 趣味用法测试你的ip可以访问哪些禁漫域名
```python
"""
该脚本的作用测试当前ip可以访问哪些禁漫域名
"""
from jmcomic import *
option = JmOption.default()
meta_data = {
# 'proxies': ProxyBuilder.clash_proxy()
}
disable_jm_log()
def get_all_domain():
template = 'https://jmcmomic.github.io/go/{}.html'
url_ls = [
template.format(i)
for i in range(300, 309)
]
domain_set: Set[str] = set()
def fetch_domain(url):
from curl_cffi import requests as postman
text = postman.get(url, allow_redirects=False, **meta_data).text
for domain in JmcomicText.analyse_jm_pub_html(text):
if domain.startswith('jm365.work'):
continue
domain_set.add(domain)
multi_thread_launcher(
iter_objs=url_ls,
apply_each_obj_func=fetch_domain,
)
return domain_set
domain_set = get_all_domain()
print(f'获取到{len(domain_set)}个域名,开始测试')
domain_status_dict = {}
def test_domain(domain: str):
client = option.new_jm_client(impl='html', domain_list=[domain], **meta_data)
status = 'ok'
try:
client.get_album_detail('123456')
except Exception as e:
status = str(e.args)
pass
domain_status_dict[domain] = status
multi_thread_launcher(
iter_objs=domain_set,
apply_each_obj_func=test_domain,
)
for domain, status in domain_status_dict.items():
print(f'{domain}: {status}')
```
# 程序输出示例
```text
获取到7个域名开始测试
18comic.vip: ok
18comic.org: ok
18comic-palworld.vip: ok
18comic-c.art: ok
jmcomic1.me: ok
jmcomic.me: ok
18comic-palworld.club: ok
```

View File

@@ -0,0 +1,144 @@
# 自定义下载文件夹名
## 0. 最简单直接粗暴有效的方式
使用插件`replace_path_string`
这个插件可以直接替换下载文件夹路径配置示例如下把如下配置放入option配置文件即可
```yml
plugins:
after_init:
- plugin: replace_path_string
kwargs:
replace:
# {左边写你要替换的原文}: {右边写替换成什么文本}
kyockcho: きょくちょ
```
该示例会把文件夹路径中所有`kyockcho`都变为`きょくちょ`,例如:
`D:/a/[kyockcho]本子名称 - kyockcho/` 改为↓
`D:/a/[きょくちょ]本子名称 - きょくちょ/`
---------------
**_如果上述简单的文本替换无法满足你或者你需要更多上下文写逻辑代码那么下面的内容正适合你阅读。_**
## 1. DirRule机制简介
当你使用download_album下载本子时本子会以一定的路径规则DirRule下载到你的磁盘上。
你可以使用配置文件定制DirRule例如下面的例子
```yaml
dir_rule:
# 设定根目录 base_dir
base_dir: D:/a/b/c/
rule: Bd / Ptitle # P表示章节title表示使用章节的title字段
# 这个规则的含义是,把图片下载到路径 {base_dir}/{Ptitle}/ 下
# 即:根目录 / 章节标题 / 图片文件
```
例如假设一个章节的名称Ptitle是ddd则最后的下载文件夹结构为 `D:/a/b/c/ddd/`
```
D:/a/b/c/ddd/00001.webp
D:/a/b/c/ddd/00002.webp
D:/a/b/c/ddd/00003.webp
...
```
上述的PtitleP表示章节title表示使用章节的title字段。
除了title你还可以写什么其实Ptitle表示的是jmcomic里的章节实体类 JmPhotoDetail 的属性。
最终能写什么取决于JmPhotoDetail有哪些属性建议使用IDE来获知这些属性不过这需要你懂一些python基础。
除了Pxxx你还可以写Axxx表示这个章节所在的本子的属性xxx详见本子实体类 JmAlbumDetail。
## 2. 自定义字段名
上述例子使用了title字段如果你想自定义一个字段然后在DirRule中使用自定义字段该怎么做
基于v2.4.6,你可以使用如下方式
1. 给你的自定义字段取个名
```yaml
dir_rule: # 忽略base_dir配置项
rule: Bd_Amyname # A表示本子myname表示本子的一个自定义字段
```
2. 在代码中,加入你自定义字段的处理函数
```python
from jmcomic import JmModuleConfig
# 你需要写一个函数把字段名作为key函数作为value加到JmModuleConfig.AFIELD_ADVICE这个字典中
JmModuleConfig.AFIELD_ADVICE['myname'] = lambda album: f'[{album.id}] {album.title}'
```
这样一来Amyname这个规则就会交由你的函数进行处理你便可以返回一个自定义的文件夹名
## 3. 更多的使用例子
### 完全使用自己的文件夹名
```python
from jmcomic import JmModuleConfig
dic = {
'248965': '社团学姐(爆赞韩漫)'
}
# Amyname
JmModuleConfig.AFIELD_ADVICE['myname'] = lambda album: dic[album.id]
download_album(248965)
```
### 文件夹名=作者+标题
```python
from jmcomic import JmModuleConfig
# Amyname
JmModuleConfig.AFIELD_ADVICE['myname'] = lambda album: f'【{album.author}{album.title}'
# album有一个内置字段 authoroname效果类似
```
### 文件夹名=禁漫车号+标题
```python
from jmcomic import JmModuleConfig
# Pmyname
JmModuleConfig.PFIELD_ADVICE['myname'] = lambda photo: f'【{photo.id}{photo.title}'
```
### 文件夹名=第x话+标题
```yaml
# 直接使用内置字段 indextitle 即可
dir_rule:
rule: Bd_Pindextitle
```