Skip to content

Commit 7a9512b

Browse files
authored
Merge pull request #217 from Ljzd-PRO/devel
Bump to v0.13.0
2 parents f1999f7 + 4165ef7 commit 7a9512b

File tree

8 files changed

+225
-143
lines changed

8 files changed

+225
-143
lines changed

CHANGELOG.md

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,61 @@
11
## Changes
22

3-
![Downloads](https://img.shields.io/github/downloads/Ljzd-PRO/KToolBox/v0.12.0/total)
3+
![Downloads](https://img.shields.io/github/downloads/Ljzd-PRO/KToolBox/v0.13.0/total)
44

55
### 💡 Feature
66

7-
- Supports resuming downloads using temporary files to restore the download process - (#205) by @kgbq3nu6s9ptn3jr
7+
- Add support for customizing the `Post.file` filename format (not `Post.attachments`)
8+
- Run `ktoolbox config-editor` to edit the configuration (`Job -> post_structure -> file`)
9+
- Or manually edit `KTOOLBOX_JOB__POST_STRUCTURE__FILE` in `.env` file or environment variables to set this option
10+
```dotenv
11+
# Example
12+
# Result filename: [2023-01-01]_TheTitle_12345_UxTleZ3zP6LHA7BPNxlEWDzX.jpg
13+
KTOOLBOX_JOB__POST_STRUCTURE__FILE=[{published}]_{title}_{id}_{}
14+
```
15+
- 📖More information: [Configuration-Reference-JobConfiguration](https://ktoolbox.readthedocs.io/latest/configuration/reference/#ktoolbox.configuration.JobConfiguration)
16+
17+
- Add support for customizing reverse proxy for download URLs - (#216)
18+
- Run `ktoolbox config-editor` to edit the configuration (`Downloader -> reverse_proxy`)
19+
- Or manually edit `KTOOLBOX_DOWNLOADER__REVERSE_PROXY` in `.env` file or environment variables to set this option
20+
```dotenv
21+
# Example
22+
# Result download URL: https://example.com/https://n1.kemono.su/data/66/83/xxxx.jpg
23+
KTOOLBOX_DOWNLOADER__REVERSE_PROXY="https://example.com/{}"
24+
```
25+
- 📖More information: [Configuration-Reference-DownloaderConfiguration](https://ktoolbox.readthedocs.io/latest/configuration/reference/#ktoolbox.configuration.DownloaderConfiguration)
26+
27+
- Rename `PostStructure.content_filepath` to `PostStructure.content`, the old configuration is still available, but it will be removed in the future. If you use this option, you will receive a warning message.
28+
- 📖More information: [Configuration-Reference-PostStructureConfiguration](https://ktoolbox.readthedocs.io/latest/configuration/reference/#ktoolbox.configuration.PostStructureConfiguration)
829
930
[//]: # (### 🪲 Fix)
1031
1132
- - -
1233
1334
### 💡 新特性
1435
15-
- 支持断点续传,借助临时下载文件恢复下载 - (#205) by @kgbq3nu6s9ptn3jr
36+
- 增加支持自定义 `Post.file` 的文件名格式(并非 `Post.attachments`)
37+
- 执行 `ktoolbox config-editor` 来编辑这项配置 (`Job -> post_structure -> file`)
38+
- 或手动编辑 `.env` 文件中的 `KTOOLBOX_JOB__POST_STRUCTURE__FILE` 或环境变量来设置这项配置
39+
```dotenv
40+
# 示例
41+
# 文件名:[2023-01-01]_TheTitle_12345_UxTleZ3zP6LHA7BPNxlEWDzX.jpg
42+
KTOOLBOX_JOB__POST_STRUCTURE__FILE=[{published}]_{title}_{id}_{}
43+
```
44+
- 📖更多信息:[Configuration-Reference-JobConfiguration](https://ktoolbox.readthedocs.io/latest/configuration/reference/#ktoolbox.configuration.JobConfiguration)
45+
46+
- 增加支持自定义下载 URL 的反向代理 - (#216)
47+
- 执行 `ktoolbox config-editor` 来编辑这项配置 (`Downloader -> reverse_proxy`)
48+
- 或手动编辑 `.env` 文件中的 `KTOOLBOX_DOWNLOADER__REVERSE_PROXY` 或环境变量来设置这项配置
49+
```dotenv
50+
# 示例
51+
# 下载 URL:https://example.com/https://n1.kemono.su/data/66/83/xxxx.jpg
52+
KTOOLBOX_DOWNLOADER__REVERSE_PROXY="https://example.com/{}"
53+
```
54+
- 📖更多信息:[Configuration-Reference-DownloaderConfiguration](https://ktoolbox.readthedocs.io/latest/configuration/reference/#ktoolbox.configuration.DownloaderConfiguration)
55+
56+
- `PostStructure.content_filepath` 更名为 `PostStructure.content`,原先的配置仍可继续使用,但未来将会被移除。如果您使用了这项配置,将会收到警告信息。
57+
- 📖更多信息:[Configuration-Reference-PostStructureConfiguration](https://ktoolbox.readthedocs.io/latest/configuration/reference/#ktoolbox.configuration.PostStructureConfiguration)
1658
1759
[//]: # (### 🪲 修复)
1860
19-
**Full Changelog**: https://github.com/Ljzd-PRO/KToolBox/compare/v0.11.1...v0.12.0
61+
**Full Changelog**: https://github.com/Ljzd-PRO/KToolBox/compare/v0.12.0...v0.13.0

ktoolbox/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
__title__ = "KToolBox"
22
# noinspection SpellCheckingInspection
33
__description__ = "A useful CLI tool for downloading posts in Kemono.party / .su"
4-
__version__ = "v0.12.0"
4+
__version__ = "v0.13.0"

ktoolbox/action/job.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ async def create_job_from_post(
4343
if post_structure:
4444
attachments_path = post_path / post_structure.attachments # attachments
4545
attachments_path.mkdir(exist_ok=True)
46-
content_path = post_path / post_structure.content_filepath # content
46+
content_path = post_path / post_structure.content # content
4747
content_path.parent.mkdir(exist_ok=True)
4848
else:
4949
attachments_path = post_path
@@ -69,7 +69,7 @@ async def create_job_from_post(
6969
)
7070
):
7171
basic_filename = f"{i + 1}{file_path_obj.suffix}" if config.job.sequential_filename else file_path_obj.name
72-
alt_filename = generate_filename(post, basic_filename)
72+
alt_filename = generate_filename(post, basic_filename, config.job.filename_format)
7373
jobs.append(
7474
Job(
7575
path=attachments_path,
@@ -84,6 +84,7 @@ async def create_job_from_post(
8484
post_file_name = Path(post.file.name) if is_valid_filename(post.file.name) else Path(
8585
urlparse(post.file.path).path
8686
)
87+
post_file_name = Path(generate_filename(post, post_file_name.name, config.job.post_structure.file))
8788
if (not config.job.allow_list or any(
8889
map(
8990
lambda x: fnmatch(post_file_name.name, x),
@@ -98,7 +99,7 @@ async def create_job_from_post(
9899
jobs.append(
99100
Job(
100101
path=post_path,
101-
alt_filename=f"{post.id}_{post_file_name.name}",
102+
alt_filename=post_file_name.name,
102103
server_path=post.file.path,
103104
type=PostFileTypeEnum.File
104105
)

ktoolbox/action/utils.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ def generate_post_path_name(post: Post) -> str:
3636
exit(1)
3737

3838

39-
def generate_filename(post: Post, basic_name: str) -> str:
39+
def generate_filename(post: Post, basic_name: str, filename_format: str) -> str:
4040
"""Generate download filename"""
41-
basic_name_suffix = Path(basic_name).suffix
42-
basic_name_filename = basic_name.split(basic_name_suffix)[0] if basic_name_suffix else basic_name
41+
basic_name_path = Path(basic_name)
42+
basic_name_filename = basic_name.replace(basic_name_path.suffix, "")
4343
try:
4444
return sanitize_filename(
45-
config.job.filename_format.format(
45+
filename_format.format(
4646
basic_name_filename,
4747
id=post.id,
4848
user=post.user,
@@ -51,10 +51,11 @@ def generate_filename(post: Post, basic_name: str) -> str:
5151
added=post.added.strftime(TIME_FORMAT) if post.added else "",
5252
published=post.published.strftime(TIME_FORMAT) if post.published else "",
5353
edited=post.edited.strftime(TIME_FORMAT) if post.edited else ""
54-
) + basic_name_suffix
54+
) + basic_name_path.suffix
5555
)
5656
except KeyError as e:
57-
logger.error(f"`JobConfiguration.filename_format` contains invalid key: {e}")
57+
logger.error(
58+
f"`JobConfiguration.filename_format` or `PostStructureConfiguration.file` contains invalid key: {e}")
5859
exit(1)
5960

6061

ktoolbox/configuration.py

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
import logging
33
import os
44
import tempfile
5+
import warnings
56
from pathlib import Path
6-
from typing import Literal, Union, Optional, Set
7+
from typing import Literal, Union, Optional, Set, ClassVar
78

89
from loguru import logger
9-
from pydantic import BaseModel, model_validator
10+
from pydantic import BaseModel, model_validator, Field, field_validator
1011
from pydantic_settings import BaseSettings, SettingsConfigDict
1112

1213
__all__ = [
@@ -60,6 +61,10 @@ class DownloaderConfiguration(BaseModel):
6061
:ivar retry_interval: Seconds of downloader retry interval
6162
:ivar use_bucket: Enable local storage bucket mode
6263
:ivar bucket_path: Path of local storage bucket
64+
:ivar reverse_proxy: Reverse proxy format for download URL. \
65+
Customize the filename format by inserting an empty ``{}`` to represent the original URL. \
66+
For example: ``https://example.com/{}`` will be ``https://example.com/https://n1.kemono.su/data/66/83/xxxxx.jpg``; \
67+
``https://example.com/?url={}`` will be ``https://example.com/?url=https://n1.kemono.su/data/66/83/xxxxx.jpg``
6368
"""
6469
scheme: Literal["http", "https"] = "https"
6570
timeout: float = 30.0
@@ -72,6 +77,7 @@ class DownloaderConfiguration(BaseModel):
7277
retry_interval: float = 3.0
7378
use_bucket: bool = False
7479
bucket_path: Path = Path("./.ktoolbox/bucket_storage")
80+
reverse_proxy: str = "{}"
7581

7682
@model_validator(mode="after")
7783
def check_bucket_path(self) -> "DownloaderConfiguration":
@@ -107,18 +113,48 @@ class PostStructureConfiguration(BaseModel):
107113
└─ 2.png
108114
```
109115
116+
- Available properties for ``file``
117+
118+
| Property | Type |
119+
|---------------|--------|
120+
| ``id`` | String |
121+
| ``user`` | String |
122+
| ``service`` | String |
123+
| ``title`` | String |
124+
| ``added`` | Date |
125+
| ``published`` | Date |
126+
| ``edited`` | Date |
127+
110128
:ivar attachments: Sub path of attachment directory
111-
:ivar content_filepath: Sub path of post content file
129+
:ivar content: Sub path of post content file
130+
:ivar content_filepath: (**Deprecated**, Use ``content`` instead) Sub path of post content file
131+
:ivar file: The format of the post `file` filename (`file` is not `attachment`, each post has only one `file`, usually the cover image) \
132+
Customize the filename format by inserting an empty ``{}`` to represent the basic filename. \
133+
You can use some of the [properties][ktoolbox.configuration.JobConfiguration] \
134+
in Post. For example: ``{title}_{}`` could result in filenames like \
135+
``TheTitle_Stelle_lv5_logo.gif``, ``TheTitle_ScxHjZIdxt5cnjaAwf3ql2p7.jpg``, etc.
112136
"""
113137
attachments: Path = Path("attachments")
138+
content: Path = Path("content.txt")
114139
content_filepath: Path = Path("content.txt")
140+
file: str = "{id}_{}"
141+
142+
@field_validator("content_filepath")
143+
def content_filepath_validator(cls, v):
144+
# noinspection PyUnresolvedReferences
145+
if v != cls.model_fields["content_filepath"].default:
146+
warnings.warn(
147+
"`PostStructureConfiguration.content_filepath` is deprecated and is scheduled for removal in further version. "
148+
"Use `PostStructureConfiguration.content` instead",
149+
FutureWarning
150+
)
115151

116152

117153
class JobConfiguration(BaseModel):
118154
"""
119155
Download jobs Configuration
120156
121-
- Available properties for ``post_dirname_format``
157+
- Available properties for ``post_dirname_format`` and ``filename_format``
122158
123159
| Property | Type |
124160
|---------------|--------|
@@ -133,15 +169,15 @@ class JobConfiguration(BaseModel):
133169
:ivar count: Number of coroutines for concurrent download
134170
:ivar post_dirname_format: Customize the post directory name format, you can use some of the \
135171
[properties][ktoolbox.configuration.JobConfiguration] in ``Post``. \
136-
e.g. ``[{published}]{id}`` > ``[2024-1-1]123123``, ``{user}_{published}_{title}`` > ``234234_2024-1-1_HelloWorld``
172+
e.g. ``[{published}]{id}`` > ``[2024-1-1]123123``, ``{user}_{published}_{title}`` > ``234234_2024-1-1_TheTitle``
137173
:ivar post_structure: Post path structure
138174
:ivar mix_posts: Save all files from different posts at same path in creator directory. \
139175
It would not create any post directory, and ``CreatorIndices`` would not been recorded.
140176
:ivar sequential_filename: Rename attachments in numerical order, e.g. ``1.png``, ``2.png``, ...
141177
:ivar filename_format: Customize the filename format by inserting an empty ``{}`` to represent the basic filename.
142178
Similar to post_dirname_format, you can use some of the [properties][ktoolbox.configuration.JobConfiguration] \
143179
in Post. For example: ``{title}_{}`` could result in filenames like \
144-
``HelloWorld_b4b41de2-8736-480d-b5c3-ebf0d917561b``, ``HelloWorld_af349b25-ac08-46d7-98fb-6ce99a237b90``, etc. \
180+
``TheTitle_b4b41de2-8736-480d-b5c3-ebf0d917561b``, ``TheTitle_af349b25-ac08-46d7-98fb-6ce99a237b90``, etc. \
145181
You can also use it with ``sequential_filename``. For instance, \
146182
``[{published}]_{}`` could result in filenames like ``[2024-1-1]_1.png``, ``[2024-1-1]_2.png``, etc.
147183
:ivar allow_list: Download files which match these patterns (Unix shell-style), e.g. ``["*.png"]``
@@ -153,8 +189,10 @@ class JobConfiguration(BaseModel):
153189
mix_posts: bool = False
154190
sequential_filename: bool = False
155191
filename_format: str = "{}"
156-
allow_list: Set[str] = set()
157-
block_list: Set[str] = set()
192+
# noinspection PyDataclass
193+
allow_list: Set[str] = Field(default_factory=set)
194+
# noinspection PyDataclass
195+
block_list: Set[str] = Field(default_factory=set)
158196

159197

160198
class LoggerConfiguration(BaseModel):
@@ -195,13 +233,13 @@ class Configuration(BaseSettings):
195233
use_uvloop: bool = True
196234

197235
# noinspection SpellCheckingInspection
198-
model_config = SettingsConfigDict(
236+
model_config: ClassVar[SettingsConfigDict] = SettingsConfigDict(
199237
env_prefix='ktoolbox_',
200238
env_nested_delimiter='__',
201-
env_file='.env',
239+
env_file=['.env', 'prod.env'],
202240
env_file_encoding='utf-8',
203241
extra='ignore'
204242
)
205243

206244

207-
config = Configuration(_env_file=['.env', 'prod.env'])
245+
config = Configuration()

ktoolbox/downloader/downloader.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def __init__(
5959
it will be used as the save path.
6060
"""
6161

62-
self._url = url
62+
self._url = config.downloader.reverse_proxy.format(url)
6363
self._path = path
6464
self._buffer_size = buffer_size or config.downloader.buffer_size
6565
self._chunk_size = chunk_size or config.downloader.chunk_size

0 commit comments

Comments
 (0)