From b070e3fdebb25a8c5ee3a63e1782b1b09d745c18 Mon Sep 17 00:00:00 2001 From: misaka10843 Date: Sun, 28 Jan 2024 23:20:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8B=86=E5=88=86=E6=96=87=E4=BB=B6=EF=BC=8C?= =?UTF-8?q?=E9=98=B2=E6=AD=A2=E6=96=87=E4=BB=B6=E8=BF=87=E9=95=BF=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E6=AD=A3=E5=B8=B8=E7=BB=B4=E6=8A=A4=E4=BB=A5=E5=8F=8A?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=8A=9F=E8=83=BD=20(#89)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 将设置移动到单文件上 * 将epub/cbz/login功能分离并格式化代码 * 尽可能向前支持配置文件 * 进一步拆分,修复范围下载不会下载结束话数 * 删除cbz使用的Pinyin库(bug多) * 增加请求502停止重试以及调整cbz功能 * Update README.md * 完成合并要求 --- .github/FUNDING.yml | 2 +- .github/ISSUE_TEMPLATE/bug-report.md | 7 +- .github/workflows/python-app.yml | 2 +- .idea/misc.xml | 3 + CODE_OF_CONDUCT.md | 8 +- Image_stitching.py | 32 +- README.md | 60 +- cbz.py | 37 ++ config.py | 76 +++ epub.py | 180 ++++++ function.py | 44 ++ login.py | 38 ++ main.py | 832 ++++----------------------- pyproject.toml | 16 +- requirements.txt | 3 +- settings.py | 305 ++++++++++ url.json | 12 +- 17 files changed, 882 insertions(+), 775 deletions(-) create mode 100644 cbz.py create mode 100644 config.py create mode 100644 epub.py create mode 100644 function.py create mode 100644 settings.py diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index f7e04fb..579ab68 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,3 @@ # These are supported funding model platforms -custom: ['https://afdian.net/@sakura_society'] +custom: [ 'https://afdian.net/@sakura_society' ] diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index 269d5d5..c5e0f99 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -20,9 +20,10 @@ Describe the bug in detail so that everyone can understand it 清晰描述复现步骤,让别人也能看到问题 Clearly describe the reproduction steps so that others can see the problem --> -1. -2. -3. + +1. +2. +3. ### 📄 [异常/日志]信息([Exception/Log] Information) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index dbb042a..b6b3ca3 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -2,7 +2,7 @@ name: python compile(Linux&Windows) on: release: - types: [published] + types: [ published ] push: branches: - master diff --git a/.idea/misc.xml b/.idea/misc.xml index b2058d2..573fda2 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,7 @@ + + \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 5138a54..a61e0ee 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,4 +1,3 @@ - # 贡献者公约 ## 我们的承诺 @@ -74,7 +73,8 @@ ## 参见 -本行为准则改编自 [Contributor Covenant][homepage] 2.1 版, 参见 [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]。 +本行为准则改编自 [Contributor Covenant][homepage] 2.1 版, +参见 [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]。 社区处理方针灵感来源于 [Mozilla's code of conduct enforcement ladder][Mozilla CoC]。 @@ -82,7 +82,11 @@ 其他语言翻译参见 [https://www.contributor-covenant.org/translations][translations]。 [homepage]: https://www.contributor-covenant.org + [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html + [Mozilla CoC]: https://github.com/mozilla/diversity + [FAQ]: https://www.contributor-covenant.org/faq + [translations]: https://www.contributor-covenant.org/translations diff --git a/Image_stitching.py b/Image_stitching.py index a0c1a8a..982e060 100644 --- a/Image_stitching.py +++ b/Image_stitching.py @@ -1,10 +1,11 @@ # -*- coding:utf-8 -*- - - -from PIL import Image + + import os - - + +from PIL import Image + + def file_name(file_dir): L = [] for root, dirs, files in os.walk(file_dir): @@ -12,27 +13,28 @@ def file_name(file_dir): if os.path.splitext(file)[1] == '.png': # 假设漫画全部都是png格式 L.append(os.path.join(root, file)) # 输出为数组 return L - - + + def join(png1, png2, NewImageName, SavePath): img1, img2 = Image.open(png1), Image.open(png2) size1, size2 = img1.size, img2.size # 获取两个图片长宽 - joint = Image.new('RGB', (size1[0]+size2[0], size1[1])) + joint = Image.new('RGB', (size1[0] + size2[0], size1[1])) loc1, loc2 = (0, 0), (size1[0], 0) - joint.paste(img2, loc1) #如需要左到右拼接,只要将img2改成img1,img1改成img2即可 + joint.paste(img2, loc1) # 如需要左到右拼接,只要将img2改成img1,img1改成img2即可 joint.paste(img1, loc2) - joint.save('%s%s.png' % (SavePath,NewImageName)) # 输出 - - + joint.save('%s%s.png' % (SavePath, NewImageName)) # 输出 + + def main(): ImgPath = input("图片文件夹位置(以/结尾):") SavePath = input("拼接后图片存放的位置(以/结尾):") image = file_name("ImgPath") # 获取当前目录下指定文件 j = 0 for i in image: - NewImage = "%s-%s"%(j+1,j) #拼接之后的图片的文件名 - join(image[j], image[j+1],NewImage,SavePath) # 如果是第一次就直接两图合并 - j = j + 1 + NewImage = "%s-%s" % (j + 1, j) # 拼接之后的图片的文件名 + join(image[j], image[j + 1], NewImage, SavePath) # 如果是第一次就直接两图合并 + j = j + 1 + if __name__ == '__main__': main() diff --git a/README.md b/README.md index 35c7fe7..3b5ea3b 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ -**请注意,在提交ISSUE前,请确保@misaka10843,以防止长时间未查看!** +> [!WARNING] +> **在2023.1.28更新之后,cbz功能的保存路径被改变,请所有使用cbz功能的用户最好[查看修改内容](https://github.com/misaka10843/copymanga-downloader/pull/89#issue-2104150068)了解新路径以及元信息问题!** -**因为尽可能缓解copymanga服务器压力,此程序限制了每分钟只能访问15次API,还请理解!** - -**如果需要稳定的下载器,请使用tachidesk以及配套的插件进行下载,本仓库仅为方便命令行直接下载而创建的** +> [!NOTE] +> **请注意,在提交ISSUE前,请确保@misaka10843,以防止长时间未查看!** +> +> **因为尽可能缓解copymanga服务器压力,此程序限制了每分钟只能访问15次API,还请理解!** # copymanga-downloader @@ -10,16 +12,17 @@ ## 前言💭 -推荐在模拟器/WSA/安卓手机中安装[tachiyomi](https://github.com/tachiyomiorg/tachiyomi),与[Copymanga插件](https://github.com/stevenyomi/copymanga),并使用tachiyomi下载! +推荐在模拟器/WSA/安卓手机中安装[tachiyomi](https://github.com/tachiyomiorg/tachiyomi) +与[Copymanga插件](https://github.com/stevenyomi/copymanga)并使用tachiyomi下载! 因为这样可以尽可能的保证下载稳定与订阅系统正常 此程序只是方便下载,而不是进行订阅操作(下载与订阅系统不稳定) - **我们已经正式基本支持命令行参数下载并且完全重构啦!** -请看[命令行参数](https://github.com/misaka10843/copymanga-downloader#%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%8F%82%E6%95%B0%EF%B8%8F)与[重大更新](https://github.com/misaka10843/copymanga-downloader#%E9%87%8D%E5%A4%A7%E6%9B%B4%E6%96%B0-)的部分! +请看[命令行参数](https://github.com/misaka10843/copymanga-downloader#%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%8F%82%E6%95%B0%EF%B8%8F) +与[重大更新](https://github.com/misaka10843/copymanga-downloader#%E9%87%8D%E5%A4%A7%E6%9B%B4%E6%96%B0-)的部分! ## 下载 @@ -53,7 +56,6 @@ or ![image](https://user-images.githubusercontent.com/69132853/229278511-3b2fe97b-5e01-4df0-9a23-d276de440472.png) - ## 技术栈 ⚒️ ![python](https://img.shields.io/badge/Python-3.0+-326c9c?style=for-the-badge&logo=Python&logoColor=326c9c) @@ -79,9 +81,12 @@ or (如果不是那就重新下载一遍,如果还有的话就发**issuse**吧qwq) -如果您是安卓用户,那么您可以使用[tachiyomi](https://github.com/tachiyomiorg/tachiyomi)客户端尝试下载(但是需要安装 `copymanga`的插件) +如果您是安卓用户,那么您可以使用[tachiyomi](https://github.com/tachiyomiorg/tachiyomi) +客户端尝试下载(但是需要安装 `copymanga`的插件) -如果您需要**从右到左**的拼接图片,并且两张为一组的话,您可以尝试使用[这个版本](https://github.com/misaka10843/copymanga-downloader/releases/tag/v2.2)中的 `Image_stitching.exe`来实现(只提供简单功能,并未做出优化) +如果您需要**从右到左**的拼接图片, +并且两张为一组的话,您可以尝试使用[这个版本](https://github.com/misaka10843/copymanga-downloader/releases/tag/v2.2) +中的 `Image_stitching.exe`来实现(只提供简单功能,并未做出优化) 如果发现无法获取/下载的时候,请多试几次,如果不行的话请重新设置一下配置,选择您能访问的API地址 @@ -91,7 +96,8 @@ or 详细请看[copymanga-downloader自动推送到kindle的使用教程](https://www.pursuecode.cn/archives/1705162565893) -请注意,在实现转换为epub时使用了第三方库[KCC](https://github.com/ciromattia/kcc/),您需要自行在[kcc/releases](https://github.com/ciromattia/kcc/releases)下载对应平台的执行程序 +请注意,在实现转换为epub时使用了第三方库[KCC](https://github.com/ciromattia/kcc/) +,您需要自行在[kcc/releases](https://github.com/ciromattia/kcc/releases)下载对应平台的执行程序 (windows平台需要下载`kcc_c2e_{版本号}.exe`) @@ -135,7 +141,6 @@ options: ![img](https://s2.loli.net/2023/01/06/FWklObHX6523CYs.png) - ### 命令示例 #### 如果我想下载*別哭啊魔王醬*的第一话 @@ -158,7 +163,8 @@ options: `python main.py --MangaPath xinglingganying --MangaStart 1 --MangaEnd 38` -**(注意!虽然说是下载全话,其实就是将范围定在了1话-最新话,所以如果下载其他漫画的全话请参考漫画更新到多少话了,然后再替换38)** +**(注意!虽然说是下载全话,其实就是将范围定在了1话-最新话,所以如果下载其他漫画的全话请参考漫画更新到多少话了,然后再替换38) +** ## 更新 🔬 @@ -186,7 +192,8 @@ options: 2022/3/29: 在 [@zhongfly](https://github.com/zhongfly) 帮助下支持了一些功能,并~~可能~~修复了问题,而且还顺便帮忙优化了下代码www -2022/3/24: 暂时支持设置一个功能(但是大概率无法下载,请注意,如果出现问题请在[这里](https://github.com/misaka10843/copymanga-downloader/issues/)提交相关信息 +2022/3/24: 暂时支持设置一个功能( +但是大概率无法下载,请注意,如果出现问题请在[这里](https://github.com/misaka10843/copymanga-downloader/issues/)提交相关信息 2022/2/25: 修复copymanga的url问题(copymanga.com似乎已经被弃用,已更换到copymanga.net) @@ -198,15 +205,27 @@ options: ## 放几张截图qwq(时效性不敢保证) -第一次初始化 +更改设置 + +![image](https://github.com/misaka10843/copymanga-downloader/assets/69132853/c3c97f7c-7202-4f17-acf3-6b06edd556b8) + +下载进度条 + +![image](https://github.com/misaka10843/copymanga-downloader/assets/69132853/f618f9cc-58d6-4bc8-a86d-1a67ff37b2ec) + +漫画搜索 + +![image](https://github.com/misaka10843/copymanga-downloader/assets/69132853/583b0d12-9017-4115-b210-2a13d7fc7027) + -![图片.png](https://s2.loli.net/2022/03/31/qKhZVtbguEAwQcJ.png) ## 如何使用 🖥️ ### 立即使用(Windows) -1.点击[这里](https://github.com/misaka10843/copymanga-downloader/releases/latest)下载最新的从作者电脑中编译的exe版本,或者下载GitHub中的编译文件[actions](https://github.com/misaka10843/copymanga-downloader/actions/)(稳定无法保证) +1.点击[这里](https://github.com/misaka10843/copymanga-downloader/releases/latest) +下载最新的从作者电脑中编译的exe版本,或者下载GitHub中的编译文件[actions](https://github.com/misaka10843/copymanga-downloader/actions/)( +稳定无法保证) 2.将此程序放入一个空文件夹(不放也没问题,就是数据会写到当前文件夹中) @@ -214,7 +233,8 @@ options: ### 立即使用(Linux,无法保证能否运行) -1.点击[actions](https://github.com/misaka10843/copymanga-downloader/actions)选择最新的编译(100%同步更新,但不能保障是否能运行) +1.点击[actions](https://github.com/misaka10843/copymanga-downloader/actions)选择最新的编译( +100%同步更新,但不能保障是否能运行) 2.下载 `附件`中的 `copymanga-download-Linux`压缩包 @@ -227,6 +247,7 @@ options: ### 编译/原代码使用(所有系统均支持) ⭐️ 建议pip安装(如果有本地有多个Python版本,建议用pipx安装) + ```bash # macOS安装pipx brew install pipx @@ -274,7 +295,8 @@ pipx install git+https://ghproxy.com/https://github.com/misaka10843/copymanga-do 3.找到类似 `comics?limit=12&offset=0&free_type=1&ordering=-datetime_modifier`的文件(?)后点击 -4.在**请求标头**中找到 `authorization: Token {各有各的不同}`,复制 `Token {各有各的不同}`即可,如 `Token 1293asd123s8adhh2juhsada2` +4.在**请求标头**中找到 `authorization: Token {各有各的不同}`,复制 `Token {各有各的不同}` +即可,如 `Token 1293asd123s8adhh2juhsada2` 图片([大图查看](https://i.loli.net/2021/11/18/Tv85D4a7GO9jNbn.png)): diff --git a/cbz.py b/cbz.py new file mode 100644 index 0000000..ca7b325 --- /dev/null +++ b/cbz.py @@ -0,0 +1,37 @@ +import os +import zipfile + +import config + + +def create_cbz(index, title, manga_name, save_dir, cbz_dir, path_word): + xml_data = f'' \ + '' \ + f'{title}' \ + f'{manga_name}' \ + f'{index}' \ + f'' + with open(os.path.join(os.path.join(config.SETTINGS['download_path'], save_dir), "ComicInfo.xml"), "w", + encoding='utf8') as file: + file.write(xml_data) + + start_dir = os.path.join(config.SETTINGS['download_path'], save_dir) + file_name = f"{manga_name}-{title}.cbz" + cbz_dir = os.path.join(cbz_dir, path_word) + file_path = os.path.join(cbz_dir, file_name) + + # 检测漫画保存目录是否存在 + if not os.path.exists(cbz_dir): + os.makedirs(cbz_dir) + + # 只添加指定类型的文件到zip文件中 + allowed_ext = ['.xml', '.jpg', '.png', '.jpeg', '.webp'] + with zipfile.ZipFile(file_path, 'w', zipfile.ZIP_DEFLATED) as zip_file: + for dir_path, dir_names, filenames in os.walk(start_dir): + fpath = dir_path.replace(start_dir, '') + fpath = fpath and fpath + os.sep or '' + for filename in filenames: + ext = os.path.splitext(filename)[1].lower() + if ext in allowed_ext: + zip_file.write(os.path.join(dir_path, filename), fpath + filename) diff --git a/config.py b/config.py new file mode 100644 index 0000000..cca17ef --- /dev/null +++ b/config.py @@ -0,0 +1,76 @@ +import datetime + +SETTINGS = { + "download_path": None, + "authorization": None, + "use_oversea_cdn": None, + "use_webp": None, + "proxies": None, + "api_url": None, + "HC": None, + "CBZ": None, + "cbz_path": None, + "api_time": 0.0, + "API_COUNTER": 0, + "loginPattern": "0", + "salt": None, + "username": None, + "password": None, + "send_to_kindle": None, + "kcc_cmd": None, + "email_address": None, + "email_passwd": None, + "kindle_address": None, + "email_smtp_address": None +} + +# ''' +# "epub_and_mail_to_kindle": None, +# "kcc_cmd": None, +# "email_address": None, +# "email_passwd": None, +# "kindle_address": None, +# ''' + +# 全局化设置,备份,防止命令行参数导致设置错位 +OG_SETTINGS = { + "download_path": None, + "authorization": None, + "use_oversea_cdn": None, + "use_webp": None, + "proxies": None, + "api_url": None, + "HC": None, + "CBZ": None, + "cbz_path": None, + "api_time": 0.0, + "API_COUNTER": 0, + "loginPattern": "0", + "salt": None, + "username": None, + "password": None, + "send_to_kindle": None, + "kcc_cmd": None, + "email_address": None, + "email_passwd": None, + "kindle_address": None, + "email_smtp_address": None +} + +# 全局化headers,节省空间 + +API_HEADER = { + 'User-Agent': '"User-Agent" to "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, ' + 'like Gecko) Chrome/102.0.5005.124 Safari/537.36 Edg/102.0.1245.44"', + 'version': datetime.datetime.now().strftime("%Y.%m.%d"), + 'region': '0', + 'webp': '0', + "platform": "1", + "referer": "https://www.copymanga.site/" +} + +PROXIES = {} + +API_COUNTER = 0 +IMG_API_COUNTER = 0 +IMG_CURRENT_TIME = 0 \ No newline at end of file diff --git a/epub.py b/epub.py new file mode 100644 index 0000000..b77f1f2 --- /dev/null +++ b/epub.py @@ -0,0 +1,180 @@ +import os +import platform +import random +import smtplib +import subprocess +from email import encoders +from email.header import Header +from email.mime.base import MIMEBase +from email.mime.multipart import MIMEMultipart + +from rich.prompt import Prompt, Confirm +from rich import print as print + +import config + + +def is_contains_chinese(strs): + for _char in strs: + if '\u4e00' <= _char <= '\u9fa5': + return True + return False + + +def epub_transformer(path: str, name: str, chapter: str) -> None: + """ + 将下载好的图片转化为epub待发送 + + :param path: 漫画存放的根地址 + :param name: 漫画的名称 + :param chapter: 漫画的章节 + :return: 空 + """ + # 定义命令和参数 + # command = SETTINGS["kcc_cmd"] + arguments = ["-o", f'{path}/{name}/{chapter}/{name} {chapter}.epub', "-t", f"{name} {chapter}", + f'{path}/{name}/{chapter}'] + command = config.SETTINGS["kcc_cmd"].split(" ") + arguments + subprocess.run(command, shell=True, capture_output=True, text=True) + + +def mailtest(sender: str, passwd: str, receiver: str, smtp_address: str, message: str) -> bool: + """ + 用于测试邮件是否可用 + + :param sender: + :param passwd: + :param receiver: + :param smtp_address: + :param message: + :return: 发送成功返回 True, 如果发送失败会返回 False + """ + try: + my_sender = sender + my_pass = passwd + my_user = receiver + + msg = MIMEMultipart() + + msg['From'] = sender + + file = MIMEBase('application', 'octet-stream') + file.add_header('Content-Disposition', 'attachment', filename=f"{message}.txt") + file.set_payload(message.encode()) + encoders.encode_base64(file) + msg.attach(file) + msg['From'] = Header(my_sender) + + server = smtplib.SMTP_SSL(smtp_address, 465) + server.login(my_sender, my_pass) + server.sendmail(my_sender, my_user, msg.as_string()) + server.quit() + + except Exception as e: + return False + return True + + +def mail(fd) -> bool: + """ + 发送邮件函数 + + :param fd: io文件句柄 + :return: 发送成功为真,否则为假 + """ + try: + message = MIMEMultipart() + file = MIMEBase('application', 'octet-stream') + file.add_header('Content-Disposition', 'attachment', filename=fd.name.split("/")[-1]) + file.set_payload(fd.read()) + encoders.encode_base64(file) + message.attach(file) + message['From'] = Header(config.SETTINGS["email_address"]) + + server = smtplib.SMTP_SSL(config.SETTINGS["email_smtp_address"], 465) + server.login(config.SETTINGS["email_address"], config.SETTINGS["email_passwd"]) + server.sendmail(config.SETTINGS["email_address"], config.SETTINGS["kindle_address"], message.as_string()) + server.quit() + + except Exception as e: + return False + return True + + +def set_kindle_config() -> None: + """ + 发送到kindle的设置 + + :return: 空 + """ + system = platform.system() + path = os.path.split(os.path.realpath(__file__))[0] + while not config.SETTINGS['kcc_cmd']: + if system == "Windows": + tmp = Prompt.ask( + "请输入kcc_c2e路径[italic yellow](建议先查看配置教程 https://www.pursuecode.cn/archives/1705162565893,默认为copymanga-downloader目录)[/]", + default=path) + "/kcc_c2e.exe" + else: + tmp = Prompt.ask( + "请输入kcc_c2e路径[italic yellow](建议先查看配置教程 https://www.pursuecode.cn/archives/1705162565893,默认为copymanga-downloader目录)[/]", + default=path) + "/kcc_c2e" + if is_contains_chinese(tmp): + print("[bold yellow]kcc_c2e路径请不要包含中文[/]") + continue + + if os.path.exists(tmp): + devices = "K1, K2, K34, K578, KDX, KPW, KPW5, KV, KO, K11, KS, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O, KoAO, KoN, KoC, KoL, KoF, KoS, KoE" + deviceset = set(devices.split(", ")) + while True: + device = Prompt.ask(f"请输入kcc设备参数, 支持的设备有[italic yellow]{devices}[/]", default=False) + device = device.strip() + if device not in deviceset: + print("[bold red]设备不存在,请重新输入[/]") + else: + break + config.SETTINGS["kcc_cmd"] = f"{tmp} -p {device} -f EPUB" + break + else: + print("[bold red]kcc_c2e不存在,请确认程序名称是否为kcc_c2e或是否安装kcc_c2e并且路径不含中文[/]") + + check = Confirm.ask("是否需要发送验证码到kindle验证", default=False) + while True: + email_address = Prompt.ask("请输入邮箱smtp账号[italic yellow](建议查看配置教程)[/]") + email_passwd = Prompt.ask("请输入邮箱smtp密码") + email_smtp_address = Prompt.ask("请输入邮箱smtp的地址") + kindle_address = Prompt.ask( + "请输入kindle推送邮件地址[italic yellow](如twoonefour_ABCDQA@kindle.com,具体请查看amazon设置)[/]") + # email_address = "489643427@qq.com" + # email_passwd = "xulsahtupltibjbh" + # email_smtp_address = "smtp.qq.com" + # kindle_address = "lys214412_uoeyap@kindle.com" + if check: + code = str(random.randint(100000, 999999)) + if mailtest(email_address, email_passwd, kindle_address, email_smtp_address, code): + if code == Prompt.ask("请输入kindle上显示的验证码"): + print("[bold green]验证码正确,验证成功[/]") + break + else: + print("[bold red]验证码错误,请重新输入配置[/]") + else: + break + config.SETTINGS["email_address"] = email_address + config.SETTINGS["email_passwd"] = email_passwd + config.SETTINGS["kindle_address"] = kindle_address + config.SETTINGS["email_smtp_address"] = email_smtp_address + + +def epub_transformerhelper(download_path, manga_name, chapter_name) -> None: + """ + 辅助函数,用于转换epub + + :param download_path: + :param manga_name: + :param chapter_name: + :return: + """ + if config.SETTINGS["send_to_kindle"]: + if not os.path.exists(f'{download_path}/{manga_name}/{chapter_name}/{manga_name} {chapter_name}.epub'): + epub_transformer(path=download_path, name=manga_name, chapter=chapter_name) + with open(f'{download_path}/{manga_name}/{chapter_name}/{manga_name} {chapter_name}.epub', "rb") as f: + mail(f) diff --git a/function.py b/function.py new file mode 100644 index 0000000..32f529d --- /dev/null +++ b/function.py @@ -0,0 +1,44 @@ +import time + +import config +from settings import save_settings + + +def is_contains_chinese(strs): + for _char in strs: + if '\u4e00' <= _char <= '\u9fa5': + return True + return False + + +# API限制相关 + +def api_restriction(): + config.API_COUNTER += 1 + # 防止退出后立马再次运行 + current_time = config.OG_SETTINGS['api_time'] + time_diff = time.time() - current_time + # 判断是否超过60秒 + if time_diff < 60 and config.API_COUNTER <= 1: + config.API_COUNTER = config.API_COUNTER + config.OG_SETTINGS['API_COUNTER'] + if config.API_COUNTER >= 15: + config.API_COUNTER = 0 + print("[bold yellow]您已经触发到了API请求阈值,我们将等60秒后再进行[/]") + time.sleep(60) + config.OG_SETTINGS['API_COUNTER'] = config.API_COUNTER + config.OG_SETTINGS['api_time'] = time.time() + # 将时间戳与API请求数量写入配置文件 + save_settings(config.OG_SETTINGS) + + +def img_api_restriction(): + config.IMG_API_COUNTER += 1 + # 防止退出后立马再次运行 + + time_diff = time.time() - config.IMG_CURRENT_TIME + # 判断是否超过60秒 + if time_diff < 60 and config.IMG_API_COUNTER >= 100: + print("[bold yellow]您已经触发到了图片服务器API请求阈值,我们将等60秒后再进行[/]") + time.sleep(60) + config.IMG_CURRENT_TIME = 0 + config.IMG_API_COUNTER = 0 diff --git a/login.py b/login.py index 82de553..3758dd7 100644 --- a/login.py +++ b/login.py @@ -4,6 +4,9 @@ # @Email : twoonefour@pursuecode.cn # @File : login.py import requests +from rich import print as print + +import config def login(**information: dict[{"username": None, "password": None, "url": None, "salt": None, "proxy": None}]) -> str: @@ -30,3 +33,38 @@ def login(**information: dict[{"username": None, "password": None, "url": None, except Exception as e: return None return None + + +def loginhelper(username: str, password: str, url: str) -> dict: + """ + 用于登录的函数,使用用户名和密码登录获取token后返回 + + :param username: (str)明文账户名 + :param password: (str)明文密码 + :param url: (str) 指定的api地址,即对应SETTINGS["api_url"]或api_urls[n],其中n为用户选择的api地址的序号 + :return: dict对象,其中包含token和随机生成的盐和加密后的密码,形如:{"token": res, "salt": salt, "password_enc": password_enc} + """ + from random import randint + from base64 import b64encode + # 随机生成盐 + salt = randint(100000, 999999) + # 加密 + password_enc = password + f"-{salt}" + password_enc = b64encode(password_enc.encode()).decode() + # 登录 + res = login(**{"username": username, "password": password_enc, "url": url, "salt": salt, "proxy": config.PROXIES}) + return {"token": res, "salt": salt, "password_enc": password_enc} + + +def login_information_builder(username: str, password: str, url: str, salt: str, proxy: dict) -> dict: + """ + 辅助函数,构建dict对象用于登录 + + :param username: string类型,明文账户名 + :param password: string类型,明文密码 + :param url: string类型,指定的api地址,即对应SETTINGS["api_url"]或api_urls[n],其中n为用户选择的api地址的序号 + :param salt: string类型,加密所用的盐 + :param proxy: dict类型,代理 + :return: dict类型,返回构造好的dict + """ + return {"username": username, "password": password, "url": url, "salt": salt, "proxy": proxy} diff --git a/main.py b/main.py index ae6d498..d1de33e 100644 --- a/main.py +++ b/main.py @@ -1,106 +1,31 @@ import argparse import csv -import datetime import json import os -import random -import smtplib +import string import sys import threading import time -import zipfile -import string -from xpinyin import Pinyin -import subprocess -import retrying as retrying + +import requests as requests from rich import print as print from rich.console import Console -from rich.prompt import Prompt, Confirm, IntPrompt -from login import login -import requests as requests -import platform -from email import encoders -from email.header import Header -from email.mime.base import MIMEBase -from email.mime.multipart import MIMEMultipart +from rich.progress import track +from rich.prompt import Prompt, IntPrompt + +import config +from cbz import create_cbz +from epub import epub_transformerhelper +from function import img_api_restriction, api_restriction +from login import login, login_information_builder +from settings import save_settings, load_settings, set_settings, change_settings console = Console(color_system='256', style=None) -# 全局化headers,节省空间 - -API_HEADER = { - 'User-Agent': '"User-Agent" to "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, ' - 'like Gecko) Chrome/102.0.5005.124 Safari/537.36 Edg/102.0.1245.44"', - 'version': datetime.datetime.now().strftime("%Y.%m.%d"), - 'region': '0', - 'webp': '0', - "platform": "1", - "referer": "https://www.copymanga.site/" -} -PROXIES = {} -# 全局化设置 -SETTINGS = { - "download_path": None, - "authorization": None, - "use_oversea_cdn": None, - "use_webp": None, - "proxies": None, - "api_url": None, - "HC": None, - "CBZ": None, - "cbz_path": None, - "api_time": 0.0, - "API_COUNTER": 0, - "loginPattern": "0", - "salt": None, - "username": None, - "password": None, - "send_to_kindle": None, - "kcc_cmd": None, - "email_address": None, - "email_passwd": None, - "kindle_address": None, - "email_smtp_address": None -} - -# ''' -# "epub_and_mail_to_kindle": None, -# "kcc_cmd": None, -# "email_address": None, -# "email_passwd": None, -# "kindle_address": None, -# ''' - -# 全局化设置,备份,防止命令行参数导致设置错位 -OG_SETTINGS = { - "download_path": None, - "authorization": None, - "use_oversea_cdn": None, - "use_webp": None, - "proxies": None, - "api_url": None, - "HC": None, - "CBZ": None, - "cbz_path": None, - "api_time": 0.0, - "API_COUNTER": 0, - "loginPattern": "0", - "salt": None, - "username": None, - "password": None, - "send_to_kindle": None, - "kcc_cmd": None, - "email_address": None, - "email_passwd": None, - "kindle_address": None, - "email_smtp_address": None -} UPDATE_LIST = [] + # API限制相关 -API_COUNTER = 0 -IMG_API_COUNTER = 0 -IMG_CURRENT_TIME = 0 def parse_args(): @@ -149,18 +74,16 @@ def parse_args(): # 命令行模式 def command_mode(): - global SETTINGS, PROXIES, API_HEADER - # 将命令行参数赋值到SETTINGS等相关全局变量 if ARGS.UseOSCdn or ARGS.UseWebp: - API_HEADER['use_oversea_cdn'] = ARGS.UseOSCdn - API_HEADER['use_webp'] = ARGS.UseWebp + config.API_HEADER['use_oversea_cdn'] = ARGS.UseOSCdn + config.API_HEADER['use_webp'] = ARGS.UseWebp if ARGS.Proxy: - PROXIES = { + config.PROXIES = { "http": ARGS.Proxy, "https": ARGS.Proxy } if ARGS.Output: - SETTINGS['download_path'] = ARGS.Output + config.SETTINGS['download_path'] = ARGS.Output manga_chapter_json = manga_chapter(ARGS.MangaPath, ARGS.MangaGroup) chapter_allocation(manga_chapter_json) print(f"[bold green][:white_check_mark: ]漫画已经下载完成![/]") @@ -202,14 +125,19 @@ def updates(): if update_want_to == 0: new_update = add_updates() response = requests.get( - f"https://api.{SETTINGS['api_url']}/api/v3/comic/{new_update[0]}/group/{new_update[1]}/chapters?limit=500" - f"&offset=0&platform=3", - headers=API_HEADER, proxies=PROXIES) + f"https://api.{config.SETTINGS['api_url']}/api/v3/comic/{new_update[0]}/group/{new_update[1]}" + f"/chapters?limit=500&offset=0&platform=3", + headers=config.API_HEADER, proxies=config.PROXIES) # 记录API访问量 api_restriction() - response.raise_for_status() + try: + response.raise_for_status() + except Exception as e: + time.sleep(5) + response.raise_for_status() manga_chapter_json = response.json() - manga_now=int(Prompt.ask(f"当前漫画有{manga_chapter_json['results']['total']}话的内容,请问您目前看到多少话了")) + manga_now = int( + Prompt.ask(f"当前漫画有{manga_chapter_json['results']['total']}话的内容,请问您目前看到多少话了")) save_updates(new_update[0], new_update[1], new_update[2], manga_now, False) else: del_manga_int = int(Prompt.ask("请输入想要删除的漫画前面的序号")) @@ -221,7 +149,7 @@ def updates(): def add_updates(): search_content = Prompt.ask("您需要搜索添加什么漫画呢") url = "https://api.%s/api/v3/search/comic?format=json&platform=3&q=%s&limit=10&offset={}" % ( - SETTINGS["api_url"], search_content) + config.SETTINGS["api_url"], search_content) offset = 0 current_page_count = 1 while True: @@ -331,12 +259,16 @@ def update_download(): def update_get_chapter(manga_path_word, manga_group_path_word, now_chapter): # 因为将偏移设置到最后下载的章节,所以可以直接下载全本 response = requests.get( - f"https://api.{SETTINGS['api_url']}/api/v3/comic/{manga_path_word}/group/{manga_group_path_word}/chapters" - f"?limit=500&offset={now_chapter}&platform=3", - headers=API_HEADER, proxies=PROXIES) + f"https://api.{config.SETTINGS['api_url']}/api/v3/comic/{manga_path_word}/group/{manga_group_path_word}" + f"/chapters?limit=500&offset={now_chapter}&platform=3", + headers=config.API_HEADER, proxies=config.PROXIES) # 记录API访问量 api_restriction() - response.raise_for_status() + try: + response.raise_for_status() + except Exception as e: + time.sleep(5) + response.raise_for_status() manga_chapter_json = response.json() # Todo 创建传输的json,并且之后会将此json保存为temp.json修复这个问题https://github.com/misaka10843/copymanga-downloader/issues/35 return_json = { @@ -357,7 +289,7 @@ def update_get_chapter(manga_path_word, manga_group_path_word, now_chapter): # 搜索相关 def search_list(url, offset, current_page_count): - response = requests.get(url.format(offset), headers=API_HEADER, proxies=PROXIES) + response = requests.get(url.format(offset), headers=config.API_HEADER, proxies=config.PROXIES) # 记录API访问量 api_restriction() # 解析JSON数据 @@ -397,7 +329,7 @@ def page_turning(selection, offset, data, current_page_count): def search(): search_content = Prompt.ask("您需要搜索什么漫画呢") url = "https://api.%s/api/v3/search/comic?format=json&platform=3&q=%s&limit=10&offset={}" % ( - SETTINGS["api_url"], search_content) + config.SETTINGS["api_url"], search_content) offset = 0 current_page_count = 1 while True: @@ -424,32 +356,33 @@ def search(): # 收藏相关 def search_on_collect(): - url = "https://%s/api/v3/member/collect/comics?limit=12&offset={}&free_type=1&ordering=-datetime_modifier" % ( - SETTINGS["api_url"]) - API_HEADER['authorization'] = SETTINGS['authorization'] + config.SETTINGS["api_url"]) + config.API_HEADER['authorization'] = config.SETTINGS['authorization'] offset = 0 current_page_count = 1 retry_count = 0 while True: # 发送GET请求 - response = requests.get(url.format(offset), headers=API_HEADER, proxies=PROXIES) + response = requests.get(url.format(offset), headers=config.API_HEADER, proxies=config.PROXIES) # 记录API访问量 api_restriction() # 解析JSON数据 data = response.json() if data['code'] == 401: settings_dir = os.path.join(os.path.expanduser("~"), ".copymanga-downloader/settings.json") - if SETTINGS["loginPattern"] == "1": + if config.SETTINGS["loginPattern"] == "1": print(f"[bold red]请求出现问题!疑似Token问题![{data['message']}][/]") print(f"[bold red]请删除{settings_dir}来重新设置!(或者也可以自行修改配置文件)[/]") sys.exit() else: - res = login(**loginInformationBuilder(SETTINGS["username"], SETTINGS["password"], SETTINGS["api_url"], SETTINGS["salt"], PROXIES)) + res = login(**login_information_builder(config.SETTINGS["username"], config.SETTINGS["password"], + config.SETTINGS["api_url"], + config.SETTINGS["salt"], config.PROXIES)) if res: - API_HEADER['authorization'] = f"Token {res}" - SETTINGS["authorization"] = f"Token {res}" - save_settings(SETTINGS) + config.API_HEADER['authorization'] = f"Token {res}" + config.SETTINGS["authorization"] = f"Token {res}" + save_settings(config.SETTINGS) continue time.sleep(2 ** retry_count) # 重试时间指数 retry_count += 1 @@ -478,7 +411,7 @@ def search_on_collect(): def collect_expect(): - url = f"https://api.{SETTINGS['api_url']}/api/v3/member/collect/comics" + url = f"https://api.{config.SETTINGS['api_url']}/api/v3/member/collect/comics" params = { "limit": 12, "offset": 0 @@ -488,8 +421,8 @@ def collect_expect(): f"[italic yellow](0:json,1:csv)[/]", choices=["0", "1"], default="1")) while True: - API_HEADER['authorization'] = SETTINGS['authorization'] - res = requests.get(url, params=params, headers=API_HEADER) + config.API_HEADER['authorization'] = config.SETTINGS['authorization'] + res = requests.get(url, params=params, headers=config.API_HEADER) res_json = json.loads(res.text) if res_json["code"] != 200: print(f"[bold red]无法获取到相关信息,请检查相关设置。Error:{res_json['message']}") @@ -518,11 +451,15 @@ def collect_expect(): # 漫画详细相关 def manga_group(manga_path_word): - response = requests.get(f"https://api.{SETTINGS['api_url']}/api/v3/comic2/{manga_path_word}", - headers=API_HEADER, proxies=PROXIES) + response = requests.get(f"https://api.{config.SETTINGS['api_url']}/api/v3/comic2/{manga_path_word}", + headers=config.API_HEADER, proxies=config.PROXIES) # 记录API访问量 api_restriction() - response.raise_for_status() + try: + response.raise_for_status() + except Exception as e: + time.sleep(5) + response.raise_for_status() manga_group_json = response.json() # 判断是否只有默认组 if len(manga_group_json["results"]["groups"]) == 1: @@ -540,12 +477,16 @@ def manga_group(manga_path_word): def manga_chapter(manga_path_word, group_path_word): response = requests.get( - f"https://api.{SETTINGS['api_url']}/api/v3/comic/{manga_path_word}/group/{group_path_word}/chapters?limit=500" - f"&offset=0&platform=3", - headers=API_HEADER, proxies=PROXIES) + f"https://api.{config.SETTINGS['api_url']}/api/v3/comic/{manga_path_word}/group/{group_path_word}" + f"/chapters?limit=500&offset=0&platform=3", + headers=config.API_HEADER, proxies=config.PROXIES) # 记录API访问量 api_restriction() - response.raise_for_status() + try: + response.raise_for_status() + except Exception as e: + time.sleep(5) + response.raise_for_status() manga_chapter_json = response.json() # Todo 创建传输的json,并且之后会将此json保存为temp.json修复这个问题https://github.com/misaka10843/copymanga-downloader/issues/35 @@ -577,7 +518,7 @@ def manga_chapter(manga_path_word, group_path_word): return_json["start"] = int(Prompt.ask("请输入开始下载的话数")) - 1 print(f"[italic blue]您选择从[yellow]{manga_chapter_json['results']['list'][return_json['start']]['name']}" f"[/yellow]开始下载[/]") - return_json["end"] = int(Prompt.ask("请输入结束下载的话数")) - 1 + return_json["end"] = int(Prompt.ask("请输入结束下载的话数")) print(f"[italic blue]您选择在[yellow]{manga_chapter_json['results']['list'][return_json['end']]['name']}" f"[/yellow]结束下载[/]") return return_json @@ -600,12 +541,16 @@ def chapter_allocation(manga_chapter_json): # 准备分配章节下载 for manga_chapter_info in manga_chapter_list: response = requests.get( - f"https://api.{SETTINGS['api_url']}/api/v3/comic/{manga_chapter_info['comic_path_word']}" + f"https://api.{config.SETTINGS['api_url']}/api/v3/comic/{manga_chapter_info['comic_path_word']}" f"/chapter2/{manga_chapter_info['uuid']}?platform=3", - headers=API_HEADER, proxies=PROXIES) + headers=config.API_HEADER, proxies=config.PROXIES) # 记录API访问量 api_restriction() - response.raise_for_status() + try: + response.raise_for_status() + except Exception as e: + time.sleep(5) + response.raise_for_status() manga_chapter_info_json = response.json() img_url_contents = manga_chapter_info_json['results']['chapter']['contents'] @@ -614,7 +559,7 @@ def chapter_allocation(manga_chapter_json): special_chars = string.punctuation + ' ' manga_name = ''.join(c for c in manga_name if c not in special_chars) num_images = len(img_url_contents) - download_path = SETTINGS['download_path'] + download_path = config.SETTINGS['download_path'] chapter_name = manga_chapter_info_json['results']['chapter']['name'] # 检查漫画文件夹是否存在 @@ -622,393 +567,68 @@ def chapter_allocation(manga_chapter_json): os.mkdir(f"{download_path}/{manga_name}/") # 创建多线程 threads = [] - with console.status(f"[bold yellow]正在下载:[{manga_name}]{chapter_name}(索引ID:" - f"{int(manga_chapter_info_json['results']['chapter']['index']) + 1})[/]"): - for i in range(num_images): - url = img_url_contents[i]['url'] - # 检查章节文件夹是否存在 - if not os.path.exists(f"{download_path}/{manga_name}/{chapter_name}/"): - os.mkdir(f"{download_path}/{manga_name}/{chapter_name}/") - # 组成下载路径 - filename = f"{download_path}/{manga_name}/{chapter_name}/{str(img_words[i] + 1).zfill(3)}.jpg" - t = threading.Thread(target=download, args=(url, filename)) - # 开始线程 - threads.append(t) - # 限制线程数量(十分不建议修改,不然很可能会被禁止访问) - if len(threads) == 4 or i == num_images - 1: - for t in threads: - # 添加一点延迟,错峰请求 - time.sleep(0.5) - t.start() - for t in threads: - time.sleep(0.5) - t.join() - threads.clear() + for i in track(range(num_images), description=f"[bold yellow]正在下载:[{manga_name}]{chapter_name}(索引ID:" + f"{int(manga_chapter_info_json['results']['chapter']['index']) + 1})[/]"): + url = img_url_contents[i]['url'] + # 检查章节文件夹是否存在 + if not os.path.exists(f"{download_path}/{manga_name}/{chapter_name}/"): + os.mkdir(f"{download_path}/{manga_name}/{chapter_name}/") + # 组成下载路径 + filename = f"{download_path}/{manga_name}/{chapter_name}/{str(img_words[i] + 1).zfill(3)}.jpg" + t = threading.Thread(target=download, args=(url, filename)) + # 开始线程 + threads.append(t) + # 限制线程数量(十分不建议修改,不然很可能会被禁止访问) + if len(threads) == 4 or i == num_images - 1: + for t in threads: + # 添加一点延迟,错峰请求 + time.sleep(0.5) + t.start() + for t in threads: + time.sleep(0.5) + t.join() + threads.clear() # 实施添加下载进度 if ARGS and ARGS.subscribe == "1": save_new_update(manga_chapter_info_json['results']['chapter']['comic_path_word'], manga_chapter_info_json['results']['chapter']['index'] + 1) print(f"[bold green][:white_check_mark:][{manga_name}]{chapter_name}下载完成![/]") - epubTransformerhelper(download_path, manga_name, chapter_name) - if SETTINGS['CBZ']: + epub_transformerhelper(download_path, manga_name, chapter_name) + if config.SETTINGS['CBZ']: with console.status(f"[bold yellow]正在保存CBZ存档:[{manga_name}]{chapter_name}[/]"): create_cbz(str(int(manga_chapter_info_json['results']['chapter']['index']) + 1), chapter_name, - manga_name, f"{manga_name}/{chapter_name}/", SETTINGS['cbz_path']) + manga_name, f"{manga_name}/{chapter_name}/", config.SETTINGS['cbz_path'], + manga_chapter_info_json['results']['chapter']['comic_path_word']) print(f"[bold green][:white_check_mark:]已将[{manga_name}]{chapter_name}保存为CBZ存档[/]") # 下载相关 -@retrying.retry(stop_max_attempt_number=3) -def download(url, filename): +def download(url, filename, overwrite=False): # 判断是否已经下载 - if os.path.exists(filename): + if not overwrite and os.path.exists(filename): print(f"[blue]您已经下载了{filename},跳过下载[/]") return + img_api_restriction() + if config.SETTINGS['HC'] == "1": + url = url.replace("c800x.jpg", "c1500x.jpg") try: - img_api_restriction() - if SETTINGS['HC'] == "1": - url = url.replace("c800x.jpg", "c1500x.jpg") - response = requests.get(url, headers=API_HEADER, proxies=PROXIES) + + response = requests.get(url, headers=config.API_HEADER, proxies=config.PROXIES) with open(filename, "wb") as f: f.write(response.content) except Exception as e: - print( - f"[bold red]无法下载{filename},似乎是CopyManga暂时屏蔽了您的IP,请稍后手动下载对应章节(章节话数为每话下载输出的索引ID),ErrMsg:{e}[/]") - - -# API限制相关 - -def api_restriction(): - global API_COUNTER, IMG_API_COUNTER - API_COUNTER += 1 - # 防止退出后立马再次运行 - current_time = OG_SETTINGS['api_time'] - time_diff = time.time() - current_time - # 判断是否超过60秒 - if time_diff < 60 and API_COUNTER <= 1: - API_COUNTER = API_COUNTER + OG_SETTINGS['API_COUNTER'] - if API_COUNTER >= 15: - API_COUNTER = 0 - print("[bold yellow]您已经触发到了API请求阈值,我们将等60秒后再进行[/]") - time.sleep(60) - OG_SETTINGS['API_COUNTER'] = API_COUNTER - OG_SETTINGS['api_time'] = time.time() - # 将时间戳与API请求数量写入配置文件 - save_settings(OG_SETTINGS) - - -def img_api_restriction(): - global IMG_API_COUNTER, IMG_CURRENT_TIME - IMG_API_COUNTER += 1 - # 防止退出后立马再次运行 - - time_diff = time.time() - IMG_CURRENT_TIME - # 判断是否超过60秒 - if time_diff < 60 and IMG_API_COUNTER >= 100: - print("[bold yellow]您已经触发到了图片服务器API请求阈值,我们将等60秒后再进行[/]") - time.sleep(60) - IMG_CURRENT_TIME = 0 - IMG_API_COUNTER = 0 - - -# 设置相关 - -def get_org_url(): - print("[italic yellow]正在获取CopyManga网站Url...[/]") - url = "https://ghproxy.net/https://raw.githubusercontent.com/misaka10843/copymanga-downloader/master/url.json" - try: - response = requests.get(url, proxies=PROXIES) - response.raise_for_status() - return response.json() - except Exception as e: - print("[bold yellow]无法链接至jsdelivr,准备直接访问Github[/]") - # 更换URL - url = "https://raw.githubusercontent.com/misaka10843/copymanga-downloader/master/url.json" + # 重新尝试一次 try: - response = requests.get(url, proxies=PROXIES) - response.raise_for_status() - return response.json() + time.sleep(5) + response = requests.get(url, headers=config.API_HEADER, proxies=config.PROXIES) + with open(filename, "wb") as f: + f.write(response.content) except Exception as e: - print(f"[bold red]无法链接至GitHub,请检查网络连接,ErrMsg:{e}[/]", ) - sys.exit() - -#检查字符串是否包含中文 -def is_contains_chinese(strs): - for _char in strs: - if '\u4e00' <= _char <= '\u9fa5': - return True - return False - - -def set_settings(): - global PROXIES - # 获取用户输入 - download_path = Prompt.ask("请输入保存路径[italic yellow](最后一个字符不能为斜杠)[/]", default=os.path.split(os.path.realpath(__file__))[0]) - use_oversea_cdn_input = Confirm.ask("是否使用海外CDN?", default=False) - use_webp_input = Confirm.ask("是否使用Webp?[italic yellow](可以节省服务器资源,下载速度也会加快)[/]", - default=True) - proxy = Prompt.ask("请输入代理地址[italic yellow](没有的话可以直接回车跳过)[/]") - hc_input = Confirm.ask("是否下载高分辨率图片[italic yellow](不选择可以节省服务器资源,下载速度也会加快)[/]", - default=False) - cbz = Confirm.ask("是否下载后打包成CBZ?", default=False) - send_to_kindle = Confirm.ask("是否启用半自动更新自动发送至kindle功能[italic yellow][/]", default=False) - if cbz: - while (True): - cbz_path = Prompt.ask("请输入CBZ文件的保存路径[italic yellow](最后一个字符不能为斜杠)[/]") - if (is_contains_chinese(cbz_path)): - print("路径请不要包含中文") - else: - break - else: - cbz_path = None - if proxy: - PROXIES = { - "http": proxy, - "https": proxy - } - if send_to_kindle: - set_kindle_config() - - api_urls = get_org_url() - for i, url in enumerate(api_urls): - print(f"{i + 1}->{url}") - choice = IntPrompt.ask("请输入要使用的API前面的数字") - - # input转bool - use_oversea_cdn = "0" - use_webp = "0" - hc = "0" - if use_oversea_cdn_input: - use_oversea_cdn = "1" - if use_webp_input: - use_webp = "1" - if hc_input: - hc = "1" - # 构造settings字典 - loginPattern = Prompt.ask("请输入登陆方式(1为token登录,2为账号密码持久登录)", default="1") - if loginPattern == "1": - authorization = Prompt.ask("请输入token") - elif loginPattern == "2": - while True: - username = Prompt.ask("请输入账号").strip() - password = Prompt.ask("请输入密码").strip() - if username == "" or password == "": - print("请输入账号密码") - continue - else: - res = loginhelper(username, password, api_urls[choice - 1]) - if res["token"]: - authorization = f"Token {res['token']}" - salt = res["salt"] - password = res["password_enc"] - break - if not os.path.exists(download_path): - os.mkdir(download_path) - settings = { - "download_path": download_path, - "authorization": authorization, - "use_oversea_cdn": use_oversea_cdn, - "use_webp": use_webp, - "proxies": proxy, - "api_url": api_urls[choice - 1], - "HC": hc, - "CBZ": cbz, - "cbz_path": cbz_path, - "api_time": 0.0, - "API_COUNTER": 0, - "loginPattern": loginPattern, - "salt": salt if loginPattern == "2" else None, - "username": username if loginPattern == "2" else None, - "password": password if loginPattern == "2" else None, - "send_to_kindle": send_to_kindle, - "kcc_cmd": SETTINGS["kcc_cmd"], - "email_address": SETTINGS["email_address"], - "email_passwd": SETTINGS["email_passwd"], - "email_smtp_address": SETTINGS["email_smtp_address"], - "kindle_address": SETTINGS["kindle_address"] - } - home_dir = os.path.expanduser("~") - settings_path = os.path.join(home_dir, ".copymanga-downloader/settings.json") - save_settings(settings) - print(f"[yellow]已将配置文件存放到{settings_path}中[/]") - - -def change_settings(): - global PROXIES - # 获取用户输入 - download_path = Prompt.ask("请输入保存路径[italic yellow](最后一个字符不能为斜杠)[/]", - default=SETTINGS['download_path']) - - use_oversea_cdn = True - use_webp = True - if SETTINGS['use_oversea_cdn'] == "0": - use_oversea_cdn = False - if SETTINGS['use_webp'] == "0": - use_webp = False - use_oversea_cdn_input = Confirm.ask("是否使用海外CDN?", default=use_oversea_cdn) - use_webp_input = Confirm.ask("是否使用Webp?[italic yellow](可以节省服务器资源,下载速度也会加快)[/]", - default=use_webp) - proxy = Prompt.ask("请输入代理地址[italic yellow](如果需要清除请输入0)[/]", default=SETTINGS['proxies']) - if SETTINGS.get('HC') is None: - hc_input = Confirm.ask("是否下载高分辨率图片[italic yellow](不选择可以节省服务器资源,下载速度也会加快)[/]", - default=False) - else: - hc_c = True - if SETTINGS['HC'] == "0": - hc_c = False - hc_input = Confirm.ask("是否下载高分辨率图片[italic yellow](不选择可以节省服务器资源,下载速度也会加快)[/]", - default=hc_c) - if SETTINGS.get('CBZ') is None or not SETTINGS.get("CBZ"): - cbz = Confirm.ask("是否下载后打包成CBZ?", default=False) - else: - cbz = True - hc_input = Confirm.ask("是否下载后打包成CBZ?", - default=cbz) - send_to_kindle_modify = Confirm.ask("是否需要修改kindle自动推送相关设置?[italic yellow][/]", default=False) - if cbz: - if SETTINGS.get('cbz_path') is None: - SETTINGS['cbz_path'] = None - - while (True): - cbz_path = Prompt.ask("请输入CBZ文件的保存路径[italic yellow](最后一个字符不能为斜杠)[/]", - default=SETTINGS['cbz_path']) - if (is_contains_chinese(cbz_path)): - print("路径请不要包含中文") - else: - break - else: - cbz_path = None - if proxy != SETTINGS['proxies'] and proxy != "0": - PROXIES = { - "http": proxy, - "https": proxy - } - if proxy == "0": - proxy = "" - api_urls = get_org_url() - for i, url in enumerate(api_urls): - print(f"{i + 1}->{url}") - choice = IntPrompt.ask("请输入要使用的API前面的数字") - if send_to_kindle_modify: - SETTINGS["send_to_kindle"] = Confirm.ask("是否继续使用kindle推送?[italic yellow][/]", default=True) - if SETTINGS["send_to_kindle"]: - set_kindle_config() - - # 构造settings字典 - - - login_change = Confirm.ask("是否要修改登陆方式?", default=False) - if login_change: - loginPattern = Prompt.ask("请输入登陆方式(1为token登录,2为账号密码持久登录,或者直接回车跳过)", default=SETTINGS["loginPattern"]) - if loginPattern == "1": - authorization = Prompt.ask("请输入token") - elif loginPattern == "2": - while True: - username = Prompt.ask("请输入账号").strip() - password = Prompt.ask("请输入密码").strip() - if username == "" or password == "": - print("请输入账号密码") - continue - else: - res = loginhelper(username, password, api_urls[choice - 1]) - if res["token"]: - SETTINGS["username"] = f"Token {res['token']}" - SETTINGS["salt"] = res["salt"] - SETTINGS["password"] = res["password_enc"] - break - else: - loginPattern = SETTINGS["loginPattern"] - authorization = SETTINGS["authorization"] - print(f"[yellow]我们正在更改您的设置中,请稍后[/]") - # input转bool - use_oversea_cdn = "0" - use_webp = "0" - hc = "0" - if use_oversea_cdn_input: - use_oversea_cdn = "1" - if use_webp_input: - use_webp = "1" - if hc_input: - hc = "1" - if not os.path.exists(download_path): - os.mkdir(download_path) - settings = { - "download_path": download_path, - "authorization": authorization, - "use_oversea_cdn": use_oversea_cdn, - "use_webp": use_webp, - "proxies": proxy, - "api_url": api_urls[choice - 1], - "HC": hc, - "CBZ": cbz, - "cbz_path": cbz_path, - "api_time": 0.0, - "API_COUNTER": 0, - "loginPattern": loginPattern, - "salt": SETTINGS["salt"] if loginPattern == "2" else None, - "username": SETTINGS["username"] if loginPattern == "2" else None, - "password": SETTINGS["password"] if loginPattern == "2" else None, - "send_to_kindle": SETTINGS["send_to_kindle"], - "kcc_cmd": SETTINGS["kcc_cmd"] if SETTINGS["send_to_kindle"] else None, - "email_address": SETTINGS["email_address"] if SETTINGS["send_to_kindle"] else None, - "email_passwd": SETTINGS["email_passwd"] if SETTINGS["send_to_kindle"] else None, - "email_smtp_address": SETTINGS["email_smtp_address"] if SETTINGS["send_to_kindle"] else None, - "kindle_address": SETTINGS["kindle_address"] if SETTINGS["send_to_kindle"] else None - } - home_dir = os.path.expanduser("~") - settings_path = os.path.join(home_dir, ".copymanga-downloader/settings.json") - save_settings(settings) - print(f"[yellow]已将重新修改配置文件并存放到{settings_path}中[/]") - - -def save_settings(settings): - home_dir = os.path.expanduser("~") - if not os.path.exists(os.path.join(home_dir, '.copymanga-downloader/')): - os.mkdir(os.path.join(home_dir, '.copymanga-downloader/')) - settings_path = os.path.join(home_dir, ".copymanga-downloader/settings.json") - # 写入settings.json文件 - with open(settings_path, "w") as f: - json.dump(settings, f) - - -def load_settings(): - global SETTINGS, PROXIES, OG_SETTINGS, API_HEADER - # 获取用户目录的路径 - home_dir = os.path.expanduser("~") - settings_path = os.path.join(home_dir, ".copymanga-downloader/settings.json") - # 检查是否有文件 - if not os.path.exists(settings_path): - return False, "settings.json文件不存在" - # 读取json配置文件 - with open(settings_path, 'r') as f: - settings = json.load(f) - - # 判断必要的字段是否存在 - necessary_fields = ["download_path", "authorization", "use_oversea_cdn", "use_webp", "proxies", "api_url"] - for field in necessary_fields: - if field not in settings: - return False, "settings.json中缺少必要字段{}".format(field) - SETTINGS = settings - if "HC" not in settings: - SETTINGS['HC'] = None - print("[bold yellow]我们更新了设置,请您按照需求重新设置一下,还请谅解[/]") - change_settings() - print("[bold yellow]感谢您的支持,重新启动本程序后新的设置将会生效[/]") - exit(0) - OG_SETTINGS = settings - # 设置请求头 - API_HEADER['use_oversea_cdn'] = settings['use_oversea_cdn'] - API_HEADER['use_webp'] = settings['use_webp'] - # 设置代理 - if settings["proxies"]: - PROXIES = { - "http": settings["proxies"], - "https": settings["proxies"] - } - return True, None + print( + f"[bold red]无法下载{filename},似乎是CopyManga暂时屏蔽了您的IP,请稍后手动下载对应章节(章节话数为每话下载输出的索引ID),ErrMsg:{e}[/]") def main(): @@ -1034,229 +654,5 @@ def main(): welcome() -# cbz格式转换 - -def create_cbz(index, title, manga_name, save_dir, cbz_dir): - pinyin = Pinyin() - xml_data = f"\n\n " \ - f"{pinyin.get_pinyin(title)}\n " \ - f"{pinyin.get_pinyin(manga_name)}\n " \ - f"{pinyin.get_pinyin(index)}\n" \ - f"" - with open(os.path.join(os.path.join(SETTINGS['download_path'], save_dir), "ComicInfo.xml"), "w") as file: - file.write(xml_data) - - start_dir = os.path.join(SETTINGS['download_path'], save_dir) - file_name = f"{save_dir}/{pinyin.get_pinyin(manga_name)}/{manga_name}{title}.cbz" - file_path = os.path.join(cbz_dir, file_name) - - # 只添加指定类型的文件到zip文件中 - allowed_ext = ['.xml', '.jpg', '.png', '.jpeg', '.webp'] - with zipfile.ZipFile(file_path, 'w', zipfile.ZIP_DEFLATED) as zip_file: - for dir_path, dir_names, filenames in os.walk(start_dir): - fpath = dir_path.replace(start_dir, '') - fpath = fpath and fpath + os.sep or '' - for filename in filenames: - ext = os.path.splitext(filename)[1].lower() - if ext in allowed_ext: - zip_file.write(os.path.join(dir_path, filename), fpath + filename) - - -def loginhelper(username: str, password: str, url: str) -> dict: - """ - 用于登录的函数,使用用户名和密码登录获取token后返回 - - :param username: (str)明文账户名 - :param password: (str)明文密码 - :param url: (str) 指定的api地址,即对应SETTINGS["api_url"]或api_urls[n],其中n为用户选择的api地址的序号 - :return: dict对象,其中包含token和随机生成的盐和加密后的密码,形如:{"token": res, "salt": salt, "password_enc": password_enc} - """ - from random import randint - from base64 import b64encode - # 随机生成盐 - salt = randint(100000, 999999) - # 加密 - password_enc = password + f"-{salt}" - password_enc = b64encode(password_enc.encode()).decode() - # 登录 - res = login(**{"username": username, "password": password_enc, "url": url, "salt": salt, "proxy": PROXIES}) - return {"token": res, "salt": salt, "password_enc": password_enc} - - -def loginInformationBuilder(username: str, password: str, url: str, salt: str, proxy: dict) -> dict: - """ - 辅助函数,构建dict对象用于登录 - - :param username: string类型,明文账户名 - :param password: string类型,明文密码 - :param url: string类型,指定的api地址,即对应SETTINGS["api_url"]或api_urls[n],其中n为用户选择的api地址的序号 - :param salt: string类型,加密所用的盐 - :param proxy: dict类型,代理 - :return: dict类型,返回构造好的dict - """ - return {"username": username, "password": password, "url": url, "salt": salt, "proxy": proxy} - - -def epubTransformer(path: str, name: str, chapter: str) -> None: - """ - 将下载好的图片转化为epub待发送 - - :param path: 漫画存放的根地址 - :param name: 漫画的名称 - :param chapter: 漫画的章节 - :return: 空 - """ - global SETTINGS - # 定义命令和参数 - # command = SETTINGS["kcc_cmd"] - arguments = ["-o", f'{path}/{name}/{chapter}/{name} {chapter}.epub', "-t", f"{name} {chapter}", f'{path}/{name}/{chapter}'] - command = SETTINGS["kcc_cmd"].split(" ") + arguments - subprocess.run(command, shell=True, capture_output=True, text=True) - - - -def mailtest(sender: str, passwd: str, receiver: str, smtp_address: str, message: str) -> bool: - """ - 用于测试邮件是否可用 - - :param sender: - :param passwd: - :param receiver: - :param smtp_address: - :param message: - :return: 发送成功返回 True, 如果发送失败会返回 False - """ - try: - my_sender = sender - my_pass = passwd - my_user = receiver - - msg = MIMEMultipart() - - msg['From'] = sender - - file = MIMEBase('application', 'octet-stream') - file.add_header('Content-Disposition', 'attachment', filename=f"{message}.txt") - file.set_payload(message.encode()) - encoders.encode_base64(file) - msg.attach(file) - msg['From'] = Header(my_sender) - - server = smtplib.SMTP_SSL(smtp_address, 465) - server.login(my_sender, my_pass) - server.sendmail(my_sender, my_user, msg.as_string()) - server.quit() - - except Exception as e: - return False - return True - - -def mail(fd) -> bool: - """ - 发送邮件函数 - - :param fd: io文件句柄 - :return: 发送成功为真,否则为假 - """ - global SETTINGS - try: - message = MIMEMultipart() - file = MIMEBase('application', 'octet-stream') - file.add_header('Content-Disposition', 'attachment', filename=fd.name.split("/")[-1]) - file.set_payload(fd.read()) - encoders.encode_base64(file) - message.attach(file) - message['From'] = Header(SETTINGS["email_address"]) - - server = smtplib.SMTP_SSL(SETTINGS["email_smtp_address"], 465) - server.login(SETTINGS["email_address"], SETTINGS["email_passwd"]) - server.sendmail(SETTINGS["email_address"], SETTINGS["kindle_address"], message.as_string()) - server.quit() - - except Exception as e: - return False - return True - - -def set_kindle_config() -> None: - """ - 发送到kindle的设置 - - :return: 空 - """ - global SETTINGS - system = platform.system() - path = os.path.split(os.path.realpath(__file__))[0] - while not SETTINGS['kcc_cmd']: - if system == "Windows": - tmp = Prompt.ask("请输入kcc_c2e路径[italic yellow](建议先查看配置教程 https://www.pursuecode.cn/archives/1705162565893,默认为copymanga-downloader目录)[/]", - default=path) + "/kcc_c2e.exe" - else: - tmp = Prompt.ask("请输入kcc_c2e路径[italic yellow](建议先查看配置教程 https://www.pursuecode.cn/archives/1705162565893,默认为copymanga-downloader目录)[/]", - default=path) + "/kcc_c2e" - if is_contains_chinese(tmp): - print("kcc_c2e路径请不要包含中文") - continue - - if os.path.exists(tmp): - devices = "K1, K2, K34, K578, KDX, KPW, KPW5, KV, KO, K11, KS, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O, KoAO, KoN, KoC, KoL, KoF, KoS, KoE" - deviceset = set(devices.split(", ")) - while True: - device = Prompt.ask(f"请输入kcc设备参数, 支持的设备有{devices}[italic yellow][/]", default=False) - device = device.strip() - if device not in deviceset: - print("设备不存在,请重新输入") - else: - break - SETTINGS["kcc_cmd"] = f"{tmp} -p {device} -f EPUB" - break - else: - print("kcc_c2e不存在,请确认程序名称是否为kcc_c2e或是否安装kcc_c2e并且路径不含中文") - - check = Confirm.ask("是否需要发送验证码到kindle验证[italic yellow][/]", default=False) - while True: - email_address = Prompt.ask("请输入邮箱smtp账号[italic yellow](建议查看配置教程)[/]") - email_passwd = Prompt.ask("请输入邮箱smtp密码[italic yellow][/]") - email_smtp_address = Prompt.ask("请输入邮箱smtp的地址[italic yellow][/]") - kindle_address = Prompt.ask("请输入kindle推送邮件地址[italic yellow](如twoonefour_ABCDQA@kindle.com,具体请查看amazon设置)[/]") - # email_address = "489643427@qq.com" - # email_passwd = "xulsahtupltibjbh" - # email_smtp_address = "smtp.qq.com" - # kindle_address = "lys214412_uoeyap@kindle.com" - if check: - code = str(random.randint(100000, 999999)) - if mailtest(email_address, email_passwd, kindle_address, email_smtp_address, code): - if code == Prompt.ask("请输入kindle上显示的验证码[italic yellow][/]"): - print("验证码正确,验证成功") - break - else: - print("验证码错误,请重新输入配置") - else: - break - SETTINGS["email_address"] = email_address - SETTINGS["email_passwd"] = email_passwd - SETTINGS["kindle_address"] = kindle_address - SETTINGS["email_smtp_address"] = email_smtp_address - - -def epubTransformerhelper(download_path, manga_name, chapter_name) -> None: - """ - 辅助函数,用于转换epub - - :param download_path: - :param manga_name: - :param chapter_name: - :return: - """ - global SETTINGS - if SETTINGS["send_to_kindle"]: - if not os.path.exists(f'{download_path}/{manga_name}/{chapter_name}/{manga_name} {chapter_name}.epub'): - epubTransformer(path=download_path, name=manga_name, chapter=chapter_name) - with open(f'{download_path}/{manga_name}/{chapter_name}/{manga_name} {chapter_name}.epub', "rb") as f: - mail(f) - - if __name__ == '__main__': main() diff --git a/pyproject.toml b/pyproject.toml index b86b335..627ade7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,16 +10,16 @@ repository = "https://github.com/misaka10843/copymanga-downloader" documentation = "" keywords = ["copymanga", "downloader"] classifiers = [ - "Topic :: Internet :: WWW/HTTP", - "Topic :: Utilities", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Operating System :: MacOS", - "Operating System :: Microsoft :: Windows", + "Topic :: Internet :: WWW/HTTP", + "Topic :: Utilities", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Operating System :: MacOS", + "Operating System :: Microsoft :: Windows", ] packages = [ - {include = "main.py"}, - {include = "Image_stitching.py"}, + { include = "main.py" }, + { include = "Image_stitching.py" }, ] [tool.poetry.dependencies] diff --git a/requirements.txt b/requirements.txt index e993a01..f798224 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,4 @@ rich>=13.3.2 retrying>=1.3.4 requests==2.31.0 -setuptools~=69.0.2 -xpinyin>=0.7.6 \ No newline at end of file +setuptools~=69.0.2 \ No newline at end of file diff --git a/settings.py b/settings.py new file mode 100644 index 0000000..8853f21 --- /dev/null +++ b/settings.py @@ -0,0 +1,305 @@ +import json +import os +import sys + +import requests +from rich.prompt import Prompt, Confirm, IntPrompt +from rich import print as print + +import config +from epub import set_kindle_config +from login import loginhelper + + +def is_contains_chinese(strs): + for _char in strs: + if '\u4e00' <= _char <= '\u9fa5': + return True + return False + + +def get_org_url(): + print("[italic yellow]正在获取CopyManga网站Url...[/]") + url = "https://ghproxy.net/https://raw.githubusercontent.com/misaka10843/copymanga-downloader/master/url.json" + try: + response = requests.get(url, proxies=config.PROXIES) + response.raise_for_status() + return response.json() + except Exception as e: + print("[bold yellow]无法链接至ghproxy.net,准备直接访问Github[/]") + # 更换URL + url = "https://raw.githubusercontent.com/misaka10843/copymanga-downloader/master/url.json" + try: + response = requests.get(url, proxies=config.PROXIES) + response.raise_for_status() + return response.json() + except Exception as e: + print(f"[bold red]无法链接至GitHub,请检查网络连接,ErrMsg:{e}[/]", ) + sys.exit() + + +def set_settings(): + # 获取用户输入 + download_path = Prompt.ask("请输入保存路径[italic yellow](最后一个字符不能为斜杠)[/]", + default=os.path.split(os.path.realpath(__file__))[0]) + use_oversea_cdn_input = Confirm.ask("是否使用海外CDN?", default=False) + use_webp_input = Confirm.ask("是否使用Webp?[italic yellow](可以节省服务器资源,下载速度也会加快)[/]", + default=True) + proxy = Prompt.ask("请输入代理地址[italic yellow](没有的话可以直接回车跳过,包括协议头)[/]") + hc_input = Confirm.ask("是否下载高分辨率图片[italic yellow](不选择可以节省服务器资源,下载速度也会加快)[/]", + default=False) + cbz = Confirm.ask("是否下载后打包成CBZ?", default=False) + send_to_kindle = Confirm.ask("是否启用半自动更新自动发送至kindle功能[italic yellow][/]", default=False) + if cbz: + while True: + cbz_path = Prompt.ask("请输入CBZ文件的保存路径[italic yellow](最后一个字符不能为斜杠)[/]") + if is_contains_chinese(cbz_path): + print("路径请不要包含中文") + else: + break + else: + cbz_path = None + if proxy: + config.PROXIES = { + "http": proxy, + "https": proxy + } + if send_to_kindle: + set_kindle_config() + + api_urls = get_org_url() + for i, url in enumerate(api_urls): + print(f"{i + 1}->{url}") + choice = IntPrompt.ask("请输入要使用的API前面的数字") + + # input转bool + use_oversea_cdn = "0" + use_webp = "0" + hc = "0" + if use_oversea_cdn_input: + use_oversea_cdn = "1" + if use_webp_input: + use_webp = "1" + if hc_input: + hc = "1" + # 构造settings字典 + login_pattern = Prompt.ask("请输入登陆方式(1为token登录,2为账号密码持久登录)", default="1") + if login_pattern == "1": + authorization = Prompt.ask("请输入token") + elif login_pattern == "2": + while True: + username = Prompt.ask("请输入账号").strip() + password = Prompt.ask("请输入密码").strip() + if username == "" or password == "": + print("请输入账号密码") + continue + else: + res = loginhelper(username, password, api_urls[choice - 1]) + if res["token"]: + authorization = f"Token {res['token']}" + salt = res["salt"] + password = res["password_enc"] + break + if not os.path.exists(download_path): + os.mkdir(download_path) + settings = { + "download_path": download_path, + "authorization": authorization, + "use_oversea_cdn": use_oversea_cdn, + "use_webp": use_webp, + "proxies": proxy, + "api_url": api_urls[choice - 1], + "HC": hc, + "CBZ": cbz, + "cbz_path": cbz_path, + "api_time": 0.0, + "API_COUNTER": 0, + "loginPattern": login_pattern, + "salt": salt if login_pattern == "2" else None, + "username": username if login_pattern == "2" else None, + "password": password if login_pattern == "2" else None, + "send_to_kindle": send_to_kindle, + "kcc_cmd": config.SETTINGS["kcc_cmd"], + "email_address": config.SETTINGS["email_address"], + "email_passwd": config.SETTINGS["email_passwd"], + "email_smtp_address": config.SETTINGS["email_smtp_address"], + "kindle_address": config.SETTINGS["kindle_address"] + } + home_dir = os.path.expanduser("~") + settings_path = os.path.join(home_dir, ".copymanga-downloader/settings.json") + save_settings(settings) + print(f"[yellow]已将配置文件存放到{settings_path}中[/]") + + +def change_settings(): + # 获取用户输入 + download_path = Prompt.ask("请输入保存路径[italic yellow](最后一个字符不能为斜杠)[/]", + default=config.SETTINGS['download_path']) + + use_oversea_cdn = True + use_webp = True + if config.SETTINGS['use_oversea_cdn'] == "0": + use_oversea_cdn = False + if config.SETTINGS['use_webp'] == "0": + use_webp = False + use_oversea_cdn_input = Confirm.ask("是否使用海外CDN?", default=use_oversea_cdn) + use_webp_input = Confirm.ask("是否使用Webp?[italic yellow](可以节省服务器资源,下载速度也会加快)[/]", + default=use_webp) + proxy = Prompt.ask("请输入代理地址[italic yellow](如果需要清除请输入0,输入时需包括协议头)[/]", + default=config.SETTINGS['proxies']) + if config.SETTINGS.get('HC') is None: + hc_input = Confirm.ask("是否下载高分辨率图片[italic yellow](不选择可以节省服务器资源,下载速度也会加快)[/]", + default=False) + else: + hc_c = True + if config.SETTINGS['HC'] == "0": + hc_c = False + hc_input = Confirm.ask("是否下载高分辨率图片[italic yellow](不选择可以节省服务器资源,下载速度也会加快)[/]", + default=hc_c) + if config.SETTINGS.get('CBZ') is None or not config.SETTINGS.get("CBZ"): + cbz = Confirm.ask("是否下载后打包成CBZ?", default=False) + else: + cbz = True + hc_input = Confirm.ask("是否下载后打包成CBZ?", + default=cbz) + send_to_kindle_modify = Confirm.ask("是否需要修改kindle自动推送相关设置?[italic yellow][/]", default=False) + if cbz: + if config.SETTINGS.get('cbz_path') is None: + config.SETTINGS['cbz_path'] = None + + while True: + cbz_path = Prompt.ask("请输入CBZ文件的保存路径[italic yellow](最后一个字符不能为斜杠)[/]", + default=config.SETTINGS['cbz_path']) + if is_contains_chinese(cbz_path): + print("路径请不要包含中文") + else: + break + else: + cbz_path = None + if proxy != config.SETTINGS['proxies'] and proxy != "0": + config.PROXIES = { + "http": proxy, + "https": proxy + } + if proxy == "0": + proxy = "" + api_urls = get_org_url() + for i, url in enumerate(api_urls): + print(f"{i + 1}->{url}") + choice = IntPrompt.ask("请输入要使用的API前面的数字") + if send_to_kindle_modify: + config.SETTINGS["send_to_kindle"] = Confirm.ask("是否继续使用kindle推送?[italic yellow][/]", default=True) + if config.SETTINGS["send_to_kindle"]: + set_kindle_config() + + # 构造settings字典 + + login_change = Confirm.ask("是否要修改登陆方式?", default=False) + if login_change: + login_pattern = Prompt.ask("请输入登陆方式(1为token登录,2为账号密码持久登录,或者直接回车跳过)", + default=config.SETTINGS["loginPattern"]) + if login_pattern == "1": + authorization = Prompt.ask("请输入token") + elif login_pattern == "2": + while True: + username = Prompt.ask("请输入账号").strip() + password = Prompt.ask("请输入密码").strip() + if username == "" or password == "": + print("请输入账号密码") + continue + else: + res = loginhelper(username, password, api_urls[choice - 1]) + if res["token"]: + config.SETTINGS["username"] = f"Token {res['token']}" + config.SETTINGS["salt"] = res["salt"] + config.SETTINGS["password"] = res["password_enc"] + break + else: + login_pattern = config.SETTINGS["loginPattern"] + authorization = config.SETTINGS["authorization"] + print(f"[yellow]我们正在更改您的设置中,请稍后[/]") + # input转bool + use_oversea_cdn = "0" + use_webp = "0" + hc = "0" + if use_oversea_cdn_input: + use_oversea_cdn = "1" + if use_webp_input: + use_webp = "1" + if hc_input: + hc = "1" + if not os.path.exists(download_path): + os.mkdir(download_path) + settings = { + "download_path": download_path, + "authorization": authorization, + "use_oversea_cdn": use_oversea_cdn, + "use_webp": use_webp, + "proxies": proxy, + "api_url": api_urls[choice - 1], + "HC": hc, + "CBZ": cbz, + "cbz_path": cbz_path, + "api_time": 0.0, + "API_COUNTER": 0, + "loginPattern": login_pattern, + "salt": config.SETTINGS["salt"] if login_pattern == "2" else None, + "username": config.SETTINGS["username"] if login_pattern == "2" else None, + "password": config.SETTINGS["password"] if login_pattern == "2" else None, + "send_to_kindle": config.SETTINGS["send_to_kindle"], + "kcc_cmd": config.SETTINGS["kcc_cmd"] if config.SETTINGS["send_to_kindle"] else None, + "email_address": config.SETTINGS["email_address"] if config.SETTINGS["send_to_kindle"] else None, + "email_passwd": config.SETTINGS["email_passwd"] if config.SETTINGS["send_to_kindle"] else None, + "email_smtp_address": config.SETTINGS["email_smtp_address"] if config.SETTINGS["send_to_kindle"] else None, + "kindle_address": config.SETTINGS["kindle_address"] if config.SETTINGS["send_to_kindle"] else None + } + home_dir = os.path.expanduser("~") + settings_path = os.path.join(home_dir, ".copymanga-downloader/settings.json") + save_settings(settings) + print(f"[yellow]已将重新修改配置文件并存放到{settings_path}中[/]") + + +def save_settings(settings): + home_dir = os.path.expanduser("~") + if not os.path.exists(os.path.join(home_dir, '.copymanga-downloader/')): + os.mkdir(os.path.join(home_dir, '.copymanga-downloader/')) + settings_path = os.path.join(home_dir, ".copymanga-downloader/settings.json") + # 写入settings.json文件 + with open(settings_path, "w") as f: + json.dump(settings, f) + + +def load_settings(): + # 获取用户目录的路径 + home_dir = os.path.expanduser("~") + settings_path = os.path.join(home_dir, ".copymanga-downloader/settings.json") + # 检查是否有文件 + if not os.path.exists(settings_path): + return False, "settings.json文件不存在" + # 读取json配置文件 + with open(settings_path, 'r') as f: + settings = json.load(f) + + # 判断必要的字段是否存在 + necessary_fields = ["download_path", "authorization", "use_oversea_cdn", "use_webp", "proxies", "api_url"] + for field in necessary_fields: + if field not in settings: + return False, "settings.json中缺少必要字段{}".format(field) + config.SETTINGS = settings + if "HC" not in settings: + config.SETTINGS['HC'] = None + print("[bold yellow]我们更新了设置,请您按照需求重新设置一下,还请谅解[/]") + change_settings() + print("[bold yellow]感谢您的支持,重新启动本程序后新的设置将会生效[/]") + exit(0) + config.OG_SETTINGS = settings + # 设置请求头 + config.API_HEADER['use_oversea_cdn'] = settings['use_oversea_cdn'] + config.API_HEADER['use_webp'] = settings['use_webp'] + # 设置代理 + if settings["proxies"]: + config.PROXIES = { + "http": settings["proxies"], + "https": settings["proxies"] + } + return True, None diff --git a/url.json b/url.json index 6b17753..11980e8 100644 --- a/url.json +++ b/url.json @@ -1,8 +1,8 @@ [ - "copymanga.org", - "copymanga.info", - "copymanga.net", - "copymanga.site", - "copymanga.tv", - "mangacopy.com" + "copymanga.org", + "copymanga.info", + "copymanga.net", + "copymanga.site", + "copymanga.tv", + "mangacopy.com" ]