mirror of
https://github.com/lifegpc/pixiv_downloader.git
synced 2026-06-21 19:34:23 +08:00
add support for download multiply images
This commit is contained in:
9
Cargo.lock
generated
9
Cargo.lock
generated
@@ -278,7 +278,9 @@ dependencies = [
|
||||
"encode_unicode",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"terminal_size",
|
||||
"unicode-width",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@@ -766,14 +768,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indicatif"
|
||||
version = "0.16.2"
|
||||
version = "0.17.0-rc.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d207dc617c7a380ab07ff572a6e52fa202a2a8f355860ac9c38e23f8196be1b"
|
||||
checksum = "088977cb4de4e09ea0232bd51c844490475ad94344fb0c60784f1fb380059b6d"
|
||||
dependencies = [
|
||||
"console",
|
||||
"lazy_static",
|
||||
"number_prefix",
|
||||
"regex",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -16,7 +16,7 @@ futures-util = "0.3"
|
||||
getopts = "0.2"
|
||||
gettext = "0.4"
|
||||
html_parser = "0.6.2"
|
||||
indicatif = "0.16"
|
||||
indicatif = "0.17.0-rc.10"
|
||||
int-enum = { version = "0.4", optional = true }
|
||||
json = "0.12"
|
||||
lazy_static = "1.4"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: pixiv_downloader\n"
|
||||
"POT-Creation-Date: 2022-03-26 15:53+0800\n"
|
||||
"POT-Creation-Date: 2022-03-28 21:09+0800\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <[email protected]>\n"
|
||||
@@ -115,15 +115,18 @@ msgstr ""
|
||||
msgid "Can not parse expired time:"
|
||||
msgstr ""
|
||||
|
||||
#: cookies.rs:366 data/json.rs:50 settings.rs:418 webclient.rs:302
|
||||
#: cookies.rs:366 data/json.rs:51 settings.rs:418 webclient.rs:343
|
||||
#: webclient.rs:345 webclient.rs:420
|
||||
msgid "Failed to remove file:"
|
||||
msgstr ""
|
||||
|
||||
#: cookies.rs:372 data/json.rs:61 settings.rs:425 webclient.rs:312
|
||||
#: cookies.rs:372 data/json.rs:62 settings.rs:425 webclient.rs:354
|
||||
#: webclient.rs:356 webclient.rs:430
|
||||
msgid "Failed to create file:"
|
||||
msgstr ""
|
||||
|
||||
#: cookies.rs:379 data/json.rs:67 settings.rs:431 webclient.rs:329
|
||||
#: cookies.rs:379 data/json.rs:68 settings.rs:431 webclient.rs:381
|
||||
#: webclient.rs:383 webclient.rs:447
|
||||
msgid "Failed to write file:"
|
||||
msgstr ""
|
||||
|
||||
@@ -131,84 +134,85 @@ msgstr ""
|
||||
msgid "Failed to unescape string:"
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:25 pixiv_web.rs:58
|
||||
#: download.rs:30 pixiv_web.rs:58
|
||||
msgid "Failed to initialize pixiv web api client."
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:34
|
||||
#: download.rs:39
|
||||
msgid "Warning: Web api client not logined, some future may not work."
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:73
|
||||
msgid "Failed to get page count."
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:82
|
||||
msgid "Failed to get pages' data."
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:95
|
||||
msgid "Failed to save metadata to JSON file."
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:110
|
||||
msgid "Failed to get ugoira's data."
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:116
|
||||
msgid "Can not find source link for ugoira."
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:122 download.rs:200 download.rs:274
|
||||
msgid "Failed to get file name from url:"
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:138 download.rs:144
|
||||
msgid "Failed to download ugoira:"
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:149
|
||||
msgid "Downloaded ugoira:"
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:159
|
||||
msgid "Warning: Failed to generate video's metadata:"
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:169
|
||||
msgid "Failed to convert from ugoira to mp4 video file:"
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:172
|
||||
msgid "Converted <src> -> <dest>"
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:175
|
||||
msgid "Failed to parse frames:"
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:182
|
||||
msgid "Warning: Unknown illust type:"
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:185
|
||||
msgid "Warning: Failed to get illust's type."
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:194 download.rs:268
|
||||
#: download.rs:58 download.rs:286 download.rs:360
|
||||
msgid "Failed to get original picture's link."
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:215 download.rs:254 download.rs:294 download.rs:324
|
||||
#: download.rs:64 download.rs:197 download.rs:292 download.rs:366
|
||||
msgid "Failed to get file name from url:"
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:79 download.rs:115 download.rs:307 download.rs:346
|
||||
#: download.rs:386 download.rs:416
|
||||
msgid "Failed to add exif data to image:"
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:234 download.rs:240 download.rs:304 download.rs:310
|
||||
#: pixiv_web.rs:153
|
||||
#: download.rs:100 download.rs:107 download.rs:326 download.rs:332
|
||||
#: download.rs:396 download.rs:402 pixiv_web.rs:153 pixiv_web.rs:169
|
||||
msgid "Failed to download image:"
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:245 download.rs:315
|
||||
#: download.rs:147
|
||||
msgid "Failed to get page count."
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:156
|
||||
msgid "Failed to get pages' data."
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:170
|
||||
msgid "Failed to save metadata to JSON file."
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:185
|
||||
msgid "Failed to get ugoira's data."
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:191
|
||||
msgid "Can not find source link for ugoira."
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:213 download.rs:219
|
||||
msgid "Failed to download ugoira:"
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:224
|
||||
msgid "Downloaded ugoira:"
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:234
|
||||
msgid "Warning: Failed to generate video's metadata:"
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:244
|
||||
msgid "Failed to convert from ugoira to mp4 video file:"
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:247
|
||||
msgid "Converted <src> -> <dest>"
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:250
|
||||
msgid "Failed to parse frames:"
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:257
|
||||
msgid "Warning: Unknown illust type:"
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:260
|
||||
msgid "Warning: Failed to get illust's type."
|
||||
msgstr ""
|
||||
|
||||
#: download.rs:337 download.rs:407 webclient.rs:390
|
||||
msgid "Downloaded image:"
|
||||
msgstr ""
|
||||
|
||||
@@ -220,103 +224,107 @@ msgstr ""
|
||||
msgid "Failed to parse value."
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:98
|
||||
#: opts.rs:101
|
||||
msgid "Warning: The specified config file not found."
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:122
|
||||
#: opts.rs:125
|
||||
msgid "Usage:"
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:124
|
||||
#: opts.rs:127
|
||||
msgid "Download an artwork"
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:126
|
||||
#: opts.rs:129
|
||||
msgid "Fix the config file"
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:128
|
||||
#: opts.rs:131
|
||||
msgid "Print all available settings"
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:136
|
||||
#: opts.rs:139
|
||||
msgid "Print help message."
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:140
|
||||
#: opts.rs:143
|
||||
msgid "The location of config file."
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:146 settings_list.rs:13
|
||||
#: opts.rs:149 settings_list.rs:13
|
||||
msgid "The location of cookies file. Used for web API."
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:152 settings_list.rs:14
|
||||
#: opts.rs:155 settings_list.rs:14
|
||||
msgid "The language of translated tags."
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:155
|
||||
#: opts.rs:158
|
||||
msgid "Verbose logging."
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:156
|
||||
#: opts.rs:159
|
||||
msgid "Overwrite existing file."
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:157
|
||||
#: opts.rs:160
|
||||
msgid "Skip overwrite existing file."
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:161 settings_list.rs:15
|
||||
#: opts.rs:164 settings_list.rs:15
|
||||
msgid "Max retry count if request failed."
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:167 settings_list.rs:16
|
||||
#: opts.rs:170 settings_list.rs:16
|
||||
msgid "The interval (in seconds) between two retries."
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:170 settings_list.rs:17
|
||||
#: opts.rs:173 settings_list.rs:17
|
||||
msgid "Use data from webpage first."
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:175 settings_list.rs:20
|
||||
#: opts.rs:178 settings_list.rs:20
|
||||
msgid "Add/Update exif information to image files even when overwrite are disabled."
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:180 settings_list.rs:22
|
||||
#: opts.rs:183 settings_list.rs:22
|
||||
msgid "Whether to enable progress bar."
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:202
|
||||
#: opts.rs:189 settings_list.rs:23
|
||||
msgid "Download multiple images at the same time."
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:210
|
||||
msgid "Unknown command."
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:212
|
||||
#: opts.rs:220
|
||||
msgid "Failed to parse ID:"
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:218
|
||||
#: opts.rs:226
|
||||
msgid "No URL or ID specified."
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:226
|
||||
#: opts.rs:234
|
||||
msgid "No detailed command specified."
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:239
|
||||
#: opts.rs:247
|
||||
msgid "Unknown config subcommand."
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:275
|
||||
#: opts.rs:283
|
||||
msgid "Retry count must be an non-negative integer:"
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:284
|
||||
#: opts.rs:292
|
||||
msgid "Failed to parse retry interval:"
|
||||
msgstr ""
|
||||
|
||||
#: opts.rs:298
|
||||
#: opts.rs:306
|
||||
msgid "Failed to parse <opt>:"
|
||||
msgstr ""
|
||||
|
||||
@@ -356,27 +364,27 @@ msgstr ""
|
||||
msgid "Failed to detect error."
|
||||
msgstr ""
|
||||
|
||||
#: pixiv_web.rs:168 pixiv_web.rs:198
|
||||
#: pixiv_web.rs:184 pixiv_web.rs:214
|
||||
msgid "Artwork's data:"
|
||||
msgstr ""
|
||||
|
||||
#: pixiv_web.rs:183 pixiv_web.rs:188
|
||||
#: pixiv_web.rs:199 pixiv_web.rs:204
|
||||
msgid "Failed to get artwork page:"
|
||||
msgstr ""
|
||||
|
||||
#: pixiv_web.rs:194
|
||||
#: pixiv_web.rs:210
|
||||
msgid "Failed to parse artwork page."
|
||||
msgstr ""
|
||||
|
||||
#: pixiv_web.rs:212
|
||||
#: pixiv_web.rs:228
|
||||
msgid "Artwork's page data:"
|
||||
msgstr ""
|
||||
|
||||
#: pixiv_web.rs:226
|
||||
#: pixiv_web.rs:242
|
||||
msgid "Ugoira's data:"
|
||||
msgstr ""
|
||||
|
||||
#: pixiv_web.rs:250
|
||||
#: pixiv_web.rs:266
|
||||
msgid "Warning: Failed to save cookies file:"
|
||||
msgstr ""
|
||||
|
||||
@@ -500,7 +508,7 @@ msgstr ""
|
||||
msgid "Do you want to delete file \"<file>\"?"
|
||||
msgstr ""
|
||||
|
||||
#: utils.rs:51 webclient.rs:154
|
||||
#: utils.rs:51 webclient.rs:156
|
||||
msgid "Can not parse URL:"
|
||||
msgstr ""
|
||||
|
||||
@@ -508,43 +516,43 @@ msgstr ""
|
||||
msgid "Failed to get file name from path:"
|
||||
msgstr ""
|
||||
|
||||
#: webclient.rs:114
|
||||
#: webclient.rs:116
|
||||
msgid "Failed to parse Set-Cookie header."
|
||||
msgstr ""
|
||||
|
||||
#: webclient.rs:119
|
||||
#: webclient.rs:121
|
||||
msgid "Failed to convert to string:"
|
||||
msgstr ""
|
||||
|
||||
#: webclient.rs:161
|
||||
#: webclient.rs:163
|
||||
msgid "Parameters should be object or array:"
|
||||
msgstr ""
|
||||
|
||||
#: webclient.rs:181
|
||||
#: webclient.rs:183
|
||||
msgid "Parameters should be array:"
|
||||
msgstr ""
|
||||
|
||||
#: webclient.rs:185
|
||||
#: webclient.rs:187
|
||||
msgid "Parameters need at least a value:"
|
||||
msgstr ""
|
||||
|
||||
#: webclient.rs:224
|
||||
#: webclient.rs:226 webclient.rs:247
|
||||
msgid "Retry after <num> seconds."
|
||||
msgstr ""
|
||||
|
||||
#: webclient.rs:228
|
||||
#: webclient.rs:230 webclient.rs:251
|
||||
msgid "Retry <count> times now."
|
||||
msgstr ""
|
||||
|
||||
#: webclient.rs:242
|
||||
#: webclient.rs:264 webclient.rs:282
|
||||
msgid "Error when request:"
|
||||
msgstr ""
|
||||
|
||||
#: webclient.rs:308
|
||||
#: webclient.rs:334 webclient.rs:426
|
||||
msgid "Downloading \"<loc>\"."
|
||||
msgstr ""
|
||||
|
||||
#: webclient.rs:319
|
||||
#: webclient.rs:366 webclient.rs:368 webclient.rs:437
|
||||
msgid "Error when downloading file:"
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: pixiv_downloader\n"
|
||||
"POT-Creation-Date: 2022-03-26 15:53+0800\n"
|
||||
"PO-Revision-Date: 2022-03-26 15:54+0800\n"
|
||||
"POT-Creation-Date: 2022-03-28 21:09+0800\n"
|
||||
"PO-Revision-Date: 2022-03-28 21:10+0800\n"
|
||||
"Last-Translator: lifegpc <[email protected]>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: zh_CN\n"
|
||||
@@ -116,15 +116,18 @@ msgstr "无效的Cookie:"
|
||||
msgid "Can not parse expired time:"
|
||||
msgstr "无法解析过期时间:"
|
||||
|
||||
#: cookies.rs:366 data/json.rs:50 settings.rs:418 webclient.rs:302
|
||||
#: cookies.rs:366 data/json.rs:51 settings.rs:418 webclient.rs:343
|
||||
#: webclient.rs:345 webclient.rs:420
|
||||
msgid "Failed to remove file:"
|
||||
msgstr "无法删除文件:"
|
||||
|
||||
#: cookies.rs:372 data/json.rs:61 settings.rs:425 webclient.rs:312
|
||||
#: cookies.rs:372 data/json.rs:62 settings.rs:425 webclient.rs:354
|
||||
#: webclient.rs:356 webclient.rs:430
|
||||
msgid "Failed to create file:"
|
||||
msgstr "无法创建文件:"
|
||||
|
||||
#: cookies.rs:379 data/json.rs:67 settings.rs:431 webclient.rs:329
|
||||
#: cookies.rs:379 data/json.rs:68 settings.rs:431 webclient.rs:381
|
||||
#: webclient.rs:383 webclient.rs:447
|
||||
msgid "Failed to write file:"
|
||||
msgstr "无法写入文件:"
|
||||
|
||||
@@ -132,84 +135,85 @@ msgstr "无法写入文件:"
|
||||
msgid "Failed to unescape string:"
|
||||
msgstr "无法反转义字符串:"
|
||||
|
||||
#: download.rs:25 pixiv_web.rs:58
|
||||
#: download.rs:30 pixiv_web.rs:58
|
||||
msgid "Failed to initialize pixiv web api client."
|
||||
msgstr "无法初始化 Pixiv 网页 API 客户端。"
|
||||
|
||||
#: download.rs:34
|
||||
#: download.rs:39
|
||||
msgid "Warning: Web api client not logined, some future may not work."
|
||||
msgstr "警告:Web API 客户端未登录,一些功能可能无法工作。"
|
||||
|
||||
#: download.rs:73
|
||||
msgid "Failed to get page count."
|
||||
msgstr "无法获取页数。"
|
||||
|
||||
#: download.rs:82
|
||||
msgid "Failed to get pages' data."
|
||||
msgstr "无法获取每页数据。"
|
||||
|
||||
#: download.rs:95
|
||||
msgid "Failed to save metadata to JSON file."
|
||||
msgstr "无法将元数据保存到 JSON 文件。"
|
||||
|
||||
#: download.rs:110
|
||||
msgid "Failed to get ugoira's data."
|
||||
msgstr "无法获取动图数据。"
|
||||
|
||||
#: download.rs:116
|
||||
msgid "Can not find source link for ugoira."
|
||||
msgstr "无法获取动图的链接。"
|
||||
|
||||
#: download.rs:122 download.rs:200 download.rs:274
|
||||
msgid "Failed to get file name from url:"
|
||||
msgstr "无法从 URL 获取文件名:"
|
||||
|
||||
#: download.rs:138 download.rs:144
|
||||
msgid "Failed to download ugoira:"
|
||||
msgstr "无法下载动图:"
|
||||
|
||||
#: download.rs:149
|
||||
msgid "Downloaded ugoira:"
|
||||
msgstr "已下载动图:"
|
||||
|
||||
#: download.rs:159
|
||||
msgid "Warning: Failed to generate video's metadata:"
|
||||
msgstr "警告:无法生成视频元数据:"
|
||||
|
||||
#: download.rs:169
|
||||
msgid "Failed to convert from ugoira to mp4 video file:"
|
||||
msgstr "无法将动图转换为 mp4 视频文件:"
|
||||
|
||||
#: download.rs:172
|
||||
msgid "Converted <src> -> <dest>"
|
||||
msgstr "已转换 <src> -> <dest>"
|
||||
|
||||
#: download.rs:175
|
||||
msgid "Failed to parse frames:"
|
||||
msgstr "无法解析帧数据:"
|
||||
|
||||
#: download.rs:182
|
||||
msgid "Warning: Unknown illust type:"
|
||||
msgstr "警告:未知的插图类型:"
|
||||
|
||||
#: download.rs:185
|
||||
msgid "Warning: Failed to get illust's type."
|
||||
msgstr "警告:无法获取插图类型。"
|
||||
|
||||
#: download.rs:194 download.rs:268
|
||||
#: download.rs:58 download.rs:286 download.rs:360
|
||||
msgid "Failed to get original picture's link."
|
||||
msgstr "无法获取原图链接。"
|
||||
|
||||
#: download.rs:215 download.rs:254 download.rs:294 download.rs:324
|
||||
#: download.rs:64 download.rs:197 download.rs:292 download.rs:366
|
||||
msgid "Failed to get file name from url:"
|
||||
msgstr "无法从 URL 获取文件名:"
|
||||
|
||||
#: download.rs:79 download.rs:115 download.rs:307 download.rs:346
|
||||
#: download.rs:386 download.rs:416
|
||||
msgid "Failed to add exif data to image:"
|
||||
msgstr "无法往图片增加 EXIF 数据:"
|
||||
|
||||
#: download.rs:234 download.rs:240 download.rs:304 download.rs:310
|
||||
#: pixiv_web.rs:153
|
||||
#: download.rs:100 download.rs:107 download.rs:326 download.rs:332
|
||||
#: download.rs:396 download.rs:402 pixiv_web.rs:153 pixiv_web.rs:169
|
||||
msgid "Failed to download image:"
|
||||
msgstr "无法下载图片:"
|
||||
|
||||
#: download.rs:245 download.rs:315
|
||||
#: download.rs:147
|
||||
msgid "Failed to get page count."
|
||||
msgstr "无法获取页数。"
|
||||
|
||||
#: download.rs:156
|
||||
msgid "Failed to get pages' data."
|
||||
msgstr "无法获取每页数据。"
|
||||
|
||||
#: download.rs:170
|
||||
msgid "Failed to save metadata to JSON file."
|
||||
msgstr "无法将元数据保存到 JSON 文件。"
|
||||
|
||||
#: download.rs:185
|
||||
msgid "Failed to get ugoira's data."
|
||||
msgstr "无法获取动图数据。"
|
||||
|
||||
#: download.rs:191
|
||||
msgid "Can not find source link for ugoira."
|
||||
msgstr "无法获取动图的链接。"
|
||||
|
||||
#: download.rs:213 download.rs:219
|
||||
msgid "Failed to download ugoira:"
|
||||
msgstr "无法下载动图:"
|
||||
|
||||
#: download.rs:224
|
||||
msgid "Downloaded ugoira:"
|
||||
msgstr "已下载动图:"
|
||||
|
||||
#: download.rs:234
|
||||
msgid "Warning: Failed to generate video's metadata:"
|
||||
msgstr "警告:无法生成视频元数据:"
|
||||
|
||||
#: download.rs:244
|
||||
msgid "Failed to convert from ugoira to mp4 video file:"
|
||||
msgstr "无法将动图转换为 mp4 视频文件:"
|
||||
|
||||
#: download.rs:247
|
||||
msgid "Converted <src> -> <dest>"
|
||||
msgstr "已转换 <src> -> <dest>"
|
||||
|
||||
#: download.rs:250
|
||||
msgid "Failed to parse frames:"
|
||||
msgstr "无法解析帧数据:"
|
||||
|
||||
#: download.rs:257
|
||||
msgid "Warning: Unknown illust type:"
|
||||
msgstr "警告:未知的插图类型:"
|
||||
|
||||
#: download.rs:260
|
||||
msgid "Warning: Failed to get illust's type."
|
||||
msgstr "警告:无法获取插图类型。"
|
||||
|
||||
#: download.rs:337 download.rs:407 webclient.rs:390
|
||||
msgid "Downloaded image:"
|
||||
msgstr "已下载图片:"
|
||||
|
||||
@@ -221,104 +225,108 @@ msgstr "无法解析从字符串解析时长。"
|
||||
msgid "Failed to parse value."
|
||||
msgstr "无法解析值。"
|
||||
|
||||
#: opts.rs:98
|
||||
#: opts.rs:101
|
||||
msgid "Warning: The specified config file not found."
|
||||
msgstr "警告:没有找到指定的设置文件。"
|
||||
|
||||
#: opts.rs:122
|
||||
#: opts.rs:125
|
||||
msgid "Usage:"
|
||||
msgstr "使用方法:"
|
||||
|
||||
#: opts.rs:124
|
||||
#: opts.rs:127
|
||||
msgid "Download an artwork"
|
||||
msgstr "下载一个作品"
|
||||
|
||||
#: opts.rs:126
|
||||
#: opts.rs:129
|
||||
msgid "Fix the config file"
|
||||
msgstr "修复设置文件"
|
||||
|
||||
#: opts.rs:128
|
||||
#: opts.rs:131
|
||||
msgid "Print all available settings"
|
||||
msgstr "打印所有可用的设置"
|
||||
|
||||
#: opts.rs:136
|
||||
#: opts.rs:139
|
||||
msgid "Print help message."
|
||||
msgstr "打印帮助信息。"
|
||||
|
||||
#: opts.rs:140
|
||||
#: opts.rs:143
|
||||
msgid "The location of config file."
|
||||
msgstr "设置文件的位置。"
|
||||
|
||||
#: opts.rs:146 settings_list.rs:13
|
||||
#: opts.rs:149 settings_list.rs:13
|
||||
msgid "The location of cookies file. Used for web API."
|
||||
msgstr "cookies 文件的位置。用于网页 API。"
|
||||
|
||||
#: opts.rs:152 settings_list.rs:14
|
||||
#: opts.rs:155 settings_list.rs:14
|
||||
msgid "The language of translated tags."
|
||||
msgstr "翻译后的标签语言。"
|
||||
|
||||
#: opts.rs:155
|
||||
#: opts.rs:158
|
||||
msgid "Verbose logging."
|
||||
msgstr "启用详细内容输出。"
|
||||
|
||||
#: opts.rs:156
|
||||
#: opts.rs:159
|
||||
msgid "Overwrite existing file."
|
||||
msgstr "覆盖已有文件。"
|
||||
|
||||
#: opts.rs:157
|
||||
#: opts.rs:160
|
||||
msgid "Skip overwrite existing file."
|
||||
msgstr "跳过覆盖已有文件。"
|
||||
|
||||
#: opts.rs:161 settings_list.rs:15
|
||||
#: opts.rs:164 settings_list.rs:15
|
||||
msgid "Max retry count if request failed."
|
||||
msgstr "请求失败时最大重试次数。"
|
||||
|
||||
#: opts.rs:167 settings_list.rs:16
|
||||
#: opts.rs:170 settings_list.rs:16
|
||||
msgid "The interval (in seconds) between two retries."
|
||||
msgstr "两次尝试的间隔时间(单位:秒)。"
|
||||
|
||||
#: opts.rs:170 settings_list.rs:17
|
||||
#: opts.rs:173 settings_list.rs:17
|
||||
msgid "Use data from webpage first."
|
||||
msgstr "优先使用来自网页的数据。"
|
||||
|
||||
#: opts.rs:175 settings_list.rs:20
|
||||
#: opts.rs:178 settings_list.rs:20
|
||||
msgid ""
|
||||
"Add/Update exif information to image files even when overwrite are disabled."
|
||||
msgstr "添加或更新图片的 EXIF 信息,即使不覆盖图片文件。"
|
||||
|
||||
#: opts.rs:180 settings_list.rs:22
|
||||
#: opts.rs:183 settings_list.rs:22
|
||||
msgid "Whether to enable progress bar."
|
||||
msgstr "是否启用进度条。"
|
||||
|
||||
#: opts.rs:202
|
||||
#: opts.rs:189 settings_list.rs:23
|
||||
msgid "Download multiple images at the same time."
|
||||
msgstr "同时下载多张图片。"
|
||||
|
||||
#: opts.rs:210
|
||||
msgid "Unknown command."
|
||||
msgstr "未知指令。"
|
||||
|
||||
#: opts.rs:212
|
||||
#: opts.rs:220
|
||||
msgid "Failed to parse ID:"
|
||||
msgstr "无法解析 ID:"
|
||||
|
||||
#: opts.rs:218
|
||||
#: opts.rs:226
|
||||
msgid "No URL or ID specified."
|
||||
msgstr "没有指定网址或 ID。"
|
||||
|
||||
#: opts.rs:226
|
||||
#: opts.rs:234
|
||||
msgid "No detailed command specified."
|
||||
msgstr "没有指定更详细的指令。"
|
||||
|
||||
#: opts.rs:239
|
||||
#: opts.rs:247
|
||||
msgid "Unknown config subcommand."
|
||||
msgstr "未知的 config 子指令。"
|
||||
|
||||
#: opts.rs:275
|
||||
#: opts.rs:283
|
||||
msgid "Retry count must be an non-negative integer:"
|
||||
msgstr "重试次数不能是负数:"
|
||||
|
||||
#: opts.rs:284
|
||||
#: opts.rs:292
|
||||
msgid "Failed to parse retry interval:"
|
||||
msgstr "无法解析间隔时间:"
|
||||
|
||||
#: opts.rs:298
|
||||
#: opts.rs:306
|
||||
msgid "Failed to parse <opt>:"
|
||||
msgstr "无法解析 <opt> :"
|
||||
|
||||
@@ -358,27 +366,27 @@ msgstr "网络错误:"
|
||||
msgid "Failed to detect error."
|
||||
msgstr "检测 error 字段失败。"
|
||||
|
||||
#: pixiv_web.rs:168 pixiv_web.rs:198
|
||||
#: pixiv_web.rs:184 pixiv_web.rs:214
|
||||
msgid "Artwork's data:"
|
||||
msgstr "作品数据:"
|
||||
|
||||
#: pixiv_web.rs:183 pixiv_web.rs:188
|
||||
#: pixiv_web.rs:199 pixiv_web.rs:204
|
||||
msgid "Failed to get artwork page:"
|
||||
msgstr "无法获取作品页:"
|
||||
|
||||
#: pixiv_web.rs:194
|
||||
#: pixiv_web.rs:210
|
||||
msgid "Failed to parse artwork page."
|
||||
msgstr "无法解析作品页。"
|
||||
|
||||
#: pixiv_web.rs:212
|
||||
#: pixiv_web.rs:228
|
||||
msgid "Artwork's page data:"
|
||||
msgstr "作品页面数据:"
|
||||
|
||||
#: pixiv_web.rs:226
|
||||
#: pixiv_web.rs:242
|
||||
msgid "Ugoira's data:"
|
||||
msgstr "动图数据:"
|
||||
|
||||
#: pixiv_web.rs:250
|
||||
#: pixiv_web.rs:266
|
||||
msgid "Warning: Failed to save cookies file:"
|
||||
msgstr "警告:无法保存 cookies 文件:"
|
||||
|
||||
@@ -506,7 +514,7 @@ msgstr "无法转换路径。"
|
||||
msgid "Do you want to delete file \"<file>\"?"
|
||||
msgstr "你想要删除文件 <file> 吗?"
|
||||
|
||||
#: utils.rs:51 webclient.rs:154
|
||||
#: utils.rs:51 webclient.rs:156
|
||||
msgid "Can not parse URL:"
|
||||
msgstr "无法解析 URL:"
|
||||
|
||||
@@ -514,43 +522,43 @@ msgstr "无法解析 URL:"
|
||||
msgid "Failed to get file name from path:"
|
||||
msgstr "无法从路径获取文件名:"
|
||||
|
||||
#: webclient.rs:114
|
||||
#: webclient.rs:116
|
||||
msgid "Failed to parse Set-Cookie header."
|
||||
msgstr "无法解析 Set-Cookie 头部。"
|
||||
|
||||
#: webclient.rs:119
|
||||
#: webclient.rs:121
|
||||
msgid "Failed to convert to string:"
|
||||
msgstr "无法转换为字符串:"
|
||||
|
||||
#: webclient.rs:161
|
||||
#: webclient.rs:163
|
||||
msgid "Parameters should be object or array:"
|
||||
msgstr "参数应该是对象或数组:"
|
||||
|
||||
#: webclient.rs:181
|
||||
#: webclient.rs:183
|
||||
msgid "Parameters should be array:"
|
||||
msgstr "参数应该是数组:"
|
||||
|
||||
#: webclient.rs:185
|
||||
#: webclient.rs:187
|
||||
msgid "Parameters need at least a value:"
|
||||
msgstr "参数需要至少含有一个值:"
|
||||
|
||||
#: webclient.rs:224
|
||||
#: webclient.rs:226 webclient.rs:247
|
||||
msgid "Retry after <num> seconds."
|
||||
msgstr "<num> 秒后重试。"
|
||||
|
||||
#: webclient.rs:228
|
||||
#: webclient.rs:230 webclient.rs:251
|
||||
msgid "Retry <count> times now."
|
||||
msgstr "正在进行第 <count> 次重试。"
|
||||
|
||||
#: webclient.rs:242
|
||||
#: webclient.rs:264 webclient.rs:282
|
||||
msgid "Error when request:"
|
||||
msgstr "请求时发生错误:"
|
||||
|
||||
#: webclient.rs:308
|
||||
#: webclient.rs:334 webclient.rs:426
|
||||
msgid "Downloading \"<loc>\"."
|
||||
msgstr "正在下载 \"<loc>\"。"
|
||||
|
||||
#: webclient.rs:319
|
||||
#: webclient.rs:366 webclient.rs:368 webclient.rs:437
|
||||
msgid "Error when downloading file:"
|
||||
msgstr "下载文件时发生错误:"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::convert::TryInto;
|
||||
use xml::unescape;
|
||||
|
||||
/// Pixiv's basic data
|
||||
pub struct PixivData<'a> {
|
||||
pub struct PixivData {
|
||||
/// ID
|
||||
pub id: PixivID,
|
||||
/// The title
|
||||
@@ -16,11 +16,11 @@ pub struct PixivData<'a> {
|
||||
/// The author
|
||||
pub author: Option<String>,
|
||||
pub description: Option<String>,
|
||||
helper: OptHelper<'a>,
|
||||
helper: OptHelper,
|
||||
}
|
||||
|
||||
impl<'a> PixivData<'a> {
|
||||
pub fn new<T: ToPixivID>(id: T, helper: OptHelper<'a>) -> Option<Self> {
|
||||
impl PixivData {
|
||||
pub fn new<T: ToPixivID>(id: T, helper: OptHelper) -> Option<Self> {
|
||||
let i = id.to_pixiv_id();
|
||||
if i.is_none() {
|
||||
return None;
|
||||
|
||||
@@ -12,6 +12,7 @@ use std::fs::File;
|
||||
use std::fs::remove_file;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Store metadata informations in JSON file
|
||||
pub struct JSONDataFile {
|
||||
@@ -71,13 +72,19 @@ impl JSONDataFile {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<PixivData<'a>> for JSONDataFile {
|
||||
impl From<PixivData> for JSONDataFile {
|
||||
fn from(p: PixivData) -> Self {
|
||||
JSONDataFile::from(&p)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a PixivData<'a>> for JSONDataFile {
|
||||
impl From<Arc<PixivData>> for JSONDataFile {
|
||||
fn from(p: Arc<PixivData>) -> Self {
|
||||
JSONDataFile::from(p.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PixivData> for JSONDataFile {
|
||||
fn from(p: &PixivData) -> Self {
|
||||
let mut f = Self {
|
||||
id: p.id.clone(),
|
||||
|
||||
146
src/download.rs
146
src/download.rs
@@ -7,6 +7,7 @@ use crate::data::json::JSONDataFile;
|
||||
#[cfg(feature = "ugoira")]
|
||||
use crate::data::video::get_video_metadata;
|
||||
use crate::gettext;
|
||||
use crate::opthelper::OptHelper;
|
||||
use crate::pixiv_link::PixivID;
|
||||
use crate::pixiv_web::PixivWebClient;
|
||||
#[cfg(feature = "ugoira")]
|
||||
@@ -15,20 +16,24 @@ use crate::utils::ask_need_overwrite;
|
||||
use crate::utils::get_file_name_from_url;
|
||||
use crate::webclient::WebClient;
|
||||
use crate::Main;
|
||||
use indicatif::MultiProgress;
|
||||
use json::JsonValue;
|
||||
use spin_on::spin_on;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use futures_util::lock::Mutex;
|
||||
|
||||
impl Main {
|
||||
pub fn download(&mut self) -> i32 {
|
||||
let mut pw = PixivWebClient::new(&self);
|
||||
if !pw.init() {
|
||||
let pw = Arc::new(Mutex::new(PixivWebClient::new(&self)));
|
||||
if !pw.try_lock().unwrap().init() {
|
||||
println!("{}", gettext("Failed to initialize pixiv web api client."));
|
||||
return 1;
|
||||
}
|
||||
if !pw.check_login() {
|
||||
if !pw.try_lock().unwrap().check_login() {
|
||||
return 1;
|
||||
}
|
||||
if !pw.logined() {
|
||||
if !pw.try_lock().unwrap().logined() {
|
||||
println!(
|
||||
"{}",
|
||||
gettext("Warning: Web api client not logined, some future may not work.")
|
||||
@@ -37,7 +42,7 @@ impl Main {
|
||||
for id in self.cmd.as_ref().unwrap().ids.iter() {
|
||||
match id {
|
||||
PixivID::Artwork(id) => {
|
||||
let r = self.download_artwork(&mut pw, id.clone());
|
||||
let r = self.download_artwork(Arc::clone(&pw), id.clone());
|
||||
if r != 0 {
|
||||
return r;
|
||||
}
|
||||
@@ -47,18 +52,87 @@ impl Main {
|
||||
0
|
||||
}
|
||||
|
||||
pub fn download_artwork(&self, pw: &mut PixivWebClient, id: u64) -> i32 {
|
||||
pub async fn download_artwork_page(pw: Arc<Mutex<PixivWebClient>>, page: JsonValue, np: u16, progress_bars: Arc<MultiProgress>, datas: Arc<PixivData>, base: Arc<PathBuf>, helper: Arc<OptHelper>) -> i32 {
|
||||
let link = &page["urls"]["original"];
|
||||
if !link.is_string() {
|
||||
println!("{}", gettext("Failed to get original picture's link."));
|
||||
return 1;
|
||||
}
|
||||
let link = link.as_str().unwrap();
|
||||
let file_name = get_file_name_from_url(link);
|
||||
if file_name.is_none() {
|
||||
println!("{} {}", gettext("Failed to get file name from url:"), link);
|
||||
return 1;
|
||||
}
|
||||
let file_name = file_name.unwrap();
|
||||
let file_name = base.join(file_name);
|
||||
if file_name.exists() {
|
||||
match helper.overwrite() {
|
||||
Some(overwrite) => {
|
||||
if !overwrite {
|
||||
#[cfg(feature = "exif")]
|
||||
{
|
||||
if helper.update_exif() {
|
||||
if add_exifdata_to_image(&file_name, &datas, np).is_err() {
|
||||
println!(
|
||||
"{} {}",
|
||||
gettext("Failed to add exif data to image:"),
|
||||
file_name.to_str().unwrap_or("(null)")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
if !ask_need_overwrite(file_name.to_str().unwrap()) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let r;
|
||||
{
|
||||
let mut pw = pw.lock().await;
|
||||
r = pw.adownload_image(link).await;
|
||||
if r.is_none() {
|
||||
println!("{} {}", gettext("Failed to download image:"), link);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
let r = r.unwrap();
|
||||
let re = WebClient::adownload_stream(&file_name, r, &helper, Some(progress_bars)).await;
|
||||
if re.is_err() {
|
||||
println!("{} {}", gettext("Failed to download image:"), link);
|
||||
return 1;
|
||||
}
|
||||
#[cfg(feature = "exif")]
|
||||
{
|
||||
if add_exifdata_to_image(&file_name, &datas, np).is_err() {
|
||||
println!(
|
||||
"{} {}",
|
||||
gettext("Failed to add exif data to image:"),
|
||||
file_name.to_str().unwrap_or("(null)")
|
||||
);
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
pub fn download_artwork(&self, pw: Arc<Mutex<PixivWebClient>>, id: u64) -> i32 {
|
||||
let mut re = None;
|
||||
let pages;
|
||||
let mut ajax_ver = true;
|
||||
if pw.helper.use_webpage() {
|
||||
re = pw.get_artwork(id);
|
||||
let helper = Arc::new(pw.try_lock().unwrap().helper.clone());
|
||||
if helper.use_webpage() {
|
||||
re = pw.try_lock().unwrap().get_artwork(id);
|
||||
if re.is_some() {
|
||||
ajax_ver = false;
|
||||
}
|
||||
}
|
||||
if re.is_none() {
|
||||
re = pw.get_artwork_ajax(id);
|
||||
re = pw.try_lock().unwrap().get_artwork_ajax(id);
|
||||
}
|
||||
if re.is_none() {
|
||||
return 1;
|
||||
@@ -76,21 +150,22 @@ impl Main {
|
||||
let pages = pages.unwrap();
|
||||
let mut pages_data: Option<JsonValue> = None;
|
||||
if pages > 1 {
|
||||
pages_data = pw.get_illust_pages(id);
|
||||
pages_data = pw.try_lock().unwrap().get_illust_pages(id);
|
||||
}
|
||||
if pages > 1 && pages_data.is_none() {
|
||||
println!("{}", gettext("Failed to get pages' data."));
|
||||
return 1;
|
||||
}
|
||||
let base = PathBuf::from(".");
|
||||
let base = Arc::new(PathBuf::from("."));
|
||||
let json_file = base.join(format!("{}.json", id));
|
||||
let mut datas = PixivData::new(id, pw.helper.clone()).unwrap();
|
||||
let mut datas = PixivData::new(id, (*helper).clone()).unwrap();
|
||||
if ajax_ver {
|
||||
datas.from_web_page_ajax_data(&re, true);
|
||||
} else {
|
||||
datas.from_web_page_data(&re, true);
|
||||
}
|
||||
let json_data = JSONDataFile::from(&datas);
|
||||
let datas = Arc::new(datas);
|
||||
let json_data = JSONDataFile::from(Arc::clone(&datas));
|
||||
if !json_data.save(&json_file) {
|
||||
println!("{}", gettext("Failed to save metadata to JSON file."));
|
||||
return 1;
|
||||
@@ -105,7 +180,7 @@ impl Main {
|
||||
match illust_type {
|
||||
0 => { }
|
||||
2 => {
|
||||
let ugoira_data = pw.get_ugoira(id);
|
||||
let ugoira_data = pw.try_lock().unwrap().get_ugoira(id);
|
||||
if ugoira_data.is_none() {
|
||||
println!("{}", gettext("Failed to get ugoira's data."));
|
||||
return 1;
|
||||
@@ -125,7 +200,7 @@ impl Main {
|
||||
let file_name = file_name.unwrap();
|
||||
let file_name = base.join(file_name);
|
||||
let dw = if file_name.exists() {
|
||||
match pw.helper.overwrite() {
|
||||
match helper.overwrite() {
|
||||
Some(overwrite) => { overwrite }
|
||||
None => { ask_need_overwrite(file_name.to_str().unwrap()) }
|
||||
}
|
||||
@@ -133,13 +208,13 @@ impl Main {
|
||||
true
|
||||
};
|
||||
if dw {
|
||||
let r = pw.download_image(src);
|
||||
let r = pw.try_lock().unwrap().download_image(src);
|
||||
if r.is_none() {
|
||||
println!("{} {}", gettext("Failed to download ugoira:"), src);
|
||||
return 1;
|
||||
}
|
||||
let r = r.unwrap();
|
||||
let re = WebClient::download_stream(&file_name, r, &pw.helper);
|
||||
let re = WebClient::download_stream(&file_name, r, &helper);
|
||||
if re.is_err() {
|
||||
println!("{} {}", gettext("Failed to download ugoira:"), src);
|
||||
return 1;
|
||||
@@ -153,7 +228,7 @@ impl Main {
|
||||
}
|
||||
#[cfg(feature = "ugoira")]
|
||||
{
|
||||
let metadata = match get_video_metadata(&datas) {
|
||||
let metadata = match get_video_metadata(Arc::clone(&datas).as_ref()) {
|
||||
Ok(m) => { m }
|
||||
Err(e) => {
|
||||
println!("{} {}", gettext("Warning: Failed to generate video's metadata:"), e);
|
||||
@@ -184,7 +259,24 @@ impl Main {
|
||||
} else {
|
||||
println!("{}", gettext("Warning: Failed to get illust's type."));
|
||||
}
|
||||
if pages_data.is_some() {
|
||||
if pages_data.is_some() && helper.download_multiple_images() {
|
||||
let mut np = 0u16;
|
||||
let pages_data = pages_data.as_ref().unwrap();
|
||||
let progress_bars = Arc::new(MultiProgress::new());
|
||||
let mut tasks = Vec::new();
|
||||
for page in pages_data.members() {
|
||||
let f = tokio::spawn(Self::download_artwork_page(Arc::clone(&pw), page.clone(), np, Arc::clone(&progress_bars), Arc::clone(&datas), Arc::clone(&base), Arc::clone(&helper)));
|
||||
tasks.push(f);
|
||||
np += 1;
|
||||
}
|
||||
let mut re = 0;
|
||||
for task in tasks {
|
||||
let r = spin_on(task);
|
||||
re |= r.unwrap_or(1);
|
||||
}
|
||||
return re;
|
||||
}
|
||||
else if pages_data.is_some() {
|
||||
#[cfg(feature = "exif")]
|
||||
let mut np = 0u16;
|
||||
let pages_data = pages_data.as_ref().unwrap();
|
||||
@@ -203,12 +295,12 @@ impl Main {
|
||||
let file_name = file_name.unwrap();
|
||||
let file_name = base.join(file_name);
|
||||
if file_name.exists() {
|
||||
match pw.helper.overwrite() {
|
||||
match helper.overwrite() {
|
||||
Some(overwrite) => {
|
||||
if !overwrite {
|
||||
#[cfg(feature = "exif")]
|
||||
{
|
||||
if pw.helper.update_exif() {
|
||||
if helper.update_exif() {
|
||||
if add_exifdata_to_image(&file_name, &datas, np).is_err() {
|
||||
println!(
|
||||
"{} {}",
|
||||
@@ -229,13 +321,13 @@ impl Main {
|
||||
}
|
||||
}
|
||||
}
|
||||
let r = pw.download_image(link);
|
||||
let r = pw.try_lock().unwrap().download_image(link);
|
||||
if r.is_none() {
|
||||
println!("{} {}", gettext("Failed to download image:"), link);
|
||||
return 1;
|
||||
}
|
||||
let r = r.unwrap();
|
||||
let re = WebClient::download_stream(&file_name, r, &pw.helper);
|
||||
let re = WebClient::download_stream(&file_name, r, &helper);
|
||||
if re.is_err() {
|
||||
println!("{} {}", gettext("Failed to download image:"), link);
|
||||
return 1;
|
||||
@@ -277,7 +369,7 @@ impl Main {
|
||||
let file_name = file_name.unwrap();
|
||||
let file_name = base.join(file_name);
|
||||
if file_name.exists() {
|
||||
let overwrite = match pw.helper.overwrite() {
|
||||
let overwrite = match helper.overwrite() {
|
||||
Some(overwrite) => {
|
||||
overwrite
|
||||
}
|
||||
@@ -287,7 +379,7 @@ impl Main {
|
||||
};
|
||||
if !overwrite {
|
||||
#[cfg(feature = "exif")]
|
||||
if pw.helper.update_exif() {
|
||||
if helper.update_exif() {
|
||||
if add_exifdata_to_image(&file_name, &datas, 0).is_err() {
|
||||
println!(
|
||||
"{} {}",
|
||||
@@ -299,13 +391,13 @@ impl Main {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
let r = pw.download_image(link);
|
||||
let r = pw.try_lock().unwrap().download_image(link);
|
||||
if r.is_none() {
|
||||
println!("{} {}", gettext("Failed to download image:"), link);
|
||||
return 1;
|
||||
}
|
||||
let r = r.unwrap();
|
||||
let re = WebClient::download_stream(&file_name, r, &pw.helper);
|
||||
let re = WebClient::download_stream(&file_name, r, &helper);
|
||||
if re.is_err() {
|
||||
println!("{} {}", gettext("Failed to download image:"), link);
|
||||
return 1;
|
||||
|
||||
@@ -11,17 +11,17 @@ use std::time::Duration;
|
||||
|
||||
/// An sturct to access all available settings/command line switches
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OptHelper<'a> {
|
||||
pub struct OptHelper {
|
||||
/// Command Line Options
|
||||
opt: &'a CommandOpts,
|
||||
opt: CommandOpts,
|
||||
/// Settings
|
||||
settings: &'a SettingStore,
|
||||
settings: SettingStore,
|
||||
default_retry_interval: NonTailList<Duration>,
|
||||
_author_name_filters: Option<Vec<AuthorNameFilter>>,
|
||||
_use_progress_bar: Option<UseProgressBar>,
|
||||
}
|
||||
|
||||
impl<'a> OptHelper<'a> {
|
||||
impl OptHelper {
|
||||
pub fn author_name_filters(&self) -> Option<&Vec<AuthorNameFilter>> {
|
||||
if self.settings.have("author-name-filters") {
|
||||
return self._author_name_filters.as_ref();
|
||||
@@ -51,7 +51,7 @@ impl<'a> OptHelper<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(opt: &'a CommandOpts, settings: &'a SettingStore) -> Self {
|
||||
pub fn new(opt: CommandOpts, settings: SettingStore) -> Self {
|
||||
let mut l = NonTailList::default();
|
||||
l += Duration::new(3, 0);
|
||||
let _author_name_filters = if settings.have("author-name-filters") {
|
||||
@@ -146,4 +146,15 @@ impl<'a> OptHelper<'a> {
|
||||
}
|
||||
String::from("[{elapsed_precise}] [{wide_bar:.green/yellow}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta}) {msg:40}")
|
||||
}
|
||||
|
||||
/// Return whether to download multiple images at the same time.
|
||||
pub fn download_multiple_images(&self) -> bool {
|
||||
if self.opt.download_multiple_images {
|
||||
return true;
|
||||
}
|
||||
if self.settings.have_bool("download-multiple-images") {
|
||||
return self.settings.get_bool("download-multiple-images").unwrap();
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
11
src/opts.rs
11
src/opts.rs
@@ -36,7 +36,7 @@ impl PartialEq<ConfigCommand> for &ConfigCommand {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
/// Command Line Options
|
||||
pub struct CommandOpts {
|
||||
/// Command
|
||||
@@ -66,6 +66,8 @@ pub struct CommandOpts {
|
||||
pub update_exif: bool,
|
||||
/// Whether to enable progress bar
|
||||
pub use_progress_bar: Option<UseOrNot>,
|
||||
/// Whether to download multiple images at the same time
|
||||
pub download_multiple_images: bool,
|
||||
}
|
||||
|
||||
impl CommandOpts {
|
||||
@@ -85,6 +87,7 @@ impl CommandOpts {
|
||||
#[cfg(feature = "exif")]
|
||||
update_exif: false,
|
||||
use_progress_bar: None,
|
||||
download_multiple_images: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,6 +183,11 @@ pub fn parse_cmd() -> Option<CommandOpts> {
|
||||
gettext("Whether to enable progress bar."),
|
||||
"yes/no/auto",
|
||||
);
|
||||
opts.optflag(
|
||||
"",
|
||||
"download-multiple-images",
|
||||
gettext("Download multiple images at the same time."),
|
||||
);
|
||||
let result = match opts.parse(&argv[1..]) {
|
||||
Ok(m) => m,
|
||||
Err(err) => {
|
||||
@@ -300,5 +308,6 @@ pub fn parse_cmd() -> Option<CommandOpts> {
|
||||
}
|
||||
re.as_mut().unwrap().use_progress_bar = Some(r.unwrap());
|
||||
}
|
||||
re.as_mut().unwrap().download_multiple_images = result.opt_present("download-multiple-images");
|
||||
re
|
||||
}
|
||||
|
||||
@@ -9,19 +9,19 @@ use reqwest::Response;
|
||||
use spin_on::spin_on;
|
||||
|
||||
/// A client which use Pixiv's web API
|
||||
pub struct PixivWebClient<'a> {
|
||||
pub struct PixivWebClient {
|
||||
client: WebClient,
|
||||
pub helper: OptHelper<'a>,
|
||||
pub helper: OptHelper,
|
||||
/// true if in is initialized
|
||||
inited: bool,
|
||||
data: Option<JsonValue>,
|
||||
}
|
||||
|
||||
impl<'a> PixivWebClient<'a> {
|
||||
pub fn new(m: &'a Main) -> Self {
|
||||
impl PixivWebClient {
|
||||
pub fn new(m: &Main) -> Self {
|
||||
Self {
|
||||
client: WebClient::new(),
|
||||
helper: OptHelper::new(m.cmd.as_ref().unwrap(), m.settings.as_ref().unwrap()),
|
||||
helper: OptHelper::new(m.cmd.as_ref().unwrap().clone(), m.settings.as_ref().unwrap().clone()),
|
||||
inited: false,
|
||||
data: None,
|
||||
}
|
||||
@@ -156,6 +156,22 @@ impl<'a> PixivWebClient<'a> {
|
||||
Some(r)
|
||||
}
|
||||
|
||||
pub async fn adownload_image<U: IntoUrl + Clone>(&mut self, url: U) -> Option<Response> {
|
||||
self.auto_init();
|
||||
let r = self.client.aget(url, json::object!{"referer": "https://www.pixiv.net/"}).await;
|
||||
if r.is_none() {
|
||||
return None;
|
||||
}
|
||||
let r = r.unwrap();
|
||||
let status = r.status();
|
||||
let code = status.as_u16();
|
||||
if code >= 400 {
|
||||
println!("{} {}", gettext("Failed to download image:"), status);
|
||||
return None;
|
||||
}
|
||||
Some(r)
|
||||
}
|
||||
|
||||
pub fn get_artwork_ajax(&mut self, id: u64) -> Option<JsonValue> {
|
||||
self.auto_init();
|
||||
let r = self.client.get(format!("https://www.pixiv.net/ajax/illust/{}", id), None);
|
||||
@@ -241,7 +257,7 @@ impl<'a> PixivWebClient<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl <'a> Drop for PixivWebClient<'a> {
|
||||
impl Drop for PixivWebClient {
|
||||
fn drop(&mut self) {
|
||||
if self.inited {
|
||||
let c = self.helper.cookies();
|
||||
|
||||
@@ -20,6 +20,7 @@ pub fn get_settings_list() -> Vec<SettingDes> {
|
||||
SettingDes::new("update-exif", gettext("Add/Update exif information to image files even when overwrite are disabled."), JsonValueType::Boolean, None).unwrap(),
|
||||
SettingDes::new("progress-bar-template", gettext("Progress bar's template. See <here> for more informations.").replace("<here>", "https://docs.rs/indicatif/latest/indicatif/#templates").as_str(), JsonValueType::Str, Some(check_nonempty_str)).unwrap(),
|
||||
SettingDes::new("use-progress-bar", gettext("Whether to enable progress bar."), JsonValueType::Multiple, Some(check_user_or_not)).unwrap(),
|
||||
SettingDes::new("download-multiple-images", gettext("Download multiple images at the same time."), JsonValueType::Boolean, None).unwrap(),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
124
src/webclient.rs
124
src/webclient.rs
@@ -6,6 +6,7 @@ use crate::gettext;
|
||||
use crate::list::NonTailList;
|
||||
use crate::opthelper::OptHelper;
|
||||
use futures_util::StreamExt;
|
||||
use indicatif::MultiProgress;
|
||||
use indicatif::ProgressBar;
|
||||
use indicatif::ProgressStyle;
|
||||
use json::JsonValue;
|
||||
@@ -18,6 +19,7 @@ use std::fs::File;
|
||||
use std::fs::remove_file;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
pub trait ToHeaders {
|
||||
@@ -231,9 +233,29 @@ impl WebClient {
|
||||
None
|
||||
}
|
||||
|
||||
pub async fn aget<U: IntoUrl + Clone, H: ToHeaders + Clone>(&mut self, url: U, headers: H) -> Option<Response> {
|
||||
let mut count = 0u64;
|
||||
while count <= self.retry {
|
||||
let r = self._aget2(url.clone(), headers.clone()).await;
|
||||
if r.is_some() {
|
||||
return r;
|
||||
}
|
||||
count += 1;
|
||||
if count <= self.retry {
|
||||
let t = self.retry_interval.as_ref().unwrap()[(count - 1).try_into().unwrap()];
|
||||
if !t.is_zero() {
|
||||
println!("{}", gettext("Retry after <num> seconds.").replace("<num>", format!("{}", t.as_secs_f64()).as_str()).as_str());
|
||||
tokio::time::sleep(t).await;
|
||||
}
|
||||
}
|
||||
println!("{}", gettext("Retry <count> times now.").replace("<count>", format!("{}", count).as_str()).as_str());
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Send GET requests
|
||||
pub fn _get<U: IntoUrl, H: ToHeaders>(&mut self, url: U, headers: H) -> Option<Response> {
|
||||
let r = self.aget(url, headers);
|
||||
let r = self._aget(url, headers);
|
||||
let r = r.send();
|
||||
let r = spin_on(r);
|
||||
match r {
|
||||
@@ -251,7 +273,25 @@ impl WebClient {
|
||||
Some(r)
|
||||
}
|
||||
|
||||
pub fn aget<U: IntoUrl, H: ToHeaders>(&mut self, url: U, headers: H) -> RequestBuilder {
|
||||
pub async fn _aget2<U: IntoUrl, H: ToHeaders>(&mut self, url: U, headers: H) -> Option<Response> {
|
||||
let r = self._aget(url, headers);
|
||||
let r = r.send().await;
|
||||
match r {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
println!("{} {}", gettext("Error when request:"), e);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
let r = r.unwrap();
|
||||
self.handle_set_cookie(&r);
|
||||
if self.verbose {
|
||||
println!("{}", r.status());
|
||||
}
|
||||
Some(r)
|
||||
}
|
||||
|
||||
pub fn _aget<U: IntoUrl, H: ToHeaders>(&mut self, url: U, headers: H) -> RequestBuilder {
|
||||
let s = url.as_str();
|
||||
if self.verbose {
|
||||
println!("GET {}", s);
|
||||
@@ -274,6 +314,84 @@ impl WebClient {
|
||||
r
|
||||
}
|
||||
|
||||
pub async fn adownload_stream<S: AsRef<OsStr> + ?Sized>(file_name: &S, r: Response, opt: &OptHelper, progress_bars: Option<Arc<MultiProgress>>) -> Result<(), ()> {
|
||||
let content_length = r.content_length();
|
||||
let use_progress_bar = match &content_length {
|
||||
Some(_) => { opt.use_progress_bar() }
|
||||
None => { false }
|
||||
};
|
||||
let mut bar = if use_progress_bar {
|
||||
Some(ProgressBar::new(content_length.unwrap()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let p = Path::new(file_name);
|
||||
if bar.is_some() {
|
||||
bar.as_mut().unwrap().set_style(ProgressStyle::default_bar()
|
||||
.template(opt.progress_bar_template().as_ref()).unwrap()
|
||||
.progress_chars("#>-"));
|
||||
let tmp = p.file_name().unwrap_or(p.as_os_str());
|
||||
bar.as_mut().unwrap().set_message(gettext("Downloading \"<loc>\".").replace("<loc>", tmp.to_str().unwrap_or("<NULL>")));
|
||||
if progress_bars.is_some() {
|
||||
bar = Some(progress_bars.unwrap().add(bar.unwrap()));
|
||||
}
|
||||
}
|
||||
if p.exists() {
|
||||
let re = remove_file(p);
|
||||
if re.is_err() {
|
||||
if bar.is_none() {
|
||||
println!("{} {}", gettext("Failed to remove file:"), re.unwrap_err());
|
||||
} else {
|
||||
bar.as_ref().unwrap().set_message(format!("{} {}", gettext("Failed to remove file:"), re.unwrap_err()));
|
||||
bar.as_ref().unwrap().abandon();
|
||||
}
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
let f = File::create(p);
|
||||
if f.is_err() {
|
||||
if bar.is_none() {
|
||||
println!("{} {}", gettext("Failed to create file:"), f.unwrap_err());
|
||||
} else {
|
||||
bar.as_ref().unwrap().set_message(format!("{} {}", gettext("Failed to create file:"), f.unwrap_err()));
|
||||
bar.as_ref().unwrap().abandon();
|
||||
}
|
||||
return Err(());
|
||||
}
|
||||
let mut f = f.unwrap();
|
||||
let mut stream = r.bytes_stream();
|
||||
while let Some(data) = stream.next().await {
|
||||
if data.is_err() {
|
||||
if bar.is_none() {
|
||||
println!("{} {}", gettext("Error when downloading file:"), data.unwrap_err());
|
||||
} else {
|
||||
bar.as_ref().unwrap().set_message(format!("{} {}", gettext("Error when downloading file:"), data.unwrap_err()));
|
||||
bar.as_ref().unwrap().abandon();
|
||||
}
|
||||
return Err(());
|
||||
}
|
||||
let data = data.unwrap();
|
||||
if bar.is_some() {
|
||||
bar.as_ref().unwrap().inc(data.len() as u64);
|
||||
bar.as_ref().unwrap().tick();
|
||||
}
|
||||
let r = f.write(&data);
|
||||
if r.is_err() {
|
||||
if bar.is_none() {
|
||||
println!("{} {}", gettext("Failed to write file:"), r.unwrap_err());
|
||||
} else {
|
||||
bar.as_ref().unwrap().set_message(format!("{} {}", gettext("Failed to write file:"), r.unwrap_err()));
|
||||
bar.as_ref().unwrap().abandon();
|
||||
}
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
if bar.is_some() {
|
||||
bar.as_mut().unwrap().finish_with_message(format!("{} {}", gettext("Downloaded image:"), p.to_str().unwrap_or("(null)")));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Download a stream
|
||||
/// * `file_name` - File name
|
||||
/// * `r` - Response
|
||||
@@ -291,7 +409,7 @@ impl WebClient {
|
||||
};
|
||||
if bar.is_some() {
|
||||
bar.as_mut().unwrap().set_style(ProgressStyle::default_bar()
|
||||
.template(opt.progress_bar_template().as_ref())
|
||||
.template(opt.progress_bar_template().as_ref()).unwrap()
|
||||
.progress_chars("#>-"));
|
||||
}
|
||||
let mut downloaded = 0usize;
|
||||
|
||||
Reference in New Issue
Block a user