commit e5a38feb297094fe5c785abc200a7615ad2cd759 Author: 王老板 Date: Fri Sep 27 17:06:15 2024 +0800 update diff --git a/Editer.py b/Editer.py new file mode 100644 index 0000000..e00a231 --- /dev/null +++ b/Editer.py @@ -0,0 +1,394 @@ +#!/usr/bin/python +# -*- coding:utf-8 -*- + +import requests # 用来抓取网页的html源码 +from bs4 import BeautifulSoup # 用于代替正则式 取源码中相应标签中的内容 +import time # 时间相关操作 +import os +from rich.progress import track as tqdm +from utils import * +import zipfile +import shutil +import re +import pickle +from PIL import Image +import time +import threading +from concurrent.futures import ThreadPoolExecutor, wait +import pickle +from selenium import webdriver +from selenium.webdriver.edge.options import Options + +lock = threading.RLock() + +class Editer(object): + def __init__(self, root_path, head='https://www.linovelib.com', book_no='0000', volume_no=1): + + 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', 'referer': head, 'cookie':'night=1'} + + self.url_head = head + options = Options() + options.add_argument('--start-minimized') + + self.driver = webdriver.Edge(options = options) + self.main_page = f'{self.url_head}/novel/{book_no}.html' + self.cata_page = f'{self.url_head}/novel/{book_no}/catalog' + self.read_tool_page = f'{self.url_head}/themes/zhmb/js/readtool.js' + self.color_chap_name = '插图' + self.color_page_name = '彩页' + self.html_buffer = dict() + + main_html = self.get_html(self.main_page) + bf = BeautifulSoup(main_html, 'html.parser') + self.title = bf.find('meta', {"property": "og:novel:book_name"})['content'] + self.author = bf.find('meta', {"property": "og:novel:author"})['content'] + try: + self.cover_url = re.search(r'src=\"(.*?)\"', str(bf.find('div', {"class": "book-img fl"}))).group(1) + except: + self.cover_url = 'cid' + + self.img_url_map = dict() + self.volume_no = volume_no + + self.epub_path = root_path + self.temp_path = os.path.join(self.epub_path, 'temp_'+ check_chars(self.title) + '_' + str(self.volume_no)) + + self.missing_last_chap_list = [] + self.is_color_page = True + self.page_url_map = dict() + self.ignore_urls = [] + self.url_buffer = [] + self.max_thread_num = 8 + self.pool = ThreadPoolExecutor(self.max_thread_num) + + # 获取html文档内容 + def get_html(self, url, is_gbk=False): + while True: + time.sleep(0.5) + self.driver.get(url) + req = self.driver.page_source + while 'Access denied | www.linovelib.com used Cloudflare to restrict access' in req: + time.sleep(5) + self.driver.get(url) + req = self.driver.page_source + if is_gbk: + req.encoding = 'GBK' #这里是网页的编码转换,根据网页的实际需要进行修改,经测试这个编码没有问题 + break + return req + + def get_html_img(self, url): + time.sleep(0.5) + req=requests.get(url, headers=self.header) + num_retry = 0 + while 'Forbidden' in str(req.content[0:100]) and num_retry<10: + req=requests.get(url, headers=self.header) + time.sleep(0.5) + num_retry += 1 + if 'Forbidden' in str(req.content[0:100]): + print('插图下载失败,链接:' + url) + return req.content + + def make_folder(self): + os.makedirs(self.temp_path, exist_ok=True) + + self.text_path = os.path.join(self.temp_path, 'OEBPS/Text') + os.makedirs(self.text_path, exist_ok=True) + + self.img_path = os.path.join(self.temp_path, 'OEBPS/Images') + os.makedirs(self.img_path, exist_ok=True) + + def get_index_url(self): + self.volume = {} + self.volume['chap_urls'] = [] + self.volume['chap_names'] = [] + chap_html_list = self.get_chap_list(is_print=False) + if len(chap_html_list)", text_html) + for img_urlre in img_urlre_list: + img_url_full = re.search(r'.[a-zA-Z]{3}/(.*?).(jpg|png|jpeg)', img_urlre) + img_url_name = img_url_full.group(1) + img_url_tail = img_url_full.group(0).split('.')[-1] + img_url = f'https://img3.readpai.com/{img_url_name}.{img_url_tail}' + + text_html = text_html.replace('
\n' + img_urlre +'\n
', img_urlre) + if not img_url in self.img_url_map: + self.img_url_map[img_url] = str(len(self.img_url_map)).zfill(2) + img_symbol = f'

[img:{self.img_url_map[img_url]}]

