This commit is contained in:
王老板 2024-09-27 16:59:07 +08:00
commit a3583d9e4e
21 changed files with 1278 additions and 0 deletions

176
Downloader.py Normal file
View File

@ -0,0 +1,176 @@
import requests
from bs4 import BeautifulSoup # 用于代替正则式 取源码中相应标签中的内容
import os
from rich.progress import track as tqdm
from concurrent.futures import ThreadPoolExecutor, wait
import time
from PIL import Image
from utils import *
class Downloader(object):
def __init__(self, comic_name, root_path = './', url_prev='.site', high_quality=False):
self.comic_name = comic_name
self.root_path = root_path
self.url_prev = url_prev
self.high_quality = high_quality
self.comic_msg_url = f"https://api.copymanga{url_prev}/api/v3/comic2/{comic_name}"
self.comic_url_api = 'https://api.copymanga{}/api/v3/comic/{}/group/{}/chapters?limit=500&offset=0&platform=3'
self.chap_url_api = 'https://api.copymanga{}/api/v3/comic/{}/chapter2/{}?platform=3'
self.header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36 Edg/87.0.664.47', 'platform': '1'}
self.max_thread_num = 16
self.pool = ThreadPoolExecutor(self.max_thread_num)
self.buffer_map = {}
def get_comic_msg(self, is_gui=False, signal=None, editline=None):
req = requests.get(self.comic_msg_url, headers=self.header).json()
req = req['results']
self.comic_title = req['comic']['name']
self.comic_author = req['comic']['author'][0]['name']
self.cover_url = req['comic']['cover']
cls_dict = req['groups']
self.cls_dict = {}
for key in cls_dict.keys():
self.cls_dict[cls_dict[key]['name']] = cls_dict[key]['path_word']
if len(cls_dict.keys())==1:
self.url_cls = list(self.cls_dict.values())[0]
elif len(cls_dict.keys())>1:
choise_name = self.get_choise(list(self.cls_dict.keys()), is_gui, signal, editline)
self.url_cls = self.cls_dict[choise_name]
self.comic_url = self.comic_url_api.format(self.url_prev, self.comic_name, self.url_cls)
def get_comic_chaps(self):
req = requests.get(self.comic_url, headers=self.header)
comic_urls = req.json()['results']['list']
num_chaps = comic_urls[0]['count']
offset = 0
while offset<num_chaps:
offset += 500
req = requests.get(self.comic_url.replace('offset=0&', f'offset={offset}&'), headers=self.header)
comic_urls += req.json()['results']['list']
self.chap_name_list = []
self.chap_uuid_list = []
self.chap_pagenum_list = []
for comic_url in comic_urls:
self.chap_name_list.append(comic_url['name'])
self.chap_uuid_list.append(comic_url['uuid'])
self.chap_pagenum_list.append(comic_url['size'])
self.comic_path = os.path.join(self.root_path, check_chars(self.comic_title))
return self.chap_name_list, self.chap_uuid_list, self.chap_pagenum_list
def get_image(self, is_gui=False, signal=None):
self.pre_request_img()
img_path = self.img_path
if is_gui:
len_iter = len(self.img_url_map.items())
signal.emit('start')
for i, (img_url, img_name) in enumerate(self.img_url_map.items()):
content = self.get_html_img(img_url, is_buffer=True)
with open(img_path+f'/{img_name}.jpg', 'wb') as f:
f.write(content) #写入二进制内容
signal.emit(int(100*(i+1)/len_iter))
def download_single_chap(self, chap_name, uuid, page_num, multithread=True, is_gui=False, signal=None):
print('正在下载'+chap_name)
chap_path = os.path.join(self.comic_path, chap_name)
os.makedirs(chap_path, exist_ok=True)
if len(os.listdir(chap_path))==page_num:
return
img_url = self.chap_url_api.format(self.url_prev, self.comic_name, uuid)
while True:
req = requests.get(img_url, headers=self.header)
try:
req = req.json()['results']
except:
req = 'throttled'
if 'throttled' not in str(req):
break
else:
print('触发访问频率上限,重新请求.....')
time.sleep(10)
img_urls = [url['url'] for url in req['chapter']['contents']]
if self.high_quality:
img_urls = [url.replace('c800x', 'c1500x') for url in img_urls]
img_nos = req['chapter']['words']
if multithread:
for img_url in img_urls:
self.pool.submit(self.prev_buffer, img_url)
len_iter = len(img_urls)
if is_gui:
signal.emit('start')
for i, (img_no, img_url) in enumerate(zip(img_nos, img_urls)):
chap_name = os.path.join(chap_path, str(int(img_no)+1).zfill(3)+'.jpg')
self.download_img(img_url, chap_name, is_buffer=multithread)
signal.emit(int(100*(i+1)/len_iter))
signal.emit('end')
else:
for i in tqdm(range(len_iter)):
img_no, img_url = img_nos[i], img_urls[i]
chap_name = os.path.join(chap_path, str(img_no+1).zfill(3)+'.jpg')
self.download_img(img_url, chap_name, is_buffer=multithread)
def download_cover(self):
os.makedirs(self.comic_path, exist_ok=True)
cover_img_name = os.path.join(self.comic_path, 'cover.jpg')
self.download_img(self.cover_url, cover_img_name)
def download_img(self, img_url, file_name, is_buffer=False):
if is_buffer:
while img_url not in self.buffer_map.keys():
time.sleep(1)
req = self.buffer_map[img_url]
else:
req = requests.get(img_url, headers=self.header).content
with open(file_name, 'wb') as f:
f.write(req)
def prev_buffer(self, url):
if url not in self.buffer_map.keys():
req = requests.get(url, headers=self.header).content
self.buffer_map[url] = req
def get_cover(self, chap_name, is_gui=False, signal=None):
chap_path = os.path.join(self.comic_path, chap_name)
imgfile = os.path.join(chap_path, '001.jpg')
img = Image.open(imgfile)
img_w, img_h = img.size
signal_msg = (imgfile, img_h, img_w)
if is_gui:
signal.emit(signal_msg)
def get_choise(self, choise_list, is_gui=False, signal=None, editline=None):
if is_gui:
error_msg = '漫画有多个部分, 请下拉选择框选择想下载的部分'
editline.addItems(choise_list)
editline.setCurrentIndex(0)
print(error_msg)
signal.emit('hang')
time.sleep(1)
while not editline.isHidden():
time.sleep(1)
choise = editline.text()
editline.clear()
else:
error_msg = '漫画有多个部分, 请输入想下载的部分的序号'
print(error_msg)
for choise_no, choise in enumerate(choise_list):
print(f'[{str(choise_no+1)}]', choise)
choise_no = input('请输入序号:')
choise_no = int(choise_no)-1
choise = choise_list[choise_no]
return choise
if __name__=='__main__':
comic_name = 'yaoyeluying'
# comic_name = 'zgmsbywt'
downloader = Downloader(comic_name=comic_name)
for i in range(0, 3):
chap_name = downloader.chap_name_list[i]
chap_uuid = downloader.chap_uuid_list[i]
downloader.download_single_chap(chap_name, chap_uuid)

