feat: 支持按作者归档保存作品文件

BREAKING CHANGE: 新增配置文件参数:account_archive

Closes #226
Closes #229
This commit is contained in:
2025-03-12 22:01:01 +08:00
parent ea63059dbf
commit c2fa98bb51
9 changed files with 84 additions and 36 deletions

View File

@ -214,6 +214,7 @@ async def example():
"""通过代码设置参数,适合二次开发"""
# 示例链接
demo_link = "https://www.xiaohongshu.com/explore/XXX?xsec_token=XXX"
# 实例对象
work_path = "D:\\" # 作品数据/文件保存根路径,默认值:项目根路径
folder_name = "Download" # 作品文件储存文件夹名称自动创建默认值Download
@ -232,9 +233,12 @@ async def example():
live_download = False # 图文动图文件下载开关
download_record = True # 是否记录下载成功的作品 ID
language = "zh_CN" # 设置程序提示语言
account_archive = True # 是否将每个作者的作品存至单独的文件夹
read_cookie = None # 读取浏览器 Cookie支持设置浏览器名称字符串或者浏览器序号整数设置为 None 代表不读取
# async with XHS() as xhs:
# pass # 使用默认参数
async with XHS(
work_path=work_path,
folder_name=folder_name,
@ -254,6 +258,7 @@ async def example():
download_record=download_record,
language=language,
read_cookie=read_cookie,
account_archive=account_archive,
) as xhs: # 使用自定义参数
download = True # 是否下载作品文件默认值False
# 返回作品详细信息,包括下载地址
@ -376,6 +381,12 @@ async def example():
<td align="center">true</td>
</tr>
<tr>
<td align="center">account_archive</td>
<td align="center">bool</td>
<td align="center">是否将每个作者的作品储存至单独的文件夹;文件夹名称为作者昵称或者作者 ID</td>
<td align="center">false</td>
</tr>
<tr>
<td align="center">language</td>
<td align="center">str</td>
<td align="center">设置程序语言,目前支持:<code>zh_CN</code><code>en_US</code></td>

View File

@ -218,6 +218,7 @@ async def example():
"""通过代码设置参数,适合二次开发"""
# 示例链接
demo_link = "https://www.xiaohongshu.com/explore/XXX?xsec_token=XXX"
# 实例对象
work_path = "D:\\" # 作品数据/文件保存根路径,默认值:项目根路径
folder_name = "Download" # 作品文件储存文件夹名称自动创建默认值Download
@ -236,9 +237,12 @@ async def example():
live_download = False # 图文动图文件下载开关
download_record = True # 是否记录下载成功的作品 ID
language = "zh_CN" # 设置程序提示语言
account_archive = True # 是否将每个作者的作品存至单独的文件夹
read_cookie = None # 读取浏览器 Cookie支持设置浏览器名称字符串或者浏览器序号整数设置为 None 代表不读取
# async with XHS() as xhs:
# pass # 使用默认参数
async with XHS(
work_path=work_path,
folder_name=folder_name,
@ -258,6 +262,7 @@ async def example():
download_record=download_record,
language=language,
read_cookie=read_cookie,
account_archive=account_archive,
) as xhs: # 使用自定义参数
download = True # 是否下载作品文件默认值False
# 返回作品详细信息,包括下载地址
@ -380,6 +385,12 @@ async def example():
<td align="center">true</td>
</tr>
<tr>
<td align="center">account_archive</td>
<td align="center">bool</td>
<td align="center">Whether to save each author's works into a separate folder; the folder name will be the author's nickname or author ID</td>
<td align="center">false</td>
</tr>
<tr>
<td align="center">language</td>
<td align="center">str</td>
<td align="center">Set program language. Currently supported: <code>zh_CN</code>, <code>en_US</code></td>

View File