' + if '00' in img_symbol: + text_html = text_html.replace(img_urlre, '') #默认第一张为封面图片 不写入彩页 + else: + text_html = text_html.replace(img_urlre, img_symbol) + symbol_index = text_html.index(img_symbol) + if text_html[symbol_index-1] != '\n': + text_html = text_html[:symbol_index] + '\n' + text_html[symbol_index:] + text = BeautifulSoup(text_html, 'html.parser').get_text() + return text + + def get_chap_text(self, url, chap_name, return_next_chapter=False): + text_chap = '' + page_no = 1 + url_ori = url + next_chap_url = None + while True: + if page_no == 1: + str_out = chap_name + else: + str_out = f' 正在下载第{page_no}页......' + print(str_out) + content_html = self.get_html(url, is_gbk=False) + text = self.get_page_text(content_html) + text_chap += text + url_new = url_ori.replace('.html', '_{}.html'.format(page_no+1))[len(self.url_head):] + if url_new in content_html: + page_no += 1 + url = self.url_head + url_new + else: + if return_next_chapter: + next_chap_url = self.url_head + re.search(r'书签下一章', content_html).group(1) + break + return text_chap, next_chap_url + + def get_text(self): + self.make_folder() + img_strs = [] #记录后文中出现的所有图片位置 + text_no=0 #text_no正文章节编号(排除插图) chap_no 是所有章节编号 + for chap_no, (chap_name, chap_url) in enumerate(zip(self.volume['chap_names'], self.volume['chap_urls'])): + is_fix_next_chap_url = (chap_name in self.missing_last_chap_list) + text, next_chap_url = self.get_chap_text(chap_url, chap_name, return_next_chapter=is_fix_next_chap_url) + if is_fix_next_chap_url: + self.volume['chap_urls'][chap_no+1] = next_chap_url #正向修复 + if chap_name == self.color_chap_name: + text_html_color = text2htmls(self.color_page_name, text) + else: + text_html = text2htmls(chap_name, text) + textfile = self.text_path + f'/{str(text_no).zfill(2)}.xhtml' + with open(textfile, 'w+', encoding='utf-8') as f: + f.writelines(text_html) + for text_line in text_html: + img_str = re.search(r"", text_line) + if img_str is not None: + img_strs.append(img_str.group(0)) + text_no += 1 + + # 将彩页中后文已经出现的图片删除,避免重复 + if self.is_color_page: #判断彩页是否存在 + text_html_color_new = [] + textfile = self.text_path + '/color.xhtml' + for text_line in text_html_color: + is_save = True + for img_str in img_strs: + if img_str in text_line: + is_save = False + break + if is_save: + text_html_color_new.append(text_line) + + with open(textfile, 'w+', encoding='utf-8') as f: + f.writelines(text_html_color_new) + + + def get_image(self, is_gui=False, signal=None): + 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) + with open(self.img_path+f'/{img_name}.jpg', 'wb') as f: + f.write(content) #写入二进制内容 + signal.emit(int(100*(i+1)/len_iter)) + signal.emit('end') + else: + for img_url, img_name in tqdm(self.img_url_map.items()): + content = self.get_html_img(img_url) + with open(self.img_path+f'/{img_name}.jpg', 'wb') as f: + f.write(content) #写入二进制内容 + + def get_cover(self, is_gui=False, signal=None): + textfile = os.path.join(self.text_path, 'cover.xhtml') + img_w, img_h = 300, 300 + try: + imgfile = os.path.join(self.img_path, '00.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) + except Exception as e: + print(e) + print('没有封面图片,请自行用第三方EPUB编辑器手动添加封面') + img_htmls = get_cover_html(img_w, img_h) + with open(textfile, 'w+', encoding='utf-8') as f: + f.writelines(img_htmls) + + def check_volume(self, is_gui=False, signal=None, editline=None): + chap_names = self.volume['chap_names'] + chap_num = len(self.volume['chap_names']) + for chap_no, url in enumerate(self.volume['chap_urls']): + if self.check_url(url): + if not self.prev_fix_url(chap_no, chap_num): #先尝试反向递归修复 + if chap_no == 0: #第一个章节都反向修复失败 说明后面章节全部缺失,只能手动输入第一个章节,保证第一个章节一定有效 + self.volume['chap_urls'][0] = self.hand_in_url(chap_names[chap_no], is_gui, signal, editline) + else: #其余章节反向修复失败 默认使用正向修复 + self.missing_last_chap_list.append(chap_names[chap_no-1]) + + #没有检测到插图页,手动输入插图页标题 + if self.color_chap_name not in self.volume['chap_names']: + self.color_chap_name = self.hand_in_color_page_name(is_gui, signal, editline) + self.volume['color_chap_name'] = self.color_chap_name + + #没有彩页 但主页封面存在,将主页封面设为书籍封面 + if self.color_chap_name=='' and (not self.check_url(self.cover_url)): + self.is_color_page = False + self.img_url_map[self.cover_url] = str(len(self.img_url_map)).zfill(2) + print('**************') + print('提示:没有彩页,但主页封面存在,将使用主页的封面图片作为本卷图书封面') + print('**************') + + def check_url(self, url):#当检测有问题返回True + return ('javascript' in url or 'cid' in url) + + def get_prev_url(self, chap_no): #获取前一个章节的链接 + content_html = self.get_html(self.volume['chap_urls'][chap_no], is_gbk=False) + next_url = self.url_head + re.search(r'
上一章', content_html).group(1) + return next_url + + def prev_fix_url(self, chap_no, chap_num): #反向递归修复缺失链接(后修复前),若成功修复返回True,否则返回False + if chap_no==chap_num-1: #最后一个章节直接选择不修复 返回False + return False + elif self.check_url(self.volume['chap_urls'][chap_no+1]): + if self.prev_fix_url(chap_no+1, chap_num): + self.volume['chap_urls'][chap_no] = self.get_prev_url(chap_no+1) + return True + else: + return False + else: + self.volume['chap_urls'][chap_no] = self.get_prev_url(chap_no+1) + return True + + def hand_in_msg(self, error_msg='', is_gui=False, signal=None, editline=None): + if is_gui: + print(error_msg) + signal.emit('hang') + time.sleep(1) + while not editline.isHidden(): + time.sleep(1) + content = editline.text() + editline.clear() + else: + content = input(error_msg) + return content + + def hand_in_url(self, chap_name, is_gui=False, signal=None, editline=None): + error_msg = f'章节\"{chap_name}\"连接失效,请手动输入该章节链接(手机版“{self.url_head}”开头的链接):' + return self.hand_in_msg(error_msg, is_gui, signal, editline) + + def hand_in_color_page_name(self, is_gui=False, signal=None, editline=None): + if is_gui: + error_msg = f'插图页面不存在,需要下拉选择插图页标题,若不需要插图页则保持本栏为空直接点确定:' + editline.addItems(self.volume['chap_names']) + editline.setCurrentIndex(-1) + else: + error_msg = f'插图页面不存在,需要手动输入插图页标题,若不需要插图页则不输入直接回车:' + return self.hand_in_msg(error_msg, is_gui, signal, editline) + + def get_toc(self): + if self.is_color_page: + ind = self.volume["chap_names"].index(self.color_chap_name) + self.volume["chap_names"].pop(ind) + toc_htmls = get_toc_html(self.title, self.volume["chap_names"]) + textfile = self.temp_path + '/OEBPS/toc.ncx' + with open(textfile, 'w+', encoding='utf-8') as f: + f.writelines(toc_htmls) + + def get_content(self): + num_chap = len(self.volume["chap_names"]) + num_img = len(os.listdir(self.img_path)) + content_htmls = get_content_html(self.title + '-' + self.volume['book_name'], self.author, num_chap, num_img, self.is_color_page) + textfile = self.temp_path + '/OEBPS/content.opf' + with open(textfile, 'w+', encoding='utf-8') as f: + f.writelines(content_htmls) + + def get_epub_head(self): + mimetype = 'application/epub+zip' + mimetypefile = self.temp_path + '/mimetype' + with open(mimetypefile, 'w+', encoding='utf-8') as f: + f.write(mimetype) + metainf_folder = os.path.join(self.temp_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): + os.remove(os.path.join(self.temp_path, 'buffer.pkl')) + epub_file = self.epub_path + '/' + check_chars(self.title + '-' + self.volume['book_name']) + '.epub' + with zipfile.ZipFile(epub_file, "w", zipfile.ZIP_DEFLATED) as zf: + for dirpath, _, filenames in os.walk(self.temp_path): + fpath = dirpath.replace(self.temp_path,'') #这一句很重要,不replace的话,就从根目录开始复制 + fpath = fpath and fpath + os.sep or '' + for filename in filenames: + zf.write(os.path.join(dirpath, filename), fpath+filename) + shutil.rmtree(self.temp_path) + return epub_file + + # # 恢复函数,根据secret_map进行恢复 + # def restore_chars(self, text): + # restored_text = "" + # i = 0 + # while i < len(text): + # char = text[i] + # if char in self.secret_map: + # restored_text += self.secret_map[char] + # else: + # restored_text += char + # i += 1 + # return restored_text + + def buffer(self): + filename = 'buffer.pkl' + filepath = os.path.join(self.temp_path, filename) + if os.path.isfile(filepath): + with open(filepath, 'rb') as f: + self.volume, self.img_url_map = pickle.load(f) + self.text_path = os.path.join(self.temp_path, 'OEBPS/Text') + os.makedirs(self.text_path, exist_ok=True) + self.img_path = os.path.join(self.temp_path, 'OEBPS/Images') + os.makedirs(self.img_path, exist_ok=True) + self.color_chap_name = self.volume['color_chap_name'] + else: + with open(filepath, 'wb') as f: + pickle.dump((self.volume ,self.img_url_map), f) + + def is_buffer(self): + filename = 'buffer.pkl' + filepath = os.path.join(self.temp_path, filename) + return os.path.isfile(filepath) \ No newline at end of file diff --git a/Editer2.py b/Editer2.py new file mode 100644 index 0000000..d3411ef --- /dev/null +++ b/Editer2.py @@ -0,0 +1,449 @@ +#!/usr/bin/python +# -*- coding:utf-8 -*- + +import requests # 用来抓取网页的html源码 +from bs4 import BeautifulSoup # 用于代替正则式 取源码中相应标签中的内容 +import time # 时间相关操作 +import os +from rich.progress import track as tqdm +from utils import * +import zipfile +import shutil +import re +import pickle +from PIL import Image +import time +import threading +from concurrent.futures import ThreadPoolExecutor, wait +import pickle +# from selenium import webdriver +# from selenium.webdriver.edge.options import Option + +lock = threading.RLock() + +class Editer(object): + def __init__(self, root_path, book_no='0000', volume_no=1): + + + self.url_head = 'https://www.linovelib.com' + self.url_head_mobile = 'https://www.bilinovel.com' + + 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', 'referer': self.url_head, 'Accept-Language': 'zh-CN,zh;q=0.9',} + self.header_mobile = {'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1', 'referer': self.url_head_mobile, 'Accept-Language': 'zh-CN,zh;q=0.9', 'cookie':'night=1'} + + + # options = Options() + # self.driver = webdriver.Edge(options = options) + self.main_page = f'{self.url_head}/novel/{book_no}.html' + self.cata_page = f'{self.url_head}/novel/{book_no}/catalog' + self.read_tool_page = f'{self.url_head}/themes/zhmb/js/readtool.js' + self.color_chap_name = '插图' + self.color_page_name = '彩页' + self.html_buffer = dict() + self.get_secret_map() + + main_html = self.get_html(self.main_page) + bf = BeautifulSoup(main_html, 'html.parser') + self.title = bf.find('meta', {"property": "og:novel:book_name"})['content'] + self.author = bf.find('meta', {"property": "og:novel:author"})['content'] + try: + self.cover_url = re.search(r'src=\"(.*?)\"', str(bf.find('div', {"class": "book-img fl"}))).group(1) + except: + self.cover_url = 'cid' + + self.img_url_map = dict() + self.volume_no = volume_no + + self.epub_path = root_path + self.temp_path = (os.path.join(self.epub_path, 'temp_'+ check_chars(self.title) + '_' + str(self.volume_no))) + + self.missing_last_chap_list = [] + self.is_color_page = True + self.page_url_map = dict() + self.ignore_urls = [] + self.url_buffer = [] + self.max_thread_num = 8 + self.pool = ThreadPoolExecutor(self.max_thread_num) + + + # 获取html文档内容 + def get_html(self, url, is_gbk=False, use_mobile=False): + if use_mobile: + header = self.header_mobile + else: + header = self.header + while True: + time.sleep(0.5) + # self.driver.get(url) + # req = self.driver.page_source + req = requests.get(url, headers=header) + while 'used Cloudflare to restrict access' in req.text: + time.sleep(5) + # self.driver.get(url) + # req = self.driver.page_source + req = requests.get(url, headers=header) + if is_gbk: + req.encoding = 'GBK' #这里是网页的编码转换,根据网页的实际需要进行修改,经测试这个编码没有问题 + break + return req.text + + def get_html_content(self, url, is_buffer=False, use_mobile=False): + if use_mobile: + header = self.header_mobile + else: + header = self.header + if is_buffer: + while not url in self.html_buffer.keys(): + time.sleep(0.1) + if url in self.html_buffer.keys(): + return self.html_buffer[url] + while True: + try: + req=requests.get(url, headers=header) + break + except Exception as e: + pass + lock.acquire() + self.html_buffer[url] = req.content + lock.release() + return req.content + + def get_secret_map(self): + with open('secret_map.cfg', 'rb') as f: + self.secret_map = pickle.load(f) + + + def get_secret_map(self): + url = 'https://gitee.com/bosswangs/bili-novel-map/releases/tag/secret_map' + html = self.get_html(url, is_gbk=True) + # print(html) + url_download = 'https://gitee.com' + re.search(r'{\"download_url\":\"(.*?).cfg', html).group(1)+'.cfg' + version = re.search(r'_v(.*?).cfg', url_download).group(1) + path = 'secret_map.cfg' + if os.path.exists(path): + with open(path, 'rb') as f: + map = pickle.load(f) + version_exist = map[1] + if version == version_exist: + self.secret_map = map[0] + return + + content = self.get_html_content(url_download) + if content is not None: + with open(path, 'wb') as f: + f.write(content) #写入二进制内容 + with open(path, 'rb') as f: + map = pickle.load(f) + self.secret_map = map[0] + return + + def make_folder(self): + os.makedirs(self.temp_path, exist_ok=True) + + self.text_path = os.path.join(self.temp_path, 'OEBPS/Text') + os.makedirs(self.text_path, exist_ok=True) + + self.img_path = os.path.join(self.temp_path, 'OEBPS/Images') + os.makedirs(self.img_path, exist_ok=True) + + def get_index_url(self): + self.volume = {} + self.volume['chap_urls'] = [] + self.volume['chap_names'] = [] + chap_html_list = self.get_chap_list(is_print=False) + if len(chap_html_list)", text_html) + for img_urlre in img_urlre_list: + img_url_full = re.search(r'.[a-zA-Z]{3}/(.*?).(jpg|png|jpeg)', img_urlre) + img_url_name = img_url_full.group(1) + img_url_tail = img_url_full.group(0).split('.')[-1] + img_url = f'https://img3.readpai.com/{img_url_name}.{img_url_tail}' + + text_html = text_html.replace('
\n' + img_urlre +'\n
', img_urlre) + if not img_url in self.img_url_map: + self.img_url_map[img_url] = str(len(self.img_url_map)).zfill(2) + img_symbol = f'

[img:{self.img_url_map[img_url]}]

' + if '00' in img_symbol: + text_html = text_html.replace(img_urlre, '') #默认第一张为封面图片 不写入彩页 + else: + text_html = text_html.replace(img_urlre, img_symbol) + symbol_index = text_html.index(img_symbol) + if text_html[symbol_index-1] != '\n': + text_html = text_html[:symbol_index] + '\n' + text_html[symbol_index:] + text = BeautifulSoup(text_html, 'html.parser').get_text() + text = self.restore_chars(text) + return text + + def get_chap_text(self, url, chap_name, return_next_chapter=False): + text_chap = '' + page_no = 1 + url_ori = url + next_chap_url = None + while True: + if page_no == 1: + str_out = chap_name + else: + str_out = f' 正在下载第{page_no}页......' + print(str_out) + content_html = self.get_html(url, is_gbk=False, use_mobile=True) + text = self.get_page_text(content_html) + text_chap += text + url_new = url_ori.replace('.html', '_{}.html'.format(page_no+1))[len(self.url_head):] + if url_new in content_html: + page_no += 1 + url = self.url_head_mobile + url_new + else: + if return_next_chapter: + # next_chap_url = self.url_head_mobile + re.search(r'书签下一章', content_html).group(1) + bf = BeautifulSoup(content_html, 'html.parser') + next_chap_url = bf.find('link', {'rel': 'prerender'}).get('href') + break + return text_chap, next_chap_url + + def get_text(self): + self.make_folder() + img_strs = [] #记录后文中出现的所有图片位置 + text_no=0 #text_no正文章节编号(排除插图) chap_no 是所有章节编号 + for chap_no, (chap_name, chap_url) in enumerate(zip(self.volume['chap_names'], self.volume['chap_urls'])): + is_fix_next_chap_url = (chap_name in self.missing_last_chap_list) + text, next_chap_url = self.get_chap_text(chap_url, chap_name, return_next_chapter=is_fix_next_chap_url) + if is_fix_next_chap_url: + self.volume['chap_urls'][chap_no+1] = next_chap_url #正向修复 + if chap_name == self.color_chap_name: + text_html_color = text2htmls(self.color_page_name, text) + else: + text_html = text2htmls(chap_name, text) + textfile = self.text_path + f'/{str(text_no).zfill(2)}.xhtml' + with open(textfile, 'w+', encoding='utf-8') as f: + f.writelines(text_html) + for text_line in text_html: + img_str = re.search(r"", text_line) + if img_str is not None: + img_strs.append(img_str.group(0)) + text_no += 1 + + # 将彩页中后文已经出现的图片删除,避免重复 + if self.is_color_page: #判断彩页是否存在 + text_html_color_new = [] + textfile = self.text_path + '/color.xhtml' + for text_line in text_html_color: + is_save = True + for img_str in img_strs: + if img_str in text_line: + is_save = False + break + if is_save: + text_html_color_new.append(text_line) + + with open(textfile, 'w+', encoding='utf-8') as f: + f.writelines(text_html_color_new) + + + def get_image(self, is_gui=False, signal=None): + for url in self.img_url_map.keys(): + self.pool.submit(self.get_html_content, url) + 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_content(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)) + signal.emit('end') + else: + for img_url, img_name in tqdm(self.img_url_map.items()): + content = self.get_html_content(img_url) + with open(img_path+f'/{img_name}.jpg', 'wb') as f: + f.write(content) #写入二进制内容 + + def get_cover(self, is_gui=False, signal=None): + textfile = os.path.join(self.text_path, 'cover.xhtml') + img_w, img_h = 300, 300 + try: + imgfile = os.path.join(self.img_path, '00.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) + except Exception as e: + print(e) + print('没有封面图片,请自行用第三方EPUB编辑器手动添加封面') + img_htmls = get_cover_html(img_w, img_h) + with open(textfile, 'w+', encoding='utf-8') as f: + f.writelines(img_htmls) + + def check_volume(self, is_gui=False, signal=None, editline=None): + chap_names = self.volume['chap_names'] + chap_num = len(self.volume['chap_names']) + for chap_no, url in enumerate(self.volume['chap_urls']): + if self.check_url(url): + if not self.prev_fix_url(chap_no, chap_num): #先尝试反向递归修复 + if chap_no == 0: #第一个章节都反向修复失败 说明后面章节全部缺失,只能手动输入第一个章节,保证第一个章节一定有效 + self.volume['chap_urls'][0] = self.hand_in_url(chap_names[chap_no], is_gui, signal, editline) + else: #其余章节反向修复失败 默认使用正向修复 + self.missing_last_chap_list.append(chap_names[chap_no-1]) + + #没有检测到插图页,手动输入插图页标题 + if self.color_chap_name not in self.volume['chap_names']: + self.color_chap_name = self.hand_in_color_page_name(is_gui, signal, editline) + + #没有彩页 但主页封面存在,将主页封面设为书籍封面 + if self.color_chap_name=='' and (not self.check_url(self.cover_url)): + self.is_color_page = False + self.img_url_map[self.cover_url] = str(len(self.img_url_map)).zfill(2) + print('**************') + print('提示:没有彩页,但主页封面存在,将使用主页的封面图片作为本卷图书封面') + print('**************') + + def check_url(self, url):#当检测有问题返回True + return ('javascript' in url or 'cid' in url) + + def get_prev_url(self, chap_no): #获取前一个章节的链接 + content_html = self.get_html(self.volume['chap_urls'][chap_no], is_gbk=False) + # next_url = self.url_head + re.search(r'
上一章', content_html).group(1) + next_url = self.url_head_mobile + re.search('var prevpage=\"(.*?)\";var', content_html).group(1) + return next_url + + def prev_fix_url(self, chap_no, chap_num): #反向递归修复缺失链接(后修复前),若成功修复返回True,否则返回False + if chap_no==chap_num-1: #最后一个章节直接选择不修复 返回False + return False + elif self.check_url(self.volume['chap_urls'][chap_no+1]): + if self.prev_fix_url(chap_no+1, chap_num): + self.volume['chap_urls'][chap_no] = self.get_prev_url(chap_no+1) + return True + else: + return False + else: + self.volume['chap_urls'][chap_no] = self.get_prev_url(chap_no+1) + return True + + def hand_in_msg(self, error_msg='', is_gui=False, signal=None, editline=None): + if is_gui: + print(error_msg) + signal.emit('hang') + time.sleep(1) + while not editline.isHidden(): + time.sleep(1) + content = editline.text() + editline.clear() + else: + content = input(error_msg) + return content + + def hand_in_url(self, chap_name, is_gui=False, signal=None, editline=None): + error_msg = f'章节\"{chap_name}\"连接失效,请手动输入该章节链接(手机版“{self.url_head}”开头的链接):' + return self.hand_in_msg(error_msg, is_gui, signal, editline) + + def hand_in_color_page_name(self, is_gui=False, signal=None, editline=None): + if is_gui: + error_msg = f'插图页面不存在,需要下拉选择插图页标题,若不需要插图页则保持本栏为空直接点确定:' + editline.addItems(self.volume['chap_names']) + editline.setCurrentIndex(-1) + else: + error_msg = f'插图页面不存在,需要手动输入插图页标题,若不需要插图页则不输入直接回车:' + return self.hand_in_msg(error_msg, is_gui, signal, editline) + + def get_toc(self): + if self.is_color_page: + ind = self.volume["chap_names"].index(self.color_chap_name) + self.volume["chap_names"].pop(ind) + toc_htmls = get_toc_html(self.title, self.volume["chap_names"]) + textfile = self.temp_path + '/OEBPS/toc.ncx' + with open(textfile, 'w+', encoding='utf-8') as f: + f.writelines(toc_htmls) + + def get_content(self): + num_chap = len(self.volume["chap_names"]) + num_img = len(os.listdir(self.img_path)) + content_htmls = get_content_html(self.title + '-' + self.volume['book_name'], self.author, num_chap, num_img, self.is_color_page) + textfile = self.temp_path + '/OEBPS/content.opf' + with open(textfile, 'w+', encoding='utf-8') as f: + f.writelines(content_htmls) + + def get_epub_head(self): + mimetype = 'application/epub+zip' + mimetypefile = self.temp_path + '/mimetype' + with open(mimetypefile, 'w+', encoding='utf-8') as f: + f.write(mimetype) + metainf_folder = os.path.join(self.temp_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): + os.remove(os.path.join(self.temp_path, 'buffer.pkl')) + epub_file = (self.epub_path + '/' + check_chars(self.title) + '-' + check_chars(self.volume['book_name']) + '.epub') + with zipfile.ZipFile(epub_file, "w", zipfile.ZIP_DEFLATED) as zf: + for dirpath, _, filenames in os.walk(self.temp_path): + fpath = dirpath.replace(self.temp_path,'') #这一句很重要,不replace的话,就从根目录开始复制 + fpath = fpath and fpath + os.sep or '' + for filename in filenames: + zf.write(os.path.join(dirpath, filename), fpath+filename) + shutil.rmtree(self.temp_path) + return epub_file + + # 恢复函数,根据secret_map进行恢复 + def restore_chars(self, text): + restored_text = "" + i = 0 + while i < len(text): + char = text[i] + if char in self.secret_map: + restored_text += self.secret_map[char] + else: + restored_text += char + i += 1 + return restored_text + + def buffer(self): + filename = 'buffer.pkl' + filepath = os.path.join(self.temp_path, filename) + if os.path.isfile(filepath): + with open(filepath, 'rb') as f: + self.volume, self.img_url_map = pickle.load(f) + self.text_path = os.path.join(self.temp_path, 'OEBPS/Text') + os.makedirs(self.text_path, exist_ok=True) + self.img_path = os.path.join(self.temp_path, 'OEBPS/Images') + os.makedirs(self.img_path, exist_ok=True) + else: + with open(filepath, 'wb') as f: + pickle.dump((self.volume ,self.img_url_map), f) + + def is_buffer(self): + filename = 'buffer.pkl' + filepath = os.path.join(self.temp_path, filename) + return os.path.isfile(filepath) \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e120d35 --- /dev/null +++ b/README.md @@ -0,0 +1,77 @@ + +
+ +
+ +