112
Editer.py Normal file
View File

@ -0,0 +1,112 @@
import zipfile
import os
import shutil
from utils import *
from PIL import Image
import numpy as np
class Editer(object):
def __init__(self, title, author, chap_list, comic_root, out_root, delete_comic=False):
self.title = self.get_epub_title(title, chap_list)
self.author = author
self.chap_list = chap_list
self.comic_root = comic_root
self.out_root = out_root
self.img_list = []
self.chap_first_imgs = []
self.delete_comic = delete_comic
def pack_img(self):
self.epub_path = os.path.join(self.out_root, 'tmp')
self.epub_oebps_path = os.path.join(self.out_root, 'tmp/OEBPS')
self.epub_img_path = os.path.join(self.out_root, 'tmp/OEBPS/Images')
self.epub_text_path = os.path.join(self.out_root, 'tmp/OEBPS/Text')
os.makedirs(self.epub_path, exist_ok=True)
os.makedirs(self.epub_oebps_path, exist_ok=True)
os.makedirs(self.epub_img_path, exist_ok=True)
os.makedirs(self.epub_text_path, exist_ok=True)
print('正在打包处理图片......')
for chap_no, chap in enumerate(self.chap_list):
img_path = os.path.join(self.comic_root, chap)
imgs = os.listdir(img_path)
img_no = 0
self.chap_first_imgs.append(str(chap_no+1).zfill(3) + '_' + str(0).zfill(4) + '.jpg')
for img_no, img in enumerate(imgs):
img_old_path = os.path.join(img_path, img)
img_new = str(chap_no + 1).zfill(3) + '_' + str(img_no).zfill(4) + '.jpg'
img_epub_path = os.path.join(self.epub_img_path, img_new)
shutil.copyfile(img_old_path, img_epub_path)
self.img_list.append(img_new)
def get_epub_title(self, title, chap_list):
if len(chap_list)==1:
title = title + '-' + chap_list[0]
else:
title = title + '-' + chap_list[0] + '-' + chap_list[-1]
return title
def typesetting(self):
print('正在生成排版......')
for img in self.img_list:
text_file = os.path.join(self.epub_text_path, img.replace('.jpg', '.xhtml'))
text_htmls = get_xhtml(img)
with open(text_file, 'w+', encoding='utf-8') as f:
f.writelines(text_htmls)
print('正在生成元数据......')
#封面
cover_path = os.path.join(self.epub_img_path, '000_0000.jpg')
shutil.copyfile(os.path.join(self.comic_root, 'cover.jpg'), cover_path)
textfile = os.path.join(self.epub_text_path, 'cover.xhtml')
# img = cv2.imread(cover_path)
img = Image.open(cover_path)
img = np.array(img)
img_htmls = get_cover_html(img.shape[1], img.shape[0])
with open(textfile, 'w+', encoding='utf-8') as f:
f.writelines(img_htmls)
#内容页
content_htmls = get_content_html(self.title, self.author, self.img_list)
textfile = os.path.join(self.epub_oebps_path, 'content.opf')
with open(textfile, 'w+', encoding='utf-8') as f:
f.writelines(content_htmls)
#目录
toc_htmls = get_toc_html(self.title, self.chap_list, self.chap_first_imgs)
textfile = os.path.join(self.epub_oebps_path, 'toc.ncx')
with open(textfile, 'w+', encoding='utf-8') as f:
f.writelines(toc_htmls)
#get epub_head
mimetype = 'application/epub+zip'
mimetypefile = os.path.join(self.epub_path, 'mimetype')
with open(mimetypefile, 'w+', encoding='utf-8') as f:
f.write(mimetype)
metainf_folder = os.path.join(self.epub_path, 'META-INF')
os.makedirs(metainf_folder, exist_ok=True)
container = metainf_folder + '/container.xml'
container_htmls = get_container_html()
with open(container, 'w+', encoding='utf-8') as f:
f.writelines(container_htmls)
def get_epub(self):
print('正在打包EPUB......')
epub_file = os.path.join(self.out_root, check_chars(self.title) + '.epub')
with zipfile.ZipFile(epub_file, "w", zipfile.ZIP_DEFLATED) as zf:
for dirpath, dirnames, filenames in os.walk(self.epub_path):
fpath = dirpath.replace(self.epub_path,'')
fpath = fpath and fpath + os.sep or ''
for filename in filenames:
zf.write(os.path.join(dirpath, filename), fpath+filename)
shutil.rmtree(self.epub_path)
if self.delete_comic:
shutil.rmtree(self.comic_root)
print('EPUB生成成功, 路径【{}'.format(epub_file))

74
README.md Normal file
View File

