Skip to content

Commit a7e8ea2

Browse files
authored
v2.4.6: 实现对本子名称进行分词并提取原始名称;实现更便捷的自定义下载文件夹名机制、并跟进对应文档;内置一些更简洁的、可用作文件夹名的字段oname, idoname, authoroname. (#172)
1 parent 7bcc28f commit a7e8ea2

File tree

6 files changed

+260
-9
lines changed

6 files changed

+260
-9
lines changed
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# 自定义下载文件夹名
2+
3+
4+
5+
## 1. DirRule简介
6+
7+
当你使用download_album下载本子时,本子会以一定的路径规则(DirRule)下载到你的磁盘上。
8+
9+
你可以使用配置文件定制DirRule,例如下面的例子
10+
11+
```yml
12+
dir_rule:
13+
base_dir: D:/a/b/c/
14+
# 规则含义: 根目录 / 章节标题 / 图片文件
15+
rule: Bd_Ptitle # P表示章节,title表示使用章节的title字段
16+
```
17+
18+
如果一个章节的名称(title)是ddd,则最后的下载文件夹结构为:
19+
20+
```
21+
D:/a/b/c/ddd/00001.webp
22+
D:/a/b/c/ddd/00002.webp
23+
D:/a/b/c/ddd/00003.webp
24+
...
25+
```
26+
27+
28+
29+
## 2. 自定义字段名
30+
31+
上述例子使用了title字段,如果你想自定义一个字段,然后在DirRule中使用自定义字段,该怎么做?
32+
33+
基于v2.4.6,你可以使用如下方式
34+
35+
36+
37+
1. 给你的自定义字段取个名
38+
39+
```yml
40+
dir_rule: # 忽略base_dir配置项
41+
rule: Bd_Amyname # A表示本子,myname表示本子的一个自定义字段
42+
```
43+
44+
45+
46+
2. 在代码中,加入你自定义字段的处理函数
47+
48+
```python
49+
from jmcomic import JmModuleConfig
50+
# 你需要写一个函数,把字段名作为key,函数作为value,加到JmModuleConfig.AFIELD_ADVICE这个字典中
51+
JmModuleConfig.AFIELD_ADVICE['myname'] = lambda album: f'[{album.id}] {album.title}'
52+
```
53+
54+
55+
56+
这样一来,Amyname这个规则就会交由你的函数进行处理,你便可以返回一个自定义的文件夹名
57+
58+
59+
60+
61+
62+
## 3. 更多的使用例子
63+
64+
65+
66+
### 完全使用自己的文件夹名
67+
68+
```python
69+
from jmcomic import JmModuleConfig
70+
71+
dic = {
72+
'248965': '社团学姐(爆赞韩漫)'
73+
}
74+
75+
# Amyname
76+
JmModuleConfig.AFIELD_ADVICE['myname'] = lambda album: dic[album.id]
77+
download_album(248965)
78+
```
79+
80+
81+
82+
### 文件夹名=作者+标题
83+
84+
```python
85+
from jmcomic import JmModuleConfig
86+
# Amyname
87+
JmModuleConfig.AFIELD_ADVICE['myname'] = lambda album: f'{album.author}{album.title}'
88+
# album有一个内置字段 authoroname,效果类似
89+
```
90+
91+
92+
93+
### 文件夹名=禁漫车号+标题
94+
95+
```python
96+
from jmcomic import JmModuleConfig
97+
# Pmyname
98+
JmModuleConfig.PFIELD_ADVICE['myname'] = lambda photo: f'{photo.id}{photo.title}'
99+
```
100+
101+
102+
103+
### 文件夹名=第x话+标题
104+
105+
```python
106+
# 直接使用内置字段 indextitle 即可
107+
dir_rule:
108+
rule: Bd_Pindextitle
109+
```
110+
111+
112+

src/jmcomic/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# 被依赖方 <--- 使用方
33
# config <--- entity <--- toolkit <--- client <--- option <--- downloader
44

5-
__version__ = '2.4.5'
5+
__version__ = '2.4.6'
66

77
from .api import *
88
from .jm_plugin import *

src/jmcomic/jm_config.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,12 @@ class JmModuleConfig:
151151
# log时解码url
152152
flag_decode_url_when_logging = True
153153

154+
# 关联dir_rule的自定义字段与对应的处理函数
155+
# 例如:
156+
# Amyname -> JmModuleConfig.AFIELD_ADVICE['myname'] = lambda album: "自定义名称"
157+
AFIELD_ADVICE = dict()
158+
PFIELD_ADVICE = dict()
159+
154160
@classmethod
155161
def downloader_class(cls):
156162
if cls.CLASS_DOWNLOADER is not None:

src/jmcomic/jm_entity.py

Lines changed: 73 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,58 @@ def id(self) -> str:
6161
def title(self) -> str:
6262
return getattr(self, 'name')
6363

64+
@property
65+
def author(self):
66+
raise NotImplementedError
67+
68+
@property
69+
def oname(self) -> str:
70+
"""
71+
oname = original name
72+
73+
示例:
74+
75+
title:"喂我吃吧 老師! [欶瀾漢化組] [BLVEFO9] たべさせて、せんせい! (ブルーアーカイブ) [中國翻譯] [無修正]"
76+
77+
oname:"喂我吃吧 老師!"
78+
79+
:return: 返回本子的原始名称
80+
"""
81+
from .jm_toolkit import JmcomicText
82+
oname = JmcomicText.parse_orig_album_name(self.title)
83+
if oname is not None:
84+
return oname
85+
86+
jm_log('entity', f'无法提取出原album名字: {self.title}')
87+
return self.title
88+
89+
@property
90+
def authoroname(self):
91+
"""
92+
authoroname = author + oname
93+
94+
比较好识别的一种本子名称方式
95+
96+
具体格式: f'【author】{oname}'
97+
98+
示例:
99+
100+
原本子名:喂我吃吧 老師! [欶瀾漢化組] [BLVEFO9] たべさせて、せんせい! (ブルーアーカイブ) [中國翻譯] [無修正]
101+
102+
authoroname:【BLVEFO9】喂我吃吧 老師!
103+
104+
:return: 返回作者名+作品原名,格式为: '【author】{oname}'
105+
"""
106+
return f'【{self.author}{self.oname}'
107+
108+
@property
109+
def idoname(self):
110+
"""
111+
类似 authoroname
112+
:return: '[id] {oname}'
113+
"""
114+
return f'[{self.id}] {self.oname}'
115+
64116
def __str__(self):
65117
return f'{self.__class__.__name__}({self.id}-{self.title})'
66118

@@ -71,19 +123,33 @@ def __alias__(cls):
71123
cls_name = cls.__name__
72124
return cls_name[cls_name.index("m") + 1: cls_name.rfind("Detail")].lower()
73125

74-
def get_dirname(self, ref: str) -> str:
126+
@classmethod
127+
def get_dirname(cls, detail: 'DetailEntity', ref: str) -> str:
75128
"""
76129
该方法被 DirDule 调用,用于生成特定层次的文件夹
130+
77131
通常调用方式如下:
78-
Atitle -> ref = 'title' -> album.get_dirname(ref)
79-
该方法需要返回 ref 对应的文件夹名,默认实现直接返回 getattr(self, ref)
132+
Atitle -> ref = 'title' -> DetailEntity.get_dirname(album, 'title')
133+
该方法需要返回 ref 对应的文件夹名,默认实现直接返回 getattr(detail, 'title')
80134
81135
用户可重写此方法,来实现自定义文件夹名
82136
137+
v2.4.5: 此方法支持优先从 JmModuleConfig.XFIELD_ADVICE 中获取自定义函数并调用返回结果
138+
139+
:param detail: 本子/章节 实例
83140
:param ref: 字段名
84141
:returns: 文件夹名
85142
"""
86-
return getattr(self, ref)
143+
144+
advice_func = (JmModuleConfig.AFIELD_ADVICE
145+
if isinstance(detail, JmAlbumDetail)
146+
else JmModuleConfig.PFIELD_ADVICE
147+
).get(ref, None)
148+
149+
if advice_func is not None:
150+
return advice_func(detail)
151+
152+
return getattr(detail, ref)
87153

88154

89155
class JmImageDetail(JmBaseEntity):
@@ -110,7 +176,7 @@ def __init__(self,
110176

111177
self.from_photo: Optional[JmPhotoDetail] = from_photo
112178
self.query_params: StrNone = query_params
113-
self.index = index
179+
self.index = index # 从1开始
114180

115181
# temp fields, in order to simplify passing parameter
116182
self.save_path: str = ''
@@ -171,7 +237,7 @@ def tag(self) -> str:
171237
"""
172238
this tag is used to print pretty info when logging
173239
"""
174-
return f'{self.aid}/{self.img_file_name}{self.img_file_suffix} [{self.index + 1}/{len(self.from_photo)}]'
240+
return f'{self.aid}/{self.img_file_name}{self.img_file_suffix} [{self.index}/{len(self.from_photo)}]'
175241

176242
@classmethod
177243
def is_image(cls):
@@ -289,7 +355,7 @@ def create_image_detail(self, index) -> JmImageDetail:
289355
data_original,
290356
from_photo=self,
291357
query_params=self.data_original_query_params,
292-
index=index,
358+
index=index + 1,
293359
)
294360

295361
def get_img_data_original(self, img_name: str) -> str:

src/jmcomic/jm_option.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ def get_rule_solver(cls, rule: str) -> Optional[RuleSolver]:
132132

133133
# Axxx or Pyyy
134134
key = 1 if rule[0] == 'A' else 2
135-
solve_func = lambda detail, ref=rule[1:]: fix_windir_name(str(detail.get_dirname(ref)))
135+
solve_func = lambda detail, ref=rule[1:]: fix_windir_name(str(DetailEntity.get_dirname(detail, ref)))
136136

137137
# 保存缓存
138138
rule_solver = (key, solve_func, rule)

src/jmcomic/jm_toolkit.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,73 @@ def parse_to_abspath(cls, dsl_text: str) -> str:
225225
def parse_dsl_text(cls, dsl_text: str) -> str:
226226
return cls.dsl_replacer.parse_dsl_text(dsl_text)
227227

228+
bracket_map = {'(': ')',
229+
'[': ']',
230+
'【': '】',
231+
'(': ')',
232+
}
233+
234+
@classmethod
235+
def parse_orig_album_name(cls, name: str, default=None):
236+
word_list = cls.tokenize(name)
237+
238+
for word in word_list:
239+
if word[0] in cls.bracket_map:
240+
continue
241+
242+
return word
243+
244+
return default
245+
246+
@classmethod
247+
def tokenize(cls, title: str) -> List[str]:
248+
"""
249+
繞道#2 [暴碧漢化組] [えーすけ(123)] よりみち#2 (COMIC 快樂天 2024年1月號) [中國翻譯] [DL版]
250+
:return: ['繞道#2', '[暴碧漢化組]', '[えーすけ(123)]', 'よりみち#2', '(COMIC 快樂天 2024年1月號)', '[中國翻譯]', '[DL版]']
251+
"""
252+
title = title.strip()
253+
ret = []
254+
bracket_map = cls.bracket_map
255+
256+
char_list = []
257+
i = 0
258+
length = len(title)
259+
260+
def add(w=None):
261+
if w is None:
262+
w = ''.join(char_list).strip()
263+
264+
if w == '':
265+
return
266+
267+
ret.append(w)
268+
char_list.clear()
269+
270+
while i < length:
271+
c = title[i]
272+
273+
if c in bracket_map:
274+
# 上一个单词结束
275+
add()
276+
# 定位右括号
277+
j = title.find(bracket_map[c], i)
278+
ExceptionTool.require_true(j != -1, f'未闭合的 {c}{bracket_map[c]}: {title[i:]}')
279+
# 整个括号的单词结束
280+
add(title[i:j + 1])
281+
# 移动指针
282+
i = j + 1
283+
else:
284+
char_list.append(c)
285+
i += 1
286+
287+
add()
288+
return ret
289+
290+
@classmethod
291+
def to_zh_cn(cls, s):
292+
import zhconv
293+
return zhconv.convert(s, 'zh_cn')
294+
228295

229296
# 支持dsl: #{???} -> os.getenv(???)
230297
JmcomicText.dsl_replacer.add_dsl_and_replacer(r'\$\{(.*?)\}', JmcomicText.match_os_env)

0 commit comments

Comments
 (0)