@ -28,30 +28,32 @@ async def example():
live_download = False # 图文动图文件下载开关
download_record = True # 是否记录下载成功的作品 ID
language = "zh_CN" # 设置程序提示语言
account_archive = True # 是否将每个作者的作品存至单独的文件夹
read_cookie = None # 读取浏览器 Cookie支持设置浏览器名称字符串或者浏览器序号整数设置为 None 代表不读取
# async with XHS() as xhs:
# pass # 使用默认参数
async with XHS(
work_path=work_path,
folder_name=folder_name,
name_format=name_format,
user_agent=user_agent,
cookie=cookie,
proxy=proxy,
timeout=timeout,
chunk=chunk,
max_retry=max_retry,
record_data=record_data,
image_format=image_format,
folder_mode=folder_mode,
image_download=image_download,
video_download=video_download,
live_download=live_download,
download_record=download_record,
language=language,
read_cookie=read_cookie,
work_path=work_path,
folder_name=folder_name,
name_format=name_format,
user_agent=user_agent,
cookie=cookie,
proxy=proxy,
timeout=timeout,
chunk=chunk,
max_retry=max_retry,
record_data=record_data,
image_format=image_format,
folder_mode=folder_mode,
image_download=image_download,
video_download=video_download,
live_download=live_download,
download_record=download_record,
language=language,
read_cookie=read_cookie,
account_archive=account_archive,
) as xhs: # 使用自定义参数
download = True # 是否下载作品文件默认值False
# 返回作品详细信息,包括下载地址
@ -62,13 +64,13 @@ async def example():
async def test():
url = ""
async with XHS(
download_record=False,
# image_format="PNG",
# image_format="WEBP",
# image_format="JPEG",
# image_format="HEIC",
# image_format="AVIF",
# image_format="AUTO",
download_record=False,
# image_format="PNG",
# image_format="WEBP",
# image_format="JPEG",
# image_format="HEIC",
# image_format="AVIF",
# image_format="AUTO",
) as xhs:
print(
await xhs.extract(

View File

@ -24,8 +24,8 @@ class Setting(Screen):
]
def __init__(
self,
data: dict,
self,
data: dict,
):
super().__init__()
self.data = data
@ -129,7 +129,7 @@ class Setting(Screen):
value=self.data["record_data"],
),
Checkbox(
_("作品文件夹归档模式"),
_("作品归档保存模式"),
id="folder_mode",
value=self.data["folder_mode"],
),
@ -157,6 +157,11 @@ class Setting(Screen):
id="download_record",
value=self.data["download_record"],
),
Checkbox(
_("作者归档保存模式"),
id="account_archive",
value=self.data["account_archive"],
),
classes="horizontal-layout",
),
Container(
@ -229,6 +234,7 @@ class Setting(Screen):
"video_download": self.query_one("#video_download").value,
"live_download": self.query_one("#live_download").value,
"download_record": self.query_one("#download_record").value,
"account_archive": self.query_one("#account_archive").value,
}
)

View File

@ -100,6 +100,7 @@ class XHS:
live_download=False,
folder_mode=False,
download_record=True,
account_archive=False,
language="zh_CN",
read_cookie: int | str = None,
_print: bool = True,
@ -125,6 +126,7 @@ class XHS:
live_download,
download_record,
folder_mode,
account_archive,
_print,
)
self.html = Html(self.manager)
@ -170,6 +172,7 @@ class XHS:
u,
container["动图地址"],
index,
self.CLEANER.filter_name(container["作者昵称"]) or container["作者ID"],
name,
container["作品类型"],
log,

View File

@ -63,23 +63,25 @@ class Download:
self.image_download = manager.image_download
self.video_download = manager.video_download
self.live_download = manager.live_download
self.account_archive = manager.account_archive
async def run(
self,
urls: list,
lives: list,
index: list | tuple | None,
name: str,
nickname: str,
filename: str,
type_: str,
log,
bar,
) -> tuple[Path, list[Any]]:
path = self.__generate_path(name)
path = self.__generate_path(nickname, filename)
if type_ == _("视频"):
tasks = self.__ready_download_video(
urls,
path,
name,
filename,
log,
)
elif type_ == _("图文"):
@ -88,7 +90,7 @@ class Download:
lives,
index,
path,
name,
filename,
log,
)
else:
@ -107,8 +109,13 @@ class Download:
tasks = await gather(*tasks)
return path, tasks
def __generate_path(self, name: str):
path = self.manager.archive(self.folder, name, self.folder_mode)
def __generate_path(self, nickname:str, filename: str):
if self.account_archive:
folder = self.folder.joinpath(nickname)
folder.mkdir(exist_ok=True)
else:
folder = self.folder
path = self.manager.archive(folder, filename, self.folder_mode)
path.mkdir(exist_ok=True)
return path

View File

@ -65,6 +65,7 @@ class Manager:
live_download: bool,
download_record: bool,
folder_mode: bool,
account_archive:bool,
_print: bool,
):
self.root = root
@ -115,6 +116,7 @@ class Manager:
self.image_download = self.check_bool(image_download, True)
self.video_download = self.check_bool(video_download, True)
self.live_download = self.check_bool(live_download, True)
self.account_archive = self.check_bool(account_archive, False)
def __check_path(self, path: str) -> Path:
if not path:

View File

@ -27,6 +27,7 @@ class Settings:
"live_download": False,
"folder_mode": False,
"download_record": True,
"account_archive": False,
"language": "zh_CN",
}
encode = "UTF-8-SIG" if system() == "Windows" else "UTF-8"

View File

@ -1,8 +1,13 @@
**项目更新内容:**
1. 增加对 `JPEG``HEIC` 图片格式的支持
2. 优化 `headers` 处理逻辑
3. 支持 `SOCKS` 代理
2. 支持按作者归档保存作品文件
3. 优化 `headers` 处理逻辑
4. 支持 `SOCKS` 代理
**注意:**
<p><strong>配置文件新增参数 <code>account_archive</code>,旧版本更新需要手动添加配置内容:<code>"account_archive": false</code>;或者直接删除旧版配置文件后再运行程序!</strong></p>
*****