+      EPUB下载器 +

+ + + + + +[哔哩轻小说](https://www.linovelib.com)(linovelib)网站小说下载,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/bilinovel-download/releases/tag/exe)页面发布了已经打包好的exe可执行程序,包括图形化版本和命令行版本(系统最低要求Windows 10)。 + +界面样例: +
+ + +
+ +PS:暂不支持漫画的排版(本来也没几个漫画< +## 使用前安装需要的包 +``` +pip install -r requirements.txt -i https://pypi.org/simple/ +``` +## 使用命令行模式运行(无需安装图形界面库,支持Linux): +``` +python bilinovel.py +``` + +## 使用图形界面运行: +``` +python bilinovel_gui.py +``` + +## 使用pyinstaller打包: +``` +pip install pyinstaller +``` +``` +pyinstaller -F -w -i .\resource\logo.png .\bilinovel_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/) + diff --git a/__pycache__/Editer.cpython-311.pyc b/__pycache__/Editer.cpython-311.pyc new file mode 100644 index 0000000..566d726 Binary files /dev/null and b/__pycache__/Editer.cpython-311.pyc differ diff --git a/__pycache__/Editer2.cpython-311.pyc b/__pycache__/Editer2.cpython-311.pyc new file mode 100644 index 0000000..7f4be1d Binary files /dev/null and b/__pycache__/Editer2.cpython-311.pyc differ diff --git a/__pycache__/bilinovel.cpython-311.pyc b/__pycache__/bilinovel.cpython-311.pyc new file mode 100644 index 0000000..6d4607e Binary files /dev/null and b/__pycache__/bilinovel.cpython-311.pyc differ diff --git a/__pycache__/utils.cpython-311.pyc b/__pycache__/utils.cpython-311.pyc new file mode 100644 index 0000000..dad1687 Binary files /dev/null and b/__pycache__/utils.cpython-311.pyc differ diff --git a/bilinovel.py b/bilinovel.py new file mode 100644 index 0000000..3b40850 --- /dev/null +++ b/bilinovel.py @@ -0,0 +1,145 @@ +import argparse +from Editer2 import Editer +import os +import shutil +from utils import * + +def parse_args(): + """Parse input arguments.""" + parser = argparse.ArgumentParser(description='config') + parser.add_argument('--book_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(book_no): + print('未输入卷号,将返回书籍目录信息......') + editer = Editer(root_path='./out', book_no=book_no) + print('*******************************') + print(editer.title, editer.author) + print('*******************************') + editer.get_chap_list() + print('*******************************') + print('请输入所需要的卷号进行下载(多卷可以用英文逗号分隔或直接使用连字符,详情见说明)') + +temp_path = '' + +def delete_tmp(): + print(temp_path) + if os.path.exists(temp_path): + shutil.rmtree(temp_path) + +def download_single_volume(root_path, + book_no, + volume_no, + is_gui=False, + hang_signal=None, + progressring_signal=None, + cover_signal=None, + edit_line_hang=None): + + editer = Editer(root_path=root_path, book_no=book_no, volume_no=volume_no) + print('正在积极地获取书籍信息....') + success = editer.get_index_url() + if not success: + print('书籍信息获取失败') + return + print(editer.title + '-' + editer.volume['book_name'], editer.author) + print('****************************') + temp_path = editer.temp_path + if not editer.is_buffer(): + editer.check_volume(is_gui=is_gui, signal=hang_signal, editline=edit_line_hang) + print('正在下载文本....') + print('*********************') + editer.get_text() + print('*********************') + editer.buffer() + else: + print('检测到文本文件,直接下载插图') + editer.buffer() + + + print('正在下载插图.....................................') + editer.get_image(is_gui=is_gui, signal=progressring_signal) + + print('正在编辑元数据....') + editer.get_cover(is_gui=is_gui, signal=cover_signal) + editer.get_toc() + editer.get_content() + editer.get_epub_head() + + print('正在生成电子书....') + epub_file = editer.get_epub() + print('生成成功!', f'电子书路径【{epub_file}】') + + +def downloader_router(root_path, + book_no, + volume_no, + is_gui=False, + hang_signal=None, + progressring_signal=None, + cover_signal=None, + edit_line_hang=None): + is_multi_chap = False + if len(book_no)==0: + print('请检查输入是否完整正确!') + return + elif volume_no == '': + query_chaps(book_no) + return + elif volume_no.isdigit(): + volume_no = int(volume_no) + if volume_no<=0: + print('请检查输入是否完整正确!') + return + elif "-" in volume_no: + start, end = map(str, volume_no.split("-")) + if start.isdigit() and end.isdigit() and int(start)>0 and int(start) 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 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.threadMode = OptionsConfigItem( + None, "ThreadMode", 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.setting_group.addSettingCard(self.download_path_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) + + 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') + + + + +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) + validator = QRegExpValidator(QRegExp("\\d+")) # 正则表达式匹配阿拉伯数字 + self.editline_book.setValidator(validator) + # self.editline_volumn.setValidator(validator) + + self.editline_book.setMaxLength(4) + # self.editline_volumn.setMaxLength(2) + + # self.editline_book.setText('2059') + # 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 = EditableComboBox(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, 1, Qt.AlignLeft|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.label_cover.setImage(self.book_icon) + self.label_cover.setFixedSize(self.cover_w, self.cover_h) + 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): + def __init__(self): + super().__init__() + + self.out_path = os.path.join(os.path.expanduser('~'), 'Downloads') + self.head = 'https://www.linovelib.com' + split_str = '**************************************\n ' + self.welcome_text = f'使用说明(共4条,记得下拉):\n{split_str}1.哔哩轻小说{self.head},根据书籍网址输入书号以及下载的卷号,书号最多输入4位阿拉伯数字。\n{split_str}2.例如小说网址是{self.head}/novel/2704.html,则书号输入2704。\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() + + 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('哔哩轻小说EPUB下载器') + 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('#FF7233') + app = QApplication(sys.argv) + w = Window() + w.show() + app.exec_() diff --git a/debug.py b/debug.py new file mode 100644 index 0000000..8804ae9 --- /dev/null +++ b/debug.py @@ -0,0 +1,73 @@ +import requests # 用来抓取网页的html源码 +head='https://www.linovel.com' +# url = 'https://www.linovelib.com/novel/3207/163708.html' +url1 = 'https://www.linovelib.com/novel/2662/catalog' +url = 'https://www.linovelib.com/novel/2662/183423.html' +# url = 'https://img3.readpai.com/2/2662/183423/210186.jpg' +url0 = 'https://www.linovelib.com/' +cookie = "night=1" +header = {'User-Agent': "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1", 'referer': head, 'Accept-Language':'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6', 'cookie':cookie} +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', 'referer': head, 'Accept-Language':'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6', 'cookie':cookie} +# session = requests.Session() +# req=session.get(url1, headers=header) +req=requests.get(url, headers=header) +print(req.text) + + + + +# from selenium import webdriver +# from selenium.webdriver.edge.options import Options +# import time + +# options = Options() +# options.add_argument('--start-minimized') + +# driver = webdriver.Edge(options = options) +# driver2 = webdriver.Edge(options = options) +# # exit(0) +# driver.get(url0) +# cookies = driver.get_cookies() +# print(driver.page_source) +# # driver.delete_all_cookies() +# driver2.get('https://www.linovelib.com/') +# # driver2.get(url) +# time.sleep(5) +# for cookie in cookies: +# driver2.add_cookie({'name': cookie['name'], 'value': cookie['value'], 'domain': cookie['domain']}) +# driver2.get(url) +# print(driver2.page_source) + + + +# cookies_dict = dict() +# for cookie in cookies: +# cookies_dict[cookie["name"]] = cookie["value"] + +# # 将 cookie 设置到 requests 的 CookieJar 中 +# requests_cookiejar = requests.utils.cookiejar_from_dict(cookies_dict) +# req=requests.get(url, headers=header, cookies=requests_cookiejar) +# print(req.text) + + +# driver.get(url) +# cookies = driver.get_cookies() + + + +# 打印 Cookie +# for cookie in cookies: +# print(cookie["name"]) +# print('*******************') +# print(cookie["value"]) +# print('*******************') + + +# print(driver2.page_source) +# # driver.delete_all_cookies() +# driver.get(url) +# print(driver.page_source) +# import pdb +# pdb.set_trace() + +# print(driver.element) diff --git a/dist/bilinovel-v0.0.3.exe b/dist/bilinovel-v0.0.3.exe new file mode 100644 index 0000000..c83829c Binary files /dev/null and b/dist/bilinovel-v0.0.3.exe differ diff --git a/dist/secret_map.cfg b/dist/secret_map.cfg new file mode 100644 index 0000000..1049fd4 Binary files /dev/null and b/dist/secret_map.cfg differ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0de23e4 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +# pip install -r requirements.txt -i https://pypi.org/simple/ +requests +bs4 +rich +pyqt5 +PyQt-Fluent-Widgets[full] +selenium \ No newline at end of file diff --git a/resource/__pycache__/book.cpython-311.pyc b/resource/__pycache__/book.cpython-311.pyc new file mode 100644 index 0000000..ab90842 Binary files /dev/null and b/resource/__pycache__/book.cpython-311.pyc differ diff --git a/resource/__pycache__/logo.cpython-311.pyc b/resource/__pycache__/logo.cpython-311.pyc new file mode 100644 index 0000000..13253f9 Binary files /dev/null and b/resource/__pycache__/logo.cpython-311.pyc differ diff --git a/resource/book.png b/resource/book.png new file mode 100644 index 0000000..ef059c6 Binary files /dev/null and b/resource/book.png differ diff --git a/resource/book.py b/resource/book.py new file mode 100644 index 0000000..2379fb1 --- /dev/null +++ b/resource/book.py @@ -0,0 +1 @@ +book_base64 = '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' \ No newline at end of file diff --git a/resource/example1.png b/resource/example1.png new file mode 100644 index 0000000..3d42496 Binary files /dev/null and b/resource/example1.png differ diff --git a/resource/example2.png b/resource/example2.png new file mode 100644 index 0000000..c439ed1 Binary files /dev/null and b/resource/example2.png differ diff --git a/resource/logo.png b/resource/logo.png new file mode 100644 index 0000000..8b08b31 Binary files /dev/null and b/resource/logo.png differ diff --git a/resource/logo.py b/resource/logo.py new file mode 100644 index 0000000..6ee4c39 --- /dev/null +++ b/resource/logo.py @@ -0,0 +1 @@ +logo_base64 = 'iVBORw0KGgoAAAANSUhEUgAAAF4AAABkCAYAAAAPM4elAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAB6ISURBVHhe7V0JmFTFtcZoVrNoYkzy8hI1edHsJuaZl5jFbGZRVEARF5TIJsxMzwAqKCqjEJUtsk3PMOz7KrvIJrIKyL5vyiKiqAgiisxMd9d5/1/LnZqe2zM9MMAQ53zf+e7tvnWrq/46dbaqe7tOLdVSLdVSLdVSLdVSLZ1KOlrY7qJYftZf4vltMmPR7O7x/OyJ4HXF0ciBorzIkeJodhHOi/V5XuSteDR7La4vjedHJsfys3vEC7KzYgWZ16nBD3/VVllLYSRDcy+IRVvfCJAHgrcB0OJ4QY4AxBNmgC8cnJJoZAfqHFwUzap3ZGDbL9uf/PjSgR6Nz4dU1ofEziuKRmInC3RlzPoxoPGSvOwX4/lZDSUv4/O2KR8PUgPuv6wkLzIckngo0f/Ugp2K9e9Gsw/H8iMjJT/7F5Kb+wnbvP8skjp1zmEHMfVnQ+pKTrV0p8t6FmC2lUSz56n8yK/YTtvks5soScV5GT8C2MM5zWsK4MnMdkEo4piFo1U0+6dn9QxQfSJfLMlvkwvAFQ1dWIdrGtsBkOL87Kckv/WFtitnB8mCBedBaq6Ga7dT+rcJ7WBaDADig9qLmjlA1JzhEh/aEd9lhZetZma749HILhji39pu1Wyip4Dp+khRXlbixKQcYOfniJrYS9TONSLHj0kZ2rVZEqOellheRsi91ctsP2crpP8JKWz5JdvFmkdqZMdvQEpmyIC2oR2pkCHJ8UEPiSyeInL0sEU5nBKrZkmsV4vwek4Bsz8QptlS2OHbtqs1h2iQ0MhtVVUtiExFRncV2bCkvHSTYsUiOyD5q+aJfPCe/krtWCnxAfefNrVD1qoHqhPez9W2y2eWYP3Pi+Vn1oVPfDCRrsdC3T2wvciCiSLvv6vBLEdH8P2MoSJt/ihy/TkineuLevs1c23vFkmMeBzqJjO8/lPEun/R7EMY8AZQPZ+0EJx+ktxrz2OYDz84PX0OwGOFbUXNHmKATaZ4TGTnOpF+OSKNLxOp/3mRWy4QqftpkS4NRQ4eMOXOEPBkm4aA7s+4VRbknmehOH0kExqeW9Qvs67xzcMbWY4BVHx8N0h5kg6nCpk1QqTDTSK3fV2kwRcB+JcM6LcS+E+JdL2r9L5XN0hiyKNnBHiyM7pa8jHjLSSnh1Remx/B4KSvXsj9MiQ+ta8Bj3TobZGh/xJp/hNI9/kGaAJOsP3zGgY82aidyKHifplXWUhOPalozg9gZN6pEuhkAj+5lwGPtGWFyO3fAuhfKAVaH5POb6x5wJPZf+abJC/nZxaacqSUuhYtPnl7wPw2rPtGBSuvBj8qiQndobNhBJdMEbVqtqjlM0WmDZZE/wfROOh0v7HJqmbvdpG214nc/FkLtAWbUu6Y39X9pEivZiIlJfq2M+HVpGLigH5uVQUdv2khqn5icBQb1GG8bFpUqa8tH30giRdGSaxv69KG5sFXH95J1KswoKR33xR5pqUBNkzS3SDccB7K1UzgyVLYhpI/WYbmXGChqj6S3Iafio/vmqV7ni4dOShq1FOlKsGlAFbNMdcxODLmabiL55aVci3pjmu2xDuWge0k3j+nnQxt8hkL2ckT3SY1vtcVsnWp7rgmpQDcUZHXoC4Y3Lw4TmRSP5HphaK2LDNlSopFrZ4vsT73BQ2M50VEzYQ76WjBWJG/1zEAB4AnnZ8FwGtPB241jO1PLGxliPkre5o+HezW9AuQ1HWye7PuuHb/2v211M8ma0kFSPXhCnasa8qB1Os7yob3NLBT+tiroPULRG6iccV9GmwfdMs1XNU4Vv1zmFjbpEKWF2Fkv4ymp5/rZ4QWz89pLYMeFFnxvO64fPi+SKc7ROp9rhQszTivB7ew3TWmHIjRZqxX89IGEngY5MBG7NkGA/sXz8B6gGvGdzdA4s8C4MkysC2kv02O6hP5tIVQE5p9KTj9HP/xPpHvIlgojvdHxDl/jO64FH0kMvwJSKoPlgWfsyDnl6Yc6eB+SfRjo6yHQ89mJO7ds9VcP7AXUemdqAvRaVAPjr7Un0XAx8EMKovgclsINVVJ4mVC7qcQHvek1Y7BZ0244CdWImrucPjXBMsC7gOfdRXUEWYF6fABiQ95uBQkHOODHhZZA8+I9P4hSEkHA64/e3zgqeO7NxE5ZpNoNcSPT8VS2JZphT7q320/a6GkxKdvdFXfrMuKollFHEVWqEY+aTpOw7r5JaN7NVAAx4FWD8C3/hkk2Sa03nsHns2TZTybGGOARePNdUrxtCg8m0+UAh3UZ481KnKFczDgYaqTkGuGrdTHVH7OFRbK9InSXpIfGUmDoSskYJBcKT5uOn/4LQBzsUgDC7wbAEr8fT8uVSXJwLNh9GxmIehy9NLkUs8mqMsxPuvIFeroLAGerLVENDIWNvJzFtL0SOVlXIJRO+6kXQM/CCrh4Bum8wSBUSdzLIGKsMC3+CFmhHUpUU5N7AeQvBUjGthJvTCIRabMxiUijf4bgwjvxtXlJJ7nlHg/O3lagI9IorC9JGCP1OKJIjvWi7yxC7+9UWTWMIkXwsYkR+YeW6kvUQXZ37OQVk7022N5kW5lFzXwIwMeMPkV0rGjov6NqFN7Iw4kMIFvdrmolbNNuQ+OiJoxQIMd1AXA4uO6IXLFrCHt2gKD/Cfcf6HInd+22UkPeEr8qU4L0/YMe1SnPmQP2vMhXGaVML+XTIffkfiIXH1PaF2WrdQ/k+zhpCQZmvuZWDT79XLpXoyyvDzL/DhXi4Y9Xgq8k1AmvO79nsjSaaYcBigxb0T51AEaLrs26CLqrT2SePp2Y0Qbfa08+MkSX93AYzaqcc+UCkJlhL7HRz2h+xFan2W9lSWafYC7LSy0qUlWF36yJC/jt3q0kiuDXuM00xRDVDoH51ykcKqBzACqyXdEZo825ZgWWDxJEhN7QJdPRySLKHf5TFEvjDJTl8TlvRVzRdpA6hsD9Ay4o5w5Tu3Qe3oMQdnrr5jye3dCBdBuVNOCN1xVhcEsR86eURXmIY7p2greFWb52/skPpa/X7k7a9afW/2p0n06DJgwPYaFpnzpUk7rZxpDz2YTGkQ30EmmZgB/N8Cb0NuUI/C7NpnzdIi6NPtaeEdexjIZ+Dd2S2J897Lq60QZs0aNQFwBMDVxjaAATkTTn5q+vTxTZO5YzOLLzWz+x7k62k48D/VZRhWHs1bX0choahELcTipET3Oh1E4FlaJNrDDHjENJO3ficahIT7wVBF3wlCO7GkLVZEYFQ+FrmW9TuIZqD10nahXMSikd16XxOTeAN5TXyfKOq7oIOpNO/s+ggp9yEbSaIOCoKmlU0VawlMj8HSh540U9eI4bfM0JmH1WrZGtlhGP516c5RInXNKoq2vSblTwLmUBIdEl/KeKwzYTscTqNu/gZHuaMo4eu+gyNSBIuOhS6cUGHfzJdiLbi1FDQLQ03HNSd3zOPcTZzd9RuTBP4psX22uVyfw5GRV8/Q9RmXSthQ+KGrdApHIL0w6hN9N7itq7XyNRWXAk7W6Kcj8Q0p1o57v8+lYXvZTge+ezM6l3L/bNPAoLP9jt5kGBVJPsBAdP4VI06edayFFuEZJyvg59D6kCOBpyWbOJ3I1pvBiU3bZdIANHU/3kvXdjOuZ/ytqFewA6d0DUHnRsgb7ZLh3C1GblookrBdTCOG6DUYe3pR6qjHaDhey7a9NO5naGIxZv3WVxIc/BkzS0PPGXvZgat1CXZZMQiyyIvXGUgCP6aU2WoAg+apnCwMMAdIM8Ovj+OhNpoyj3dDzd1xipDcDke1Lz4nMHACdiYiVA5cNg+qAX7cQduJ/jIFlnbzeivfMMNffPwQdOwjAtwpp4wlwn/sQST8LNfOhrl7NheFvCp3Otmb+1kThHf6AfqIdUHuqe1M9Y5lzSgd42kuonNVqfGkKoQxJYeHniqKRD8NuDli7lNZHLzomasyTkAw00Ek7jwTq/t+YMo72QrXc+yPdGdXqx5JYOB7AQ6UwVcCB8yW+DPAcSNTX7CciCzFLSB/CRZ0LF9XL858UQ2WpqVEdZZPUevx+axhXAn/npSZo7NzICBRnbMcGUHf7JT7mX2l5NlrPR7OK3huaW36FivvCS/q1/HVKNeO4f1u4isN1A/VCB/fI6KU7Czp1MoFv+ytTxhE9kgxMV3go6j4ATwmbO9JKfArgG1iXkgNwDz7PQnlS0UeSWDC2+oDXnk1nbTs0HXxTpB0knQJBW7N/h0g+gsdGF5u20uvCrFOT+kLiQ+oLYb0NsH/G73OT9TwVP1TMPysFHqy45Y5EnbgSejfILFrWqgNAOiNMenMvDOT1Wm9Ks+8jBsB0XjQBwJ9jpnAklaoh8ND1d8BTmtbfXI/HJbF0MnRzy9D2VZ0jkugbgXDASyPRVYYXFQC/GW7zmF4id8FNZpvqw4Yx3T1niI5twussy9Tz8YK2LbgfyUJuiMDDeD5Z6ZYN51K6cPr17aVrpj7wNKBvWS+FhKmpumC6cnYQeOpRAs+OJauaTctEmkOnU7p0nfAwbkaHR3Yx10GJFTO0UQxt44lwr+aidqyytYN6ZRongYLBKHwW2suInMDzu7fgYCyfK/FCuJQV5GwcW3++e7mtfxyJWEH24LSAHwpXEXpWE0ZeGnzVuJTOwOrUMHTk3m2mDAmeiHSDUaIf3OwKRK7jEK3CwBJ4PX09iQ+AxwC6waT3M7i9uQ5KrH2h7MrWyTJmj1rzgl5r0DQhz9ikh+qJvLIBQdse7RCo5TDwG+EBxWOiti7XXp7GJKxOj7UmiWaPYNbXQm6IW9Hi0ezllW7Hcy4lE0kk+udt/mzVgtXzVA0t0ejNNqFGYrk8jDwBDIBHJ8Ikfjtcz8jv8T2jV9TH2cRZBaEIaMNiqAcatso7nRbDNU3MHizygU09c4GGqYyK6LXt9mGJyttgPZtV5XYhEHhUsIcWOOzGUsaPDERQ4VxKNvDRhp5asMA3xbR07h+J+p4G7B8AugUG5aXnATQk7G8VAY/vCTqZdgRSGSz/bX+5mpf/oOf7QTBcdJwOHX47bV+eAl2SF9nvr0ppYioYruQHYTeVY7qUC232kSqnbxvjelHiCRKl/5/fEZlj12hJ8ID0TuBFk0S2rTQ+8zHcyzTzxiWiNsCgHsGsIDFQuu2/Sr0aDiYjxm53m6CN9Mp6SQx5RHskoW08EWYgxa0pfjqYUs+AcTkEZcYAUUM6iQx5AnofQgXvKj66c3rAg7m+UW5hpErA06WcM8I07PhHIqN7Gm8lkHjo+3suFTUDfvqJkJ8y0HXiyIixyy2iXFph7/bqzVA6z2b/q6Z+6vo++Nzk+yINv+K1Bay9NgRUEAL1bG8AH1ZfWU4NPKxtOq6kZvyQGtPNNLCkSNSsQUYinVpgqH8X3L/R/zZlqkrPI6LVwNv6eOSM6ngDImBrsN/YJYlx1ZSh1JwEPOlfdxgh8kHnOZc762Mw3kVg9eIEifdvp+8Pr7eU6curHvefbyE3VCWJ1y7lo2Ya0pdnJFsX3koAPBp7+9fhu6KMo0RcR68MOmRoZ1h5eCh90Jg8NIYLKgyojsJekKjjs6Hj/dQwgW8PI759jSkDyU9M7lV9iTJysqph0MScTeCxOfDBTA/vWC2yer7EYfPSMbDVA/wQuJR0EUm74W4xAg0ahyOnZ9d7zXUSdbxLETgwXXlO3Vbw+5fb1a0DcN2YA7rlIlOGg0lDm3W1qNUwyCQ+UYIIWhW0h8Sl50tXyszZLJlc+izWVARsUJnaWXBtde2md7Z0KmzTIokNrtylTK1q0vZqwPwR/Fiwk4B6twFACnYcQEI4RR+DD+yIM4P5GQYfQQd4BBN4uJ9qETpNYrqZM6IBBs9JG72m5ogNFk03ZcIIak/Pmn07RHFr4ILJoibn6Y2y6UikcSmHmC2KpBUQBLrFdBacytNHtIeB4JR+wAAuJWb/6QOeLiVXoEiHIPktrwJIkAzXMEroA78z1x05vz0A3jKTYP+8HDPCGuz33xU19DF8j8jRAe/SBvAsTojg9mo1AqcgPjjFg8rM2TA6djsp9u00bq4O5NhWCzz5ps9q35wBZHzk4+H1eZzanUw3gHJc2E7UQoT8JE77R271dDIaRglte43OqwS0FR2n3+4Adx1JBh5SqzCwcj03TOE6mbOJOfrRT5kyJEo4Ax4eq0K0N1zvRRDHZ2tLQbMG1q1GkTr80QiR32YeGdx1vt0sfI/uUinwqQOodFMGjlEuMSPfNA5TU/VoZhtowaSUUFoOWd+cxFwIF0OCDljWfr8HPLf/QSq1HtUziIx7OKOa/gC6/jcijb5hBoPXtDGH39/4uyL3/QJuZ2MR7nqbjfoYJxw3efZQgiFVr6wVNbY7gEHfmbPB54B6ImhjzqaMgcWR/cvBoHD7ygQY+UpcyopSBuklyRzTpURjNXFBm6syei+lBYr5miyA4CfK9mwWufeH6ASu6U7YjhD4uy4RmWgX0mncRmHKB4bYdjb03P+c/B2OHBx6Ju3rihrV1Ww95KbbMOLMZXyyBvahyO4wGAcvjG1zwGtBwJFCcDeic+bw54/TsU0oTpZTJ8mqkBbWTD1Pl5IRKHeETepjfHkHACWi9ZWi7N4ZTa/twHe/MmrIdYKs/f5vJwHf2QDvyvgAu87759ybH5QD+2U0288cZD5l2B/OAXe7uS0cPjEv7waHEWsLzLLAs/HqY/vegWCtXSCxARW7lKnTwukuhDjmjwyFlB+CB8Ldwwx69IKI7SClmlv51tj8C4lbsjveaHxyV06XRacaJ0n8SAB/EwYv2NzkOuwY9xNcH+ygTncEB2W8+xxTODJ+LWpqgbEVYUQDy5nLAQvqsPUSeL7oYvvqSrOUKRdCSGkt/TnmjzBL6Vb+KRnB7mGwlqwrRJY9Z66TuLJD317vhXedQAcIbCMEXKOs4aTrOfkZqC7MDA28LefKl2P/OjgYDPvZlfMHyQ0GjxQS2gcu8MD7KUNsy/0M5jBIQZ22DvZ36RS91sDZnwp47UqmWvojUf9UvNjtM36Eeyn5/BNpG4yY1sloEDvDqdnkMlHPDTbXSdSH/bLK5nV0xwk8jKUP/CQH/LesxNuy7j7NScD6HJR1ZfxyyedglrczQK+q8fF+R92b47oXU/A+lodq1ekS2IbY8E4pgbceTQWL3fRs8ivY3pHMTJbNtVv13nwVwCM4ch3W6gPSyiUzRx8eERn+uDczbAdSAg+XzQfeAVQGLAzOg3+Ga3eXsRP8Xf+6f1/wOencleGRs4uBG7eRu93MY3t7dXvlucg/mKkTqNrxPQF8CEbgyrd3cN9kRRuakpmezbgepnE0SI1h5R1IPN4BF29gJ3OdRN39LAYi8FYshwHPJ8AZ5frguaMPIhdIBmLmcZ0Urh2jVXniDhNs+QPm3x+cu8/26OrkfQzeuIOMRDVKtakNrLsHTFvVB0EUBigxs1C72GE4VbqhiVThFr5kZtCAqE0TjSz9Wup2dkC7cReL9PVWjWKYvtyhwLxOAB7KUZI4lbmjzNEcqKggNeyz13Hez+neo4moQ7AfPrE90/qLtLnW1B12vz537L6zDFBVFwRIh982GcvMn2N2OQOL8vxtxi1dMMhwp9XiSTqoTM4Z2VRBxVv4SNTz0FXhm1aTmcAPR2hPSaYP3PEWM/VdZ7gPJbeBRcLSsmkGUAc6y1HCbr2oLPBchtNRrq3LByX4DsztIo/XE7Vvu70xiRIY7M0vIxDKMKrPdwt1G8A8JjNB5a4IF4e0Q9CmDaz3+xQy7nDmwxdbluk0SrKeT3vTqt4/mWqbdjLzR5ilPAA1A/2td1jpJwHZMDSQ2zba8x0KHnGrhA88j2HAzxtmgHdAuDpdp/U5mDu72v5O1CYERiQa8BSP+6tlM0RaXekBaH/f1emfO4nnYj6JC/XBzGEZHDlT70bEzaQeYpTYYKYfkoCnmklnmzYp5YMJycwf4SPyW5abgGMQBkFHr7ZD7CAlJQ6d7WgbpE+nDah/ebTnqYD3Oxqce5+ZH2KaYKVNF9OAcz2XT5l4pJg7f5Db8Lx8Url6cXQDXfc8UdzT79INo2Hs7/INPZjnzMpycDDg3H3hA1+lBxNI4Y/ihDB/hMmsNfON+/UsrL+OXm3DCHzO/0EivLcx7dpo0gY6IGE5dLYiiffBILvPvJfRqrYPX4NqQnkS13HtHsiAENJLS0SrWtLtvQGjDs3uHEe2jVs73E4KEhfum0G6XbtdG+gocFBLirXa9YHXWqMqj+KQyj18lorpUr4wVnsVCpa9FHh0gsYo8yqTKnC0fxcCkr9ZybONDwN+Iep0Ot51MgDI/4x7mbWcZhN2PnEWMllGl1QDZu8P6nPn/veU4iR3EqRTH9wrFAyevUc/vGBeisHclXMprVGt2sNnpHKPW6ZiupTj7drqwol6igadYmdbobFbV5rrpLdeE/XErVqHBp0O82oI/F8BPCNaAsdyDjAeA8DAlDouzPhBD1XN441w/cLScq5d5di7zrTH8FzjmvrE9HYbu1Xbr4vl540yZeaOCpJlVtqr/rglKfkB41CGZ8MHsbTvzZ1YLnpl4wh88+/DD55pGkaie9YHHoavklj2ZoDPfS2O6EPfiPv1w2hOt6JcGPgMyHq1gGdlw/25UC0trBENynnHoB6vPq2vvwIhgl4/5u355BtK3IA+djP6ZNvh6mP6Y0oe+o+BWTxDb32x0n5iDxiTyjxSnwy4YwI/DC4ld4rt3WSX9mzD6LpxY9ML9iluEsFhZjCYGSyLziQDzwX0hgiqGAukTBsQODAN+pOIXNfDsxkAA69z9ajPlfMBD9j7Xut02J2l08vMGrUF9XGDE9eLSaMws52B1feinpsxc4eg/1BLavNSY/OAV/Ij9VWm4CUSYaCTYUzidClfR5DB90Jy24PrHIG/+1JEoVHTcBIkSI1FhOqnDXQH0Hkf+A1LMVs8qdXlwK7D7jOZIHMhpYkXOWu27Sj32bIG8GJJ9GwqiptvPeJiCN3DBJ9spMEmcWD4wIKzF2wLbJXq2VwHUdwrGsc9x/tllXuJRJXJJM5yWht/NBx4ZinV1hXmx2Fg9NaNvgCxdwSBC3i23ddOosGaXlBWJZGTJX7bGpHI76xOdcC58v5n+x1BCAbFctggue/op3eC6tgK95Zq0qfVC/WLLrh1JDEBqsfucNMPv90Hb6fRN+GtIT55BAFjz1YmGmde/xj6P6qTxKOZOVXyZFKR4ouC8iPrwqNZSHzhAyJLPD1eEbGT80d76QALRDLwuzeJevjvxggHgNmy7lgG7JDrZc7tseHXEOneZlzg5LVaRuCLJks8WNSw+yndAwskRui+ES9DSuIjOm1US8d+Fx+q532Uql/m5UV52SWhKqegjX7uM21aiUFK3m2QBDzf7KQ614f+dqtaLAsuA7S9N/jeHt25u87F9FZXi4yB9B6wD875RCBXztJqotyiNbdv8wHjlGB7tHu9qEkFV1rITp5Q5bla5RRkZ4WrnByzmfO5QaLWwrPh4si2VWaT6j748MsA9JLnzM4z0nZcuwfeTutfimRdI9LhBrMPnY+1O3p7n05+la5qhQDqvnff0dDyXIfy3xHJbYigri/iiG3l1QkpXmLUCl9IkQy4Y+08wM3lRlsm+RxxwYQrauwjg6tpvTFTHmgnudX4MjhH+vWH0ezxaeVxfM7LhMvZWT+RnTbR7eSiib+qVQZoAozv6OczZdD5TvjfXfRbpBSfMKxoywcDq1ULAHh6W6wNQ+0MeEjUwEck+ZUpxOOUvf7QUekLP9PIXjqm50M74F4skQ5RujAzFH3kyWA+gDwPbulL08zzrpxV3EjlXL3KCPqb+ZrEc/3T3uuYDp+WF346OqFX3BJ8vrqczz5R/fBBBWb1KNlk7sPcuUY/3lLpkxjpkN7XDhd35VxR0/PT38ZXBWb/K3vFbbUSlwiL8jKuh3+f/pu002HqU76fjK9mYSSYDtHoMSO5b6d+PpW729SYrtxKgTqrF2if2W/0n2/UrsddeBaaU08a/Kq+xjxdxgDwaQ+ZN1rU6jn6pUM6l86nSeZPEJkxRNSzz+i3biT6V/ympFPBHuin/zXmpCq/uP8/gNnPM/rifkcc8Sr/VcVZyrp/NeGvKnw60T9nOVuY/YI3V3P+nMWnk/o7ohrM7A+8l5r5d0SOTv4PuGoOOyNa4/+Ay6dq+cu5M8hatZxVfzknco57VzpX18/mP1k8kt/6Qt2fqrx+vKYQ95PU/q3oGaTaP9I9w0RJUn2zroRkDWOeo/avo88AqZG5X6z9s/QzQNpwTZignwmSobkXMP0AKRwI3gaAik92IGx4X4yZtQN1Di6KZtU7Yv+/g797VhrN6iZ/EEhHC9tdFMvP+ks8v00mQOsez8+eCF4HIA9gUI7geJwhfHE0u4ifoaMP4Pp6uLCTAHoPgJ6F2XQd1w9slQbsj5NKOd2EQfxELcC1VEu19LGnOnX+H37k8vXQxRGPAAAAAElFTkSuQmCC' \ No newline at end of file diff --git a/resource/logo_big.png b/resource/logo_big.png new file mode 100644 index 0000000..4643b89 Binary files /dev/null and b/resource/logo_big.png differ diff --git a/resource/trans_base64.py b/resource/trans_base64.py new file mode 100644 index 0000000..99fbec4 --- /dev/null +++ b/resource/trans_base64.py @@ -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/book.png " +base64_string = image_to_base64(image_path) +print(base64_string) \ No newline at end of file diff --git a/secret_map.cfg b/secret_map.cfg new file mode 100644 index 0000000..1049fd4 Binary files /dev/null and b/secret_map.cfg differ diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..7b4ac92 --- /dev/null +++ b/utils.py @@ -0,0 +1,149 @@ +def get_cover_html(img_w, img_h): + img_htmls = [] + img_msg = ' \n' + img_htmls.append('\n') + img_htmls.append('\n') + img_htmls.append('\n') + img_htmls.append('\n') + img_htmls.append(' Cover\n') + img_htmls.append('\n') + img_htmls.append('\n') + img_htmls.append('
\n') + img_htmls.append(' \n') + img_htmls.append(img_msg) + img_htmls.append(' \n') + img_htmls.append('
\n') + img_htmls.append('\n') + img_htmls.append('') + return img_htmls + + +def text2htmls(chap_name, text): + text_lines = text.split('\n') + text_body = [] + text_body.append('\n') + text_body.append('

' + chap_name + '

\n') + for text_line in text_lines: + if text_line.startswith('[img:'): + img_no = text_line[5:7] + text_line_html = f' \"{img_no}\"\n' + else: + text_line_html = '

' + text_line + '

\n' + text_body.append(text_line_html) + text_body.append('\n') + text_head = [] + text_head.append('\n') + text_head.append('\n') + text_head.append('\n') + text_head.append('\n') + text_head.append(''+ chap_name+'\n') + text_head.append('\n') + text_head.append('\n') + text_htmls = text_head + text_body + [''] + return text_htmls + +def get_toc_html(title, chap_names): + toc_htmls = [] + toc_htmls.append('\n') + toc_htmls.append('\n\n') + toc_htmls.append('\n') + toc_htmls.append(' \n') + toc_htmls.append(' \n') + toc_htmls.append(' \n') + toc_htmls.append(' \n') + toc_htmls.append(' \n') + toc_htmls.append(' \n') + toc_htmls.append('\n') + toc_htmls.append(' '+ title +'\n') + toc_htmls.append('\n') + toc_htmls.append('\n') + for chap_no, chap_name in enumerate(chap_names): + toc_htmls.append(' \n') + toc_htmls.append(' \n') + toc_htmls.append(' '+ chap_name +'\n') + toc_htmls.append(' \n') + toc_htmls.append(' \n') + toc_htmls.append(' \n') + toc_htmls.append('\n') + toc_htmls.append('') + return toc_htmls + + +def get_content_html(title, author, num_chap, num_img, img_exist=False): + content_htmls = [] + content_htmls.append('\n') + content_htmls.append('\n') + content_htmls.append(' \n') + content_htmls.append(' urn:uuid:942b8224-476b-463b-9078-cdfab0ee2686\n') + content_htmls.append(' zh\n') + content_htmls.append(' '+ title +'\n') + content_htmls.append(' '+ author +'\n') + content_htmls.append(' \n') + content_htmls.append(' \n') + content_htmls.append(' \n') + content_htmls.append(' \n') + content_htmls.append(' \n') + if img_exist: + content_htmls.append(' \n') + for chap_no in range(num_chap): + content_htmls.append(' \n') + + + for img_no in range(num_img): + content_htmls.append(' \n') + + content_htmls.append(' \n') + content_htmls.append(' \n') + + + content_htmls.append(' \n') + content_htmls.append(' \n') + for chap_no in range(num_chap): + content_htmls.append(' \n') + + content_htmls.append(' \n') + content_htmls.append(' \n') + content_htmls.append(' \n') + content_htmls.append(' \n') + content_htmls.append('\n') + return content_htmls + + +def get_container_html(): + container_htmls = [] + container_htmls.append('\n') + container_htmls.append('\n') + container_htmls.append(' \n') + container_htmls.append(' \n') + container_htmls.append(' \n') + container_htmls.append('\n') + return container_htmls + + +def get_color_html(colorimg_num): + color_htmls = [] + color_htmls.append('\n') + color_htmls.append('\n') + color_htmls.append('\n') + + color_htmls.append(' 彩插\n') + color_htmls.append('\n') + color_htmls.append('\n') + for i in range(1, colorimg_num): + color_htmls.append(' \"'+str(i).zfill(2)+'\"\n') + color_htmls.append('\n') + color_htmls.append('') + return color_htmls + +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