@ -0,0 +1,74 @@
<div align="center">
<img src="resource/logo.png" width="150" style="margin-right: 3000px;"/>
</div>
<h1 align="center">
&nbsp;&nbsp;&nbsp;&nbsp;拷贝漫画EPUB下载器
</h1>
[拷贝漫画](https://www.copymanga.site)(copymanga)下载, 并打包为EPUB格式。
特性:
* Fluent Design风格界面下载进度与书籍封面显示主题切换下载目录自定义。
* 前后端分离,同时支持命令行版本。
* 章节批量下载。
* EPUB格式自动打包。
* 图片质量自定义选择。
* 断点续传,避免重复下载。
* 多线程预缓存策略,下载速度快。
* 网站域名自定义更换,防止被墙。
* ...................
有建议或bug可以提issue也可以加QQ群获得更多信息563072544
图形界面使用[PyQt-Fluent-Widgets](https://pyqt-fluent-widgets.readthedocs.io/en/latest/index.html)界面编写。
[release](https://github.com/ShqWW/copymanga-download/releases)页面发布了已经打包好的exe可执行程序包括图形化版本和命令行版本(系统最低要求Windows 10)。
界面样例:
<div align="center">
<img src="resource/example1.png" width="400"/>
<img src="resource/example2.png" width="400"/>
</div>
## 使用前安装需要的包
```
pip install -r requirements.txt -i https://pypi.org/simple/
```
## 使用命令行模式运行(无需安装图形界面库支持Linux):
```
python copymanga.py
```
## 使用图形界面运行:
```
python copymanga_gui.py
```
## 使用pyinstaller打包:
```
pip install pyinstaller
```
```
pyinstaller -F -w -i .\resource\logo.png .\copymanga_gui.py
```
## 相关项目:
* [轻小说文库EPUB下载器](https://github.com/ShqWW/lightnovel-download)
* [哔哩轻小说EPUB下载器](https://github.com/ShqWW/bilinovel-download)
* [拷贝漫画EPUB下载器](https://github.com/ShqWW/copymanga-download)
## EPUB书籍漫画编辑和管理工具推荐
1. [Sigil](https://sigil-ebook.com/)
2. [Calibre](https://www.calibre-ebook.com/)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

146
comic.py Normal file
View File

@ -0,0 +1,146 @@
#!/usr/bin/python
# -*- coding:utf-8 -*-
import requests # 用来抓取网页的html源码
import random # 取随机数
from bs4 import BeautifulSoup # 用于代替正则式 取源码中相应标签中的内容
import sys
import time # 时间相关操作
import js2py
import os
from tqdm import tqdm
class downloader(object):
def __init__(self):
self.server = 'https://www.iimanhua.cc/'
self.target = 'https://www.iimanhua.cc/comic/2189/'
self.names = [] # 章节名
self.urls = [] # 章节链接
self.nums = 0 # 章节数
"""
获取html文档内容
"""
def get_content(self, url):
# 设置headers是为了模拟浏览器访问 否则的话可能会被拒绝 可通过浏览器获取,这里不用修改
header = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Connection': 'keep-alive',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh-CN, zh',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36'
}
# 设置一个超时时间 取随机数 是为了防止网站被认定为爬虫,不用修改
timeout = random.choice(range(80, 180))
while True:
try:
req = requests.get(url=url, headers=header)
req.encoding = 'GBK' #这里是网页的编码转换,根据网页的实际需要进行修改,经测试这个编码没有问题
break
except Exception as e:
print('3', e)
time.sleep(random.choice(range(5, 10)))
return req.text
"""
获取下载的章节目录
"""
def get_download_catalogue(self, url):
# html = self.get_content(url)
# bf = BeautifulSoup(html, 'html.parser')
# print(bf)
# texts = bf.find_all('div', {'class': 'listmain'})
finename = "./kkk.txt"
f = open(finename,'r', encoding='utf-8') # 返回一个文件对象
line = f.readline()
while line:
# print(line.strip('\n'))
name, url = self.get_url(line)
self.names.append(name)
self.urls.append(self.server + url)
line = f.readline()
# print(self.urls)
self.nums = len(self.urls)
"""
获取下载的具体章节
"""
def get_url(self, url_str):
st = url_str.find("/comic")
ed = url_str.find("\" title")
st2 = url_str.find("")
ed2 = url_str.find("\">")
url = url_str[st:ed]
name = url_str[st2+1:ed2]
return name, url
def get_download_content(self, chap, path, name, url):
#html = self.get_content(url)
chappath = os.path.join(path, str(chap).zfill(3)+ '' +name)
os.makedirs(chappath, exist_ok=True)
html = self.get_content(url)
bf = BeautifulSoup(html, 'html.parser')
jscmd = bf.find('script', {'language': 'javascript', 'type': 'text/javascript'}).text
# print(jscmd)
jscmd += '''
\nvar b=base64decode(packed).slice(4);
var a = eval(b);
'''
# print(jscmd)
jsres = js2py.eval_js(jscmd) #执行js代码
imgurls = self.get_img_url(jsres)
page_no = 1
for imgurl in tqdm(imgurls):
r=requests.get(imgurl)
with open(chappath+'/'+str(page_no)+'.jpg','wb') as f:
f.write(r.content) #写入二进制内容
page_no += 1
"""
解析url
"""
def get_img_url(self, jsres):
imgserver = 'https://res.img.96youhuiquan.com/'
imgstrs = jsres.split(";")
imgurls = []
for imgstr in imgstrs:
if len(imgstr)>1:
st = imgstr.find("]=")
imgurl = imgstr[st+3:-1]
imgurls.append(imgserver+imgurl)
return imgurls
def writer(self, path, name, text):
write_flag = True
with open(path, 'a', encoding='utf-8') as f:
f.writelines(name)
f.write('\n')
f.writelines(text)
f.write('\n\n')
if __name__ == '__main__':
path = './duannao/'
dl = downloader()
dl.get_download_catalogue(dl.target)
for chap_no in range(77-1, dl.nums):
print("" + str(chap_no+1) + "")
dl.get_download_content(chap_no+1, path, dl.names[chap_no], dl.urls[chap_no])

131
copymanga.py Normal file
View File

@ -0,0 +1,131 @@
import argparse
from Downloader import Downloader
from Editer import Editer
import os
import shutil
def parse_args():
"""Parse input arguments."""
parser = argparse.ArgumentParser(description='config')
parser.add_argument('--comic_no', default='0000', type=str)
parser.add_argument('--volume_no', default='1', type=int)
parser.add_argument('--no_input', default=False, type=bool)
args = parser.parse_args()
return args
def query_chaps(comic_name, url_prev, is_gui=False, hang_signal=None, edit_line_hang=None):
print('未输入卷号,将返回书籍目录信息......')
editer = Downloader(comic_name=comic_name, root_path='./out', url_prev=url_prev)
print('*******************************')
editer.get_comic_msg(is_gui, hang_signal, edit_line_hang)
editer.get_comic_chaps()
print(editer.comic_title, editer.comic_author)
print('*******************************')
for i, chap_name in enumerate(editer.chap_name_list):
print(f'[{str(i+1)}]', chap_name)
print('*******************************')
print('请输入所需要的卷号进行下载(多卷可以用英文逗号分隔或直接使用连字符,详情见说明)')
def download_task(root_path,
comic_name,
chap_no_list,
url_prev,
high_quality,
is_gui=False,
multi_thread=False,
hang_signal=None,
progressring_signal=None,
cover_signal=None,
edit_line_hang=None):
downloader = Downloader(comic_name=comic_name, root_path=root_path, url_prev=url_prev, high_quality=high_quality)
print('正在积极地获取书籍信息....')
downloader.get_comic_msg(is_gui, hang_signal, edit_line_hang)
downloader.get_comic_chaps()
print(downloader.comic_title, downloader.comic_author)
print('****************************')
print('正在下载漫画....')
for chap_no in chap_no_list:
chap_name = downloader.chap_name_list[chap_no-1]
chap_uuid = downloader.chap_uuid_list[chap_no-1]
page_num = downloader.chap_pagenum_list[chap_no-1]
downloader.download_single_chap(chap_name, chap_uuid, page_num, multithread=multi_thread, is_gui=is_gui, signal=progressring_signal)
downloader.get_cover(chap_name=chap_name, is_gui=is_gui, signal=cover_signal)
downloader.download_cover()
print('漫画下载成功!', f'漫画路径【{downloader.comic_path}')
chap_list = [downloader.chap_name_list[chap_no-1] for chap_no in chap_no_list]
editer = Editer(downloader.comic_title, downloader.comic_author, chap_list, downloader.comic_path, root_path, delete_comic=0)
editer.pack_img()
editer.typesetting()
editer.get_epub()
def downloader_router(root_path,
comic_name,
chap_no,
url_prev,
high_quality,
is_gui=False,
multi_thread=False,
hang_signal=None,
progressring_signal=None,
cover_signal=None,
edit_line_hang=None):
if len(comic_name)==0:
print('请检查输入是否完整正确!')
return
elif chap_no == '':
query_chaps(comic_name, url_prev, is_gui, hang_signal, edit_line_hang)
return
elif chap_no.isdigit():
chap_no = int(chap_no)
chap_no_list = [chap_no]
if chap_no<=0:
print('请检查输入是否完整正确!')
return
elif "-" in chap_no:
start, end = map(str, chap_no.split("-"))
if start.isdigit() and end.isdigit() and int(start)>0 and int(start)<int(end):
chap_no_list = list(range(int(start), int(end) + 1))
else:
print('请检查输入是否完整正确!')
return
elif "," in chap_no:
chap_no_list = [num for num in chap_no.split(",")]
if all([num.isdigit() for num in chap_no_list]):
chap_no_list = [int(num) for num in chap_no_list]
else:
print('请检查输入是否完整正确!')
return
else:
print('请检查输入是否完整正确!')
return
download_task(root_path, comic_name, chap_no_list, url_prev, high_quality, is_gui, multi_thread, hang_signal, progressring_signal, cover_signal, edit_line_hang)
print('所有下载任务都已经完成!')
if __name__=='__main__':
args = parse_args()
download_path = os.path.join(os.path.expanduser('~'), 'Downloads')
if args.no_input:
downloader_router(root_path='out', comic_name=args.comic_no, chap_no=args.volume_no)
else:
while True:
args.comic_name = input('请输入书籍号:')
args.volume_no = input('请输入卷号(查看目录信息不输入直接按回车,下载多卷请使用逗号分隔或者连字符-)')
# args.comic_name = 'xinglingganying'
# args.volume_no = '40'
downloader_router(root_path='out', comic_name=args.comic_name, chap_no=args.volume_no, url_prev='.tv', high_quality=True, multi_thread=True)
# exit(0)

411
copymanga_gui.py Normal file
View File

@ -0,0 +1,411 @@
# coding:utf-8
from PyQt5.QtCore import Qt, pyqtSignal, QObject, QThread, QRegExp
from PyQt5.QtGui import QIcon, QFont, QTextCursor, QPixmap, QColor,QRegExpValidator
from PyQt5.QtWidgets import QApplication, QFrame, QGridLayout, QFileDialog
from qfluentwidgets import (setTheme, Theme, PushSettingCard, SettingCardGroup, ExpandLayout, TextEdit, ImageLabel, LineEdit, PushButton, Theme, ProgressRing, setTheme, Theme, OptionsSettingCard, OptionsConfigItem, OptionsValidator, FluentWindow, SubtitleLabel, NavigationItemPosition, setThemeColor, qconfig, ComboBox, SwitchSettingCard, BoolValidator, MessageBox)
from qfluentwidgets import FluentIcon as FIF
import sys
import base64
import shutil
from resource.logo import logo_base64
from resource.book import book_base64
from copymanga import *
from enum import Enum
font_label = QFont('微软雅黑', 18)
font_msg = QFont('微软雅黑', 11)
class MainThread(QThread):
def __init__(self, parent):
super().__init__()
self.parent = parent
def run(self):
self.parent.clear_signal.emit('')
try:
comic_no = self.parent.editline_book.text()
chap_no = self.parent.editline_volumn.text()
downloader_router(self.parent.parent.out_path, comic_no, chap_no, self.parent.parent.url_prev, self.parent.parent.high_quality, True, self.parent.parent.multi_thread, self.parent.hang_signal, self.parent.progressring_signal, self.parent.cover_signal, self.parent.editline_hang)
self.parent.end_signal.emit('')
except Exception as e:
self.parent.end_signal.emit('')
print('错误,请检查网络情况或确认输入是否正确')
print('错误信息:')
print(e)
def terminate(self) -> None:
result = super().terminate()
return result
class EmittingStr(QObject):
textWritten = pyqtSignal(str) # 定义一个发送str的信号
def write(self, text):
self.textWritten.emit(str(text))
def flush(self):
pass
def isatty(self):
pass
class UrlPrev(Enum):
""" Theme enumeration """
SITE = ".site"
COM= ".com"
ORG = ".org"
NET = ".net"
TV = ".tv"
INFO = ".info"
class SettingWidget(QFrame):
def __init__(self, text: str, parent=None):
super().__init__(parent=parent)
self.parent = parent
self.expandLayout = ExpandLayout(self)
self.setObjectName(text.replace(' ', '-'))
self.setting_group = SettingCardGroup(self.tr("下载设置"), self)
self.download_path_card = PushSettingCard(
self.tr('选择文件夹'),
FIF.DOWNLOAD,
self.tr("下载目录"),
self.parent.out_path,
self.setting_group
)
self.themeMode = OptionsConfigItem(None, "ThemeMode", Theme.DARK, OptionsValidator(Theme), None)
self.urlMode = OptionsConfigItem(None, "urlMode", UrlPrev.SITE, OptionsValidator(UrlPrev), None)
self.threadMode = OptionsConfigItem(None, "ThreadMode", True, BoolValidator())
self.qualityMode = OptionsConfigItem(None, "QualityMode", True, BoolValidator())
self.theme_card = OptionsSettingCard(
self.themeMode,
FIF.BRUSH,
self.tr('应用主题'),
self.tr("更改外观"),
texts=[
self.tr(''), self.tr(''),
self.tr('跟随系统设置')
],
parent=self.parent
)
self.url_card = OptionsSettingCard(
self.urlMode,
FIF.VPN,
self.tr('漫画域名后缀'),
self.tr("漫画域名切换"),
texts=[
self.tr('.tv'), self.tr('.org'),
self.tr('.com'), self.tr('.info'),
self.tr('.site'), self.tr('.net')
],
parent=self.parent
)
self.thread_card = SwitchSettingCard(
FIF.SPEED_HIGH,
self.tr('多线程缓存'),
self.tr('开启后理论上会加快下载速度,但可能会增加服务端压力'),
parent=self.parent,
configItem=self.threadMode
)
self.quality_card = SwitchSettingCard(
FIF.LEAF,
self.tr('下载高质量图片'),
self.tr('开启后会提高漫画清晰度,但会增加下载漫画的体积'),
parent=self.parent,
configItem=self.qualityMode
)
self.thread_card.setValue(True)
self.quality_card.setValue(True)
self.thread_changed()
self.quality_changed()
self.setting_group.addSettingCard(self.download_path_card)
self.setting_group.addSettingCard(self.thread_card)
self.setting_group.addSettingCard(self.quality_card)
self.setting_group.addSettingCard(self.url_card)
self.setting_group.addSettingCard(self.theme_card)
self.expandLayout.setSpacing(28)
self.expandLayout.setContentsMargins(20, 10, 20, 0)
self.expandLayout.addWidget(self.setting_group)
self.download_path_card.clicked.connect(self.download_path_changed)
self.theme_card.optionChanged.connect(self.theme_changed)
self.url_card.optionChanged.connect(self.url_changed)
self.thread_card.checkedChanged.connect(self.thread_changed)
self.quality_card.checkedChanged.connect(self.quality_changed)
def download_path_changed(self):
""" download folder card clicked slot """
self.parent.out_path = QFileDialog.getExistingDirectory(
self, self.tr("Choose folder"), self.parent.out_path)
self.download_path_card.contentLabel.setText(self.parent.out_path)
def theme_changed(self):
theme_name = self.theme_card.choiceLabel.text()
self.parent.set_theme(theme_name)
if os.path.exists('./config'):
shutil.rmtree('./config')
def url_changed(self):
self.parent.url_prev = self.url_card.choiceLabel.text()
if os.path.exists('./config'):
shutil.rmtree('./config')
def thread_changed(self):
is_checked = self.thread_card.isChecked()
self.thread_card.switchButton.setText(
self.tr('') if is_checked else self.tr(''))
self.parent.multi_thread = is_checked
if os.path.exists('./config'):
shutil.rmtree('./config')
def quality_changed(self):
is_checked = self.quality_card.isChecked()
self.quality_card.switchButton.setText(
self.tr('') if is_checked else self.tr(''))
self.parent.high_quality = is_checked
if os.path.exists('./config'):
shutil.rmtree('./config')
class HomeWidget(QFrame):
progressring_signal = pyqtSignal(object)
end_signal = pyqtSignal(object)
hang_signal = pyqtSignal(object)
clear_signal = pyqtSignal(object)
cover_signal = pyqtSignal(object)
def __init__(self, text: str, parent=None):
super().__init__(parent=parent)
self.setObjectName(text)
self.parent = parent
self.label_book = SubtitleLabel('名称:', self)
self.label_volumn = SubtitleLabel('卷号:', self)
self.editline_book = LineEdit(self)
self.editline_volumn = LineEdit(self)
# self.editline_book.setText('yaoyeluying')
# self.editline_volumn.setText('3')
self.book_icon = QPixmap()
self.book_icon.loadFromData(base64.b64decode(book_base64))
self.cover_w, self.cover_h = 152, 230
self.label_cover = ImageLabel(self.book_icon, self)
self.label_cover.setBorderRadius(8, 8, 8, 8)
self.label_cover.setFixedSize(self.cover_w, self.cover_h)
self.text_screen = TextEdit()
self.text_screen.setReadOnly(True)
self.text_screen.setFixedHeight(self.cover_h)
self.progressRing = ProgressRing(self)
self.progressRing.setValue(0)
self.progressRing.setTextVisible(True)
self.progressRing.setFixedSize(50, 50)
self.btn_run = PushButton('确定', self)
self.btn_run.setShortcut(Qt.Key_Return)
self.btn_stop = PushButton('取消', self)
self.btn_hang = PushButton('确定', self)
self.editline_hang = ComboBox(self)
self.gridLayout = QGridLayout(self)
self.screen_layout = QGridLayout()
self.btn_layout = QGridLayout()
self.hang_layout = QGridLayout()
self.label_book.setFont(font_label)
self.label_volumn.setFont(font_label)
self.editline_book.setFont(font_label)
self.editline_volumn.setFont(font_label)
self.text_screen.setFont(font_msg)
self.editline_hang.setFont(font_msg)
self.gridLayout.addWidget(self.editline_book, 0, 1)
self.gridLayout.addWidget(self.editline_volumn, 1, 1)
self.gridLayout.addWidget(self.label_book, 0, 0)
self.gridLayout.addWidget(self.label_volumn, 1, 0)
self.gridLayout.addLayout(self.btn_layout, 2, 1, 1, 1)
self.btn_layout.addWidget(self.btn_run, 2, 1)
self.btn_layout.addWidget(self.btn_stop, 2, 2)
self.gridLayout.addLayout(self.screen_layout, 3, 0, 2, 2)
self.screen_layout.addWidget(self.progressRing, 0, 0, Qt.AlignRight|Qt.AlignBottom)
self.screen_layout.addWidget(self.text_screen, 0, 0)
self.screen_layout.addWidget(self.label_cover, 0, 1)
self.gridLayout.addLayout(self.hang_layout, 5, 0, 1, 2)
self.hang_layout.addWidget(self.editline_hang, 0, 0)
self.hang_layout.addWidget(self.btn_hang, 0, 1)
self.screen_layout.setContentsMargins(0,0,0,0)
self.btn_layout.setContentsMargins(0,0,0,0)
self.gridLayout.setContentsMargins(20, 10, 20, 10)
self.btn_run.clicked.connect(self.process_start)
self.btn_stop.clicked.connect(self.process_stop)
self.btn_hang.clicked.connect(self.process_continue)
self.progressring_signal.connect(self.progressring_msg)
self.end_signal.connect(self.process_end)
self.hang_signal.connect(self.process_hang)
self.clear_signal.connect(self.clear_screen)
self.cover_signal.connect(self.display_cover)
self.progressRing.hide()
self.btn_hang.hide()
self.editline_hang.hide()
self.btn_stop.setEnabled(False)
sys.stdout = EmittingStr(textWritten=self.outputWritten)
sys.stderr = EmittingStr(textWritten=self.outputWritten)
self.text_screen.setText(self.parent.welcome_text)
def process_start(self):
self.label_cover.setImage(self.book_icon)
self.label_cover.setFixedSize(self.cover_w, self.cover_h)
self.btn_run.setEnabled(False)
self.btn_run.setText('正在下载')
self.btn_stop.setEnabled(True)
self.main_thread = MainThread(self)
self.main_thread.start()
def process_end(self, input=None):
self.btn_run.setEnabled(True)
self.btn_run.setText('开始下载')
self.btn_run.setShortcut(Qt.Key_Return)
self.btn_stop.setEnabled(False)
self.progressRing.hide()
self.btn_hang.hide()
self.editline_hang.clear()
self.editline_hang.hide()
if input=='refresh':
self.label_cover.setImage(self.book_icon)
self.label_cover.setFixedSize(self.cover_w, self.cover_h)
self.clear_signal.emit('')
self.text_screen.setText(self.parent.welcome_text)
def outputWritten(self, text):
cursor = self.text_screen.textCursor()
scrollbar=self.text_screen.verticalScrollBar()
is_bottom = (scrollbar.value()>=scrollbar.maximum() - 15)
cursor.movePosition(QTextCursor.End)
cursor.insertText(text)
if is_bottom:
self.text_screen.setTextCursor(cursor)
# self.text_screen.ensureCursorVisible()
def clear_screen(self):
self.text_screen.clear()
def display_cover(self, signal_msg):
filepath, img_h, img_w = signal_msg
self.label_cover.setImage(filepath)
self.label_cover.setFixedSize(int(img_w*self.cover_h/img_h), self.cover_h)
def progressring_msg(self, input):
if input == 'start':
self.progressRing.setValue(0)
self.progressRing.show()
elif input == 'end':
self.progressRing.hide()
self.progressRing.setValue(0)
else:
self.progressRing.setValue(input)
def process_hang(self, input=None):
self.btn_hang.setEnabled(True)
self.btn_hang.setShortcut(Qt.Key_Return)
self.btn_hang.show()
self.editline_hang.show()
def process_continue(self, input=None):
self.btn_hang.hide()
self.btn_hang.setEnabled(False)
self.editline_hang.hide()
def process_stop(self):
self.main_thread.terminate()
self.end_signal.emit('refresh')
class Window(FluentWindow):
update_signal = pyqtSignal(object)
def __init__(self):
super().__init__()
self.out_path = os.path.join(os.path.expanduser('~'), 'Downloads')
self.url_prev = '.tv'
self.head = 'https://www.copymanga.tv'
split_str = '**************************************\n '
self.welcome_text = f'使用说明共4条记得下拉\n{split_str}1.拷贝漫画{self.head},根据书籍网址输入漫画名以及下载的卷号。\n{split_str}2.例如漫画网址是{self.head}/comic/yaoyeluying则漫画名输入yaoyeluying。\n{split_str}3.要查询漫画卷号卷名等信息,则可以只输入漫画名不输入卷号,点击确定会返回漫画卷名称和对应的卷号。\n{split_str}4.根据上一步返回的信息确定自己想下载的卷号,要下载编号[2]对应卷则卷号输入2。想下载多卷比如[1]至[3]对应卷则卷号输入1-3或1,2,3英文逗号分隔编号也可以不连续并点击确定。'
self.homeInterface = HomeWidget('Home Interface', self)
self.settingInterface = SettingWidget('Setting Interface', self)
self.initNavigation()
self.initWindow()
self.multi_thread = True
self.high_quality = True
def initNavigation(self):
self.addSubInterface(self.homeInterface, FIF.HOME, '主界面')
self.addSubInterface(self.settingInterface, FIF.SETTING, '设置', NavigationItemPosition.BOTTOM)
def initWindow(self):
self.resize(700, 460)
pixmap = QPixmap()
pixmap.loadFromData(base64.b64decode(logo_base64))
self.setWindowIcon(QIcon(pixmap))
self.setWindowTitle('拷贝漫画下载器')
self.setFont(font_label)
desktop = QApplication.desktop().availableGeometry()
w, h = desktop.width(), desktop.height()
self.move(w//2 - self.width()//2, h//2 - self.height()//2)
def set_theme(self, mode=None):
if mode=='':
setTheme(Theme.LIGHT)
elif mode=='':
setTheme(Theme.DARK)
elif mode=='跟随系统设置':
setTheme(Theme.AUTO)
theme = qconfig.theme
if theme == Theme.DARK:
self.homeInterface.label_book.setTextColor(QColor(255,255,255))
self.homeInterface.label_volumn.setTextColor(QColor(255,255,255))
elif theme == Theme.LIGHT:
self.homeInterface.label_book.setTextColor(QColor(0,0,0))
self.homeInterface.label_volumn.setTextColor(QColor(0,0,0))
if __name__ == '__main__':
QApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
setTheme(Theme.DARK)
setThemeColor('#1E90FF')
app = QApplication(sys.argv)
w = Window()
w.show()
app.exec_()

6
requirements.txt Normal file
View File

@ -0,0 +1,6 @@
# pip install -r requirements.txt -i https://pypi.org/simple/
requests
bs4
rich
pyqt5
PyQt-Fluent-Widgets[full]

Binary file not shown.

Binary file not shown.

BIN
resource/book.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

1
resource/book.py Normal file
View File

@ -0,0 +1 @@
book_base64 = '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'

BIN
resource/example1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

BIN
resource/example2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

BIN
resource/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

1
resource/logo.py Normal file

File diff suppressed because one or more lines are too long

23
resource/trans_base64.py Normal file
View File

@ -0,0 +1,23 @@
from PIL import Image
import base64
# from resource.logo import logo_base64
import io
# # 从Base64编码数据中获取图像数据
# image_bytes = base64.b64decode(logo_base64)
# # 将图像数据解码为Image对象
# image = Image.open(io.BytesIO(image_bytes))
# # 显示图像
# image.show()
def image_to_base64(image_path):
with open(image_path, "rb") as image_file:
encoded_string = base64.b64encode(image_file.read())
return encoded_string.decode("utf-8")
image_path = "resource/logo.png "
base64_string = image_to_base64(image_path)
print(base64_string)

197
utils.py Normal file
View File

@ -0,0 +1,197 @@
def get_cover_html(img_w, img_h):
img_htmls = []
img_msg = ' <image width=\"'+ str(img_w)+'\" height=\"'+ str(img_h)+'\" xlink:href="../Images/000_0000.jpg"/>\n'
img_htmls.append('<?xml version="1.0" encoding="UTF-8" standalone="no" ?>\n')
img_htmls.append('<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n')
img_htmls.append('\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n')
img_htmls.append('<html xmlns=\"http://www.w3.org/1999/xhtml\">\n')
img_htmls.append('<head>\n')
img_htmls.append(' <title>Cover</title>\n')
img_htmls.append('</head>\n')
img_htmls.append('<body>\n')
img_htmls.append(' <div style="text-align: center; padding: 0pt; margin: 0pt;">\n')
img_htmls.append(' <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"100%\" preserveAspectRatio=\"xMidYMid meet\" version=\"1.1\" viewBox=\"0 0 '+ str(img_w)+' '+ str(img_h)+'\" width=\"100%\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n')
img_htmls.append(img_msg)
img_htmls.append(' </svg>\n')
img_htmls.append(' </div>\n')
img_htmls.append('</body>\n')
img_htmls.append('</html>')
return img_htmls
def get_xhtml(img):
text_body = []
text_body.append('<body>\n')
text_body.append(' <img alt=\"'+ img.replace('.jpg', '') +'\" src=\"../Images/'+ img +'\"/>\n')
text_body.append('</body>\n')
text_head = []
text_head.append('<head>\n')
text_head.append(' <title></title>\n')
text_head.append('</head>\n')
text_htmls = ['<?xml version="1.0" encoding="utf-8"?>\n', '<html>\n'] + text_head + text_body + ['</html>']
return text_htmls
def get_toc_html(title, chap_names, chap_imgs):
toc_htmls = []
toc_htmls.append('<?xml version=\"1.0\" encoding=\"utf-8\"?>\n')
toc_htmls.append('<!DOCTYPE ncx PUBLIC \"-//NISO//DTD ncx 2005-1//EN\"\n')
toc_htmls.append(' \"http://www.daisy.org/z3986/2005/ncx-2005-1.dtd\">\n\n')
toc_htmls.append('<ncx xmlns=\"http://www.daisy.org/z3986/2005/ncx/\" version=\"2005-1\">\n')
toc_htmls.append(' <head>\n')
toc_htmls.append(' <meta name=\"dtb:uid\" content=\"urn:uuid:a18aac05-497d-476d-b66f-0211f609743d\" />\n')
toc_htmls.append(' <meta name=\"dtb:depth\" content=\"0\" />\n')
toc_htmls.append(' <meta name=\"dtb:totalPageCount\" content=\"0\" />\n')
toc_htmls.append(' <meta name=\"dtb:maxPageNumber\" content=\"0\" />\n')
toc_htmls.append(' </head>\n')
toc_htmls.append('<docTitle>\n')
toc_htmls.append(' <text>'+ title +'</text>\n')
toc_htmls.append('</docTitle>\n')
toc_htmls.append('<navMap>\n')
for chap_no, (chap_name, chap_img) in enumerate(zip(chap_names, chap_imgs)):
toc_htmls.append(' <navPoint id=\"navPoint-'+str(chap_no+1)+'\" playOrder=\"'+str(chap_no+1)+'\">\n')
toc_htmls.append(' <navLabel>\n')
toc_htmls.append(' <text>'+ chap_name +'</text>\n')
toc_htmls.append(' </navLabel>\n')
toc_htmls.append(' <content src="Text/'+chap_img.replace('.jpg', '.xhtml')+'"/>\n')
toc_htmls.append(' </navPoint>\n')
toc_htmls.append('</navMap>\n')
toc_htmls.append('</ncx>')
return toc_htmls
def get_content_html(title, author, img_list):
content_htmls = []
content_htmls.append('<?xml version=\"1.0\" encoding=\"utf-8\"?>\n')
content_htmls.append('<package version=\"2.0\" unique-identifier=\"BookId\" xmlns=\"http://www.idpf.org/2007/opf\">\n')
content_htmls.append(' <metadata xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:opf=\"http://www.idpf.org/2007/opf\">\n')
content_htmls.append(' <dc:identifier id=\"BookId\" opf:scheme=\"UUID\">urn:uuid:942b8224-476b-463b-9078-cdfab0ee2686</dc:identifier>\n')
content_htmls.append(' <dc:language>zh</dc:language>\n')
content_htmls.append(' <dc:title>'+ title +'</dc:title>\n')
content_htmls.append(' <dc:creator opf:role="aut" opf:file-as="未知">'+ author +'</dc:creator>\n')
content_htmls.append(' <meta name=\"cover\" content=\"x000_0000.jpg\"/>\n')
content_htmls.append(' </metadata>\n')
content_htmls.append(' <manifest>\n')
content_htmls.append(' <item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml"/>\n')
for img in img_list:
text = img.replace('.jpg', '.xhtml')
content_htmls.append(' <item id=\"x'+ text +'\" href=\"Text/'+ text +'\" media-type=\"application/xhtml+xml\"/>\n')
content_htmls.append(' <item id="cover.xhtml" href="Text/cover.xhtml" media-type="application/xhtml+xml"/>\n')
for img in img_list:
content_htmls.append(' <item id=\"x'+ img + '\" href=\"Images/'+ img +'\" media-type=\"image/jpeg\"/>\n')
content_htmls.append(' <item id=\"x000_0000.jpg\" href=\"Images/000_0000.jpg\" media-type=\"image/jpeg\"/>\n')
content_htmls.append(' </manifest>\n')
content_htmls.append(' <spine toc="ncx">\n')
content_htmls.append(' <itemref idref="xcolor"/>\n')
for img in img_list:
text = img.replace('.jpg', '.xhtml')
content_htmls.append(' <itemref idref=\"x'+ text + '\"/>\n')
content_htmls.append(' <itemref idref="cover.xhtml"/>\n')
content_htmls.append(' </spine>\n')
content_htmls.append(' <guide>\n')
content_htmls.append(' <reference type="cover" title="封面" href="Text/cover.xhtml"/>\n')
content_htmls.append(' </guide>\n')
content_htmls.append('</package>\n')
return content_htmls
def get_container_html():
container_htmls = []
container_htmls.append('<?xml version="1.0" encoding="UTF-8"?>\n')
container_htmls.append('<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">\n')
container_htmls.append(' <rootfiles>\n')
container_htmls.append(' <rootfile full-path="OEBPS/content.opf" media-type="application/oebps-package+xml"/>\n')
container_htmls.append(' </rootfiles>\n')
container_htmls.append('</container>\n')
return container_htmls
def get_color_html(colorimg_num):
color_htmls = []
color_htmls.append('<?xml version=\"1.0\" encoding=\"utf-8\"?>\n')
color_htmls.append('<html>\n')
color_htmls.append('<head>\n')
color_htmls.append(' <title>彩插</title>\n')
color_htmls.append('</head>\n')
color_htmls.append('<body>\n')
for i in range(1, colorimg_num):
color_htmls.append(' <img alt=\"'+str(i).zfill(2)+'\" src=\"../Images/'+str(i).zfill(2)+'.jpg\"/>\n')
color_htmls.append('</body>\n')
color_htmls.append('</html>')
return color_htmls
def get_vol(vol_no):
vol_no = str(vol_no)
s="零一二三四五六七八九"
for c in "0123456789":
vol_no=vol_no.replace(c,s[eval(c)])
vol_no = '' + vol_no + ''
return vol_no
def check_chars(win_chars):
win_illegal_chars = '?*"<>|:/\\'
new_chars = ''
for char in win_chars:
if char in win_illegal_chars:
new_chars += '\u25A0'
else:
new_chars += char
return new_chars