') and not line.startswith('
'):
153 | text_chap += (line+'\n')
154 | text_chap = BeautifulSoup(text_chap, 'html.parser').get_text()
155 | return text_chap
156 |
157 | def get_text(self):
158 | self.make_folder()
159 | img_strs = [] #记录后文中出现的所有图片位置
160 | text_no=0 #text_no正文章节编号(排除插图) chap_no 是所有章节编号
161 | for chap_no, (chap_name, chap_url) in enumerate(zip(self.volume['chap_names'], self.volume['chap_urls'])):
162 | if chap_name == self.color_chap_name:
163 | text = self.get_chap_text(chap_url, chap_name, True)
164 | text_html_color = text2htmls(self.color_page_name, text)
165 | else:
166 | text = self.get_chap_text(chap_url, chap_name)
167 | text_html = text2htmls(chap_name, text)
168 | textfile = self.text_path + f'/{str(text_no).zfill(2)}.xhtml'
169 | with open(textfile, 'w+', encoding='utf-8') as f:
170 | f.writelines(text_html)
171 | for text_line in text_html:
172 | img_str = re.search(r"
", text_line)
173 | if img_str is not None:
174 | img_strs.append(img_str.group(0))
175 | text_no += 1
176 |
177 | # 将彩页中后文已经出现的图片删除,避免重复
178 | if self.is_color_page: #判断彩页是否存在
179 | text_html_color_new = []
180 | textfile = self.text_path + '/color.xhtml'
181 | for text_line in text_html_color:
182 | is_save = True
183 | for img_str in img_strs:
184 | if img_str in text_line:
185 | is_save = False
186 | break
187 | if is_save:
188 | text_html_color_new.append(text_line)
189 |
190 | with open(textfile, 'w+', encoding='utf-8') as f:
191 | f.writelines(text_html_color_new)
192 |
193 |
194 | def get_image(self, is_gui=False, signal=None):
195 | for url in self.img_url_map.keys():
196 | self.pool.submit(self.get_html_img, url)
197 | img_path = self.img_path
198 | if is_gui:
199 | len_iter = len(self.img_url_map.items())
200 | signal.emit('start')
201 | for i, (img_url, img_name) in enumerate(self.img_url_map.items()):
202 | content = self.get_html_img(img_url, is_buffer=True)
203 | with open(img_path+f'/{img_name}.jpg', 'wb') as f:
204 | f.write(content) #写入二进制内容
205 | signal.emit(int(100*(i+1)/len_iter))
206 | signal.emit('end')
207 | else:
208 | for img_url, img_name in tqdm(self.img_url_map.items()):
209 | content = self.get_html_img(img_url)
210 | with open(img_path+f'/{img_name}.jpg', 'wb') as f:
211 | f.write(content) #写入二进制内容
212 |
213 | def get_cover(self, is_gui=False, signal=None):
214 | textfile = os.path.join(self.text_path, 'cover.xhtml')
215 | img_w, img_h = 300, 300
216 | try:
217 | imgfile = os.path.join(self.img_path, '00.jpg')
218 | img = Image.open(imgfile)
219 | img_w, img_h = img.size
220 | signal_msg = (imgfile, img_h, img_w)
221 | if is_gui:
222 | signal.emit(signal_msg)
223 | except Exception as e:
224 | print(e)
225 | print('没有封面图片,请自行用第三方EPUB编辑器手动添加封面')
226 | img_htmls = get_cover_html(img_w, img_h)
227 | with open(textfile, 'w+', encoding='utf-8') as f:
228 | f.writelines(img_htmls)
229 |
230 | def check_volume(self, is_gui=False, signal=None, editline=None):
231 | #没有检测到插图页,手动输入插图页标题
232 | if self.color_chap_name not in self.volume['chap_names']:
233 | self.is_color_page = False
234 | self.img_url_map[self.cover_url] = str(len(self.img_url_map)).zfill(2)
235 | print('**************')
236 | print('提示:没有彩页,但主页封面存在,将使用主页的封面图片作为本卷图书封面')
237 | print('**************')
238 |
239 | def hand_in_msg(self, error_msg='', is_gui=False, signal=None, editline=None):
240 | if is_gui:
241 | print(error_msg)
242 | signal.emit('hang')
243 | time.sleep(1)
244 | while not editline.isHidden():
245 | time.sleep(1)
246 | content = editline.text()
247 | editline.clear()
248 | else:
249 | content = input(error_msg)
250 | return content
251 |
252 | def hand_in_url(self, chap_name, is_gui=False, signal=None, editline=None):
253 | error_msg = f'章节\"{chap_name}\"连接失效,请手动输入该章节链接(手机版“{self.url_head}”开头的链接):'
254 | return self.hand_in_msg(error_msg, is_gui, signal, editline)
255 |
256 | def hand_in_color_page_name(self, is_gui=False, signal=None, editline=None):
257 | if is_gui:
258 | error_msg = f'插图页面不存在,需要下拉选择插图页标题,若不需要插图页则保持本栏为空直接点确定:'
259 | editline.addItems(self.volume['chap_names'])
260 | editline.setCurrentIndex(-1)
261 | else:
262 | error_msg = f'插图页面不存在,需要手动输入插图页标题,若不需要插图页则不输入直接回车:'
263 | return self.hand_in_msg(error_msg, is_gui, signal, editline)
264 |
265 | def get_toc(self):
266 | if self.is_color_page:
267 | ind = self.volume["chap_names"].index(self.color_chap_name)
268 | self.volume["chap_names"].pop(ind)
269 | toc_htmls = get_toc_html(self.title, self.volume["chap_names"])
270 | textfile = self.temp_path + '/OEBPS/toc.ncx'
271 | with open(textfile, 'w+', encoding='utf-8') as f:
272 | f.writelines(toc_htmls)
273 |
274 | def get_content(self):
275 | num_chap = len(self.volume["chap_names"])
276 | num_img = len(os.listdir(self.img_path))
277 | content_htmls = get_content_html(self.title + '-' + self.volume['book_name'], self.author, num_chap, num_img, self.is_color_page)
278 | textfile = self.temp_path + '/OEBPS/content.opf'
279 | with open(textfile, 'w+', encoding='utf-8') as f:
280 | f.writelines(content_htmls)
281 |
282 | def get_epub_head(self):
283 | mimetype = 'application/epub+zip'
284 | mimetypefile = self.temp_path + '/mimetype'
285 | with open(mimetypefile, 'w+', encoding='utf-8') as f:
286 | f.write(mimetype)
287 | metainf_folder = os.path.join(self.temp_path, 'META-INF')
288 | os.makedirs(metainf_folder, exist_ok=True)
289 | container = metainf_folder + '/container.xml'
290 | container_htmls = get_container_html()
291 | with open(container, 'w+', encoding='utf-8') as f:
292 | f.writelines(container_htmls)
293 |
294 | def get_epub(self):
295 | epub_file = (self.epub_path + '/' + check_chars(self.title) + '-' + check_chars(self.volume['book_name']) + '.epub')
296 | with zipfile.ZipFile(epub_file, "w", zipfile.ZIP_DEFLATED) as zf:
297 | for dirpath, _, filenames in os.walk(self.temp_path):
298 | fpath = dirpath.replace(self.temp_path,'') #这一句很重要,不replace的话,就从根目录开始复制
299 | fpath = fpath and fpath + os.sep or ''
300 | for filename in filenames:
301 | zf.write(os.path.join(dirpath, filename), fpath+filename)
302 | shutil.rmtree(self.temp_path)
303 | return epub_file
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |

4 |
5 |
6 |
7 | EPUB下载器
8 |
9 |
10 |
11 |
12 |
13 |
14 | [文库吧/轻小说文库](www.wenku8.net)(wenku8)网站小说下载,EPUB打包。
15 |
16 | 特性:
17 |
18 | * Fluent Design风格界面,下载进度与书籍封面显示,主题切换,下载目录自定义。
19 | * 前后端分离,同时支持命令行版本。
20 | * EPUB格式打包,支持多种阅读器。
21 | * 插图排版。
22 | * 书籍批量下载。
23 | * 图片多线程下载。
24 | * 缺失链接自动修复。
25 | * 自定义彩页。
26 | * ...................
27 |
28 |
29 | 有建议或bug可以提issue,由于软件更新频繁,可以加QQ群获得更多信息:563072544
30 |
31 | 图形界面使用[PyQt-Fluent-Widgets](https://pyqt-fluent-widgets.readthedocs.io/en/latest/index.html)界面编写。
32 |
33 | [release](https://github.com/ShqWW/lightnovel-download/releases/tag/downloader)页面发布了已经打包好的exe可执行程序,包括图形化版本和命令行版本(系统最低要求Windows 10)。
34 |
35 | 界面样例:
36 |
37 |

38 |

39 |
40 |
41 | ## 使用前安装需要的包
42 | ```
43 | pip install -r requirements.txt -i https://pypi.org/simple/
44 | ```
45 | ## 使用命令行模式运行(无需安装图形界面库,支持Linux):
46 | ```
47 | python bilinovel.py
48 | ```
49 |
50 | ## 使用图形界面运行:
51 | ```
52 | python bilinovel_gui.py
53 | ```
54 |
55 | ## 使用pyinstaller打包:
56 | ```
57 | pip install pyinstaller
58 | ```
59 | ```
60 | pyinstaller -F -w -i .\resource\logo.png --paths=C:\Users\haoru\bilinovel-download .\lightnovel_gui.py --clean
61 | ```
62 | ```
63 | pyinstaller -F -i .\resource\logo.png --paths=C:\Users\haoru\bilinovel-download .\lightnovel.py --clean
64 | ```
65 |
66 | ## 相关项目:
67 |
68 | * [轻小说文库EPUB下载器](https://github.com/ShqWW/lightnovel-download)
69 |
70 | * [哔哩轻小说EPUB下载器](https://github.com/ShqWW/bilinovel-download)
71 |
72 | * [拷贝漫画EPUB下载器](https://github.com/ShqWW/copymanga-download)
73 |
74 |
75 |
76 | ## EPUB书籍编辑和管理工具推荐:
77 | 1. [Sigil](https://sigil-ebook.com/)
78 | 2. [Calibre](https://www.calibre-ebook.com/)
79 |
80 |
--------------------------------------------------------------------------------
/lightnovel.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | from Editer import Editer
3 | import os
4 | import shutil
5 | from utils import *
6 |
7 | def parse_args():
8 | """Parse input arguments."""
9 | parser = argparse.ArgumentParser(description='config')
10 | parser.add_argument('--book_no', default='0000', type=str)
11 | parser.add_argument('--volume_no', default='1', type=int)
12 | parser.add_argument('--no_input', default=False, type=bool)
13 | args = parser.parse_args()
14 | return args
15 |
16 |
17 | def query_chaps(book_no):
18 | print('未输入卷号,将返回书籍目录信息......')
19 | editer = Editer(root_path='./out', book_no=book_no)
20 | print('*******************************')
21 | print(editer.title, editer.author)
22 | print('*******************************')
23 | editer.get_chap_list()
24 | print('*******************************')
25 | print('请输入所需要的卷号进行下载(多卷可以用英文逗号分隔或直接使用连字符,详情见说明)')
26 |
27 | temp_path = ''
28 |
29 | def delete_tmp():
30 | print(temp_path)
31 | if os.path.exists(temp_path):
32 | shutil.rmtree(temp_path)
33 |
34 | def download_single_volume(root_path,
35 | book_no,
36 | volume_no,
37 | is_gui=False,
38 | hang_signal=None,
39 | progressring_signal=None,
40 | cover_signal=None,
41 | edit_line_hang=None):
42 |
43 | editer = Editer(root_path=root_path, book_no=book_no, volume_no=volume_no)
44 | print('正在积极地获取书籍信息....')
45 | success = editer.get_index_url()
46 | if not success:
47 | print('书籍信息获取失败')
48 | return
49 | print(editer.title + '-' + editer.volume['book_name'], editer.author)
50 | print('****************************')
51 | temp_path = editer.temp_path
52 | editer.check_volume(is_gui=is_gui, signal=hang_signal, editline=edit_line_hang)
53 | print('正在下载文本....')
54 | print('*********************')
55 | editer.get_text()
56 | print('*********************')
57 |
58 | print('正在下载插图.....................................')
59 | editer.get_image(is_gui=is_gui, signal=progressring_signal)
60 |
61 | print('正在编辑元数据....')
62 | editer.get_cover(is_gui=is_gui, signal=cover_signal)
63 | editer.get_toc()
64 | editer.get_content()
65 | editer.get_epub_head()
66 |
67 | print('正在生成电子书....')
68 | epub_file = editer.get_epub()
69 | print('生成成功!', f'电子书路径【{epub_file}】')
70 |
71 |
72 | def downloader_router(root_path,
73 | book_no,
74 | volume_no,
75 | is_gui=False,
76 | hang_signal=None,
77 | progressring_signal=None,
78 | cover_signal=None,
79 | edit_line_hang=None):
80 | is_multi_chap = False
81 | if len(book_no)==0:
82 | print('请检查输入是否完整正确!')
83 | return
84 | elif volume_no == '':
85 | query_chaps(book_no)
86 | return
87 | elif volume_no.isdigit():
88 | volume_no = int(volume_no)
89 | if volume_no<=0:
90 | print('请检查输入是否完整正确!')
91 | return
92 | elif "-" in volume_no:
93 | start, end = map(str, volume_no.split("-"))
94 | if start.isdigit() and end.isdigit() and int(start)>0 and int(start) None:
35 | result = super().terminate()
36 | return result
37 |
38 | class EmittingStr(QObject):
39 | textWritten = pyqtSignal(str) # 定义一个发送str的信号
40 | def write(self, text):
41 | self.textWritten.emit(str(text))
42 | def flush(self):
43 | pass
44 | def isatty(self):
45 | pass
46 |
47 | class SettingWidget(QFrame):
48 | def __init__(self, text: str, parent=None):
49 | super().__init__(parent=parent)
50 |
51 | self.parent = parent
52 | self.expandLayout = ExpandLayout(self)
53 | self.setObjectName(text.replace(' ', '-'))
54 | self.setting_group = SettingCardGroup(self.tr("下载设置"), self)
55 |
56 | self.download_path_card = PushSettingCard(
57 | self.tr('选择文件夹'),
58 | FIF.DOWNLOAD,
59 | self.tr("下载目录"),
60 | self.parent.out_path,
61 | self.setting_group
62 | )
63 | self.themeMode = OptionsConfigItem(
64 | None, "ThemeMode", Theme.DARK, OptionsValidator(Theme), None)
65 |
66 | self.threadMode = OptionsConfigItem(
67 | None, "ThreadMode", True, BoolValidator())
68 |
69 | self.theme_card = OptionsSettingCard(
70 | self.themeMode,
71 | FIF.BRUSH,
72 | self.tr('应用主题'),
73 | self.tr("更改外观"),
74 | texts=[
75 | self.tr('亮'), self.tr('暗'),
76 | self.tr('跟随系统设置')
77 | ],
78 | parent=self.parent
79 | )
80 |
81 | self.setting_group.addSettingCard(self.download_path_card)
82 | self.setting_group.addSettingCard(self.theme_card)
83 | self.expandLayout.setSpacing(28)
84 | self.expandLayout.setContentsMargins(20, 10, 20, 0)
85 | self.expandLayout.addWidget(self.setting_group)
86 |
87 | self.download_path_card.clicked.connect(self.download_path_changed)
88 | self.theme_card.optionChanged.connect(self.theme_changed)
89 |
90 | def download_path_changed(self):
91 | """ download folder card clicked slot """
92 | self.parent.out_path = QFileDialog.getExistingDirectory(
93 | self, self.tr("Choose folder"), self.parent.out_path)
94 | self.download_path_card.contentLabel.setText(self.parent.out_path)
95 |
96 | def theme_changed(self):
97 | theme_name = self.theme_card.choiceLabel.text()
98 | self.parent.set_theme(theme_name)
99 | if os.path.exists('./config'):
100 | shutil.rmtree('./config')
101 |
102 |
103 |
104 |
105 | class HomeWidget(QFrame):
106 |
107 | progressring_signal = pyqtSignal(object)
108 | end_signal = pyqtSignal(object)
109 | hang_signal = pyqtSignal(object)
110 | clear_signal = pyqtSignal(object)
111 | cover_signal = pyqtSignal(object)
112 |
113 | def __init__(self, text: str, parent=None):
114 | super().__init__(parent=parent)
115 | self.setObjectName(text)
116 | self.parent = parent
117 | self.label_book = SubtitleLabel('书号:', self)
118 | self.label_volumn = SubtitleLabel('卷号:', self)
119 | self.editline_book = LineEdit(self)
120 | self.editline_volumn = LineEdit(self)
121 | validator = QRegExpValidator(QRegExp("\\d+")) # 正则表达式匹配阿拉伯数字
122 | self.editline_book.setValidator(validator)
123 | # self.editline_volumn.setValidator(validator)
124 |
125 | self.editline_book.setMaxLength(4)
126 | # self.editline_volumn.setMaxLength(2)
127 |
128 | # self.editline_book.setText('2059')
129 | # self.editline_volumn.setText('3')
130 | self.book_icon = QPixmap()
131 | self.book_icon.loadFromData(base64.b64decode(book_base64))
132 | self.cover_w, self.cover_h = 152, 230
133 |
134 | self.label_cover = ImageLabel(self.book_icon, self)
135 | self.label_cover.setBorderRadius(8, 8, 8, 8)
136 | self.label_cover.setFixedSize(self.cover_w, self.cover_h)
137 |
138 | self.text_screen = TextEdit()
139 | self.text_screen.setReadOnly(True)
140 | self.text_screen.setFixedHeight(self.cover_h)
141 |
142 | self.progressRing = ProgressRing(self)
143 | self.progressRing.setValue(0)
144 | self.progressRing.setTextVisible(True)
145 | self.progressRing.setFixedSize(50, 50)
146 |
147 | self.btn_run = PushButton('确定', self)
148 | self.btn_run.setShortcut(Qt.Key_Return)
149 | self.btn_stop = PushButton('取消', self)
150 | self.btn_hang = PushButton('确定', self)
151 |
152 | self.editline_hang = EditableComboBox(self)
153 | self.gridLayout = QGridLayout(self)
154 | self.screen_layout = QGridLayout()
155 | self.btn_layout = QGridLayout()
156 | self.hang_layout = QGridLayout()
157 |
158 | self.label_book.setFont(font_label)
159 | self.label_volumn.setFont(font_label)
160 | self.editline_book.setFont(font_label)
161 | self.editline_volumn.setFont(font_label)
162 | self.text_screen.setFont(font_msg)
163 | self.editline_hang.setFont(font_msg)
164 |
165 | self.gridLayout.addWidget(self.editline_book, 0, 1)
166 | self.gridLayout.addWidget(self.editline_volumn, 1, 1)
167 | self.gridLayout.addWidget(self.label_book, 0, 0)
168 | self.gridLayout.addWidget(self.label_volumn, 1, 0)
169 |
170 | self.gridLayout.addLayout(self.btn_layout, 2, 1, 1, 1)
171 | self.btn_layout.addWidget(self.btn_run, 2, 1)
172 | self.btn_layout.addWidget(self.btn_stop, 2, 2)
173 |
174 | self.gridLayout.addLayout(self.screen_layout, 3, 0, 2, 2)
175 |
176 | self.screen_layout.addWidget(self.progressRing, 0, 1, Qt.AlignLeft|Qt.AlignBottom)
177 | self.screen_layout.addWidget(self.text_screen, 0, 0)
178 | self.screen_layout.addWidget(self.label_cover, 0, 1)
179 |
180 |
181 |
182 | self.gridLayout.addLayout(self.hang_layout, 5, 0, 1, 2)
183 | self.hang_layout.addWidget(self.editline_hang, 0, 0)
184 | self.hang_layout.addWidget(self.btn_hang, 0, 1)
185 |
186 | self.screen_layout.setContentsMargins(0,0,0,0)
187 | self.btn_layout.setContentsMargins(0,0,0,0)
188 | self.gridLayout.setContentsMargins(20, 10, 20, 10)
189 |
190 | self.btn_run.clicked.connect(self.process_start)
191 | self.btn_stop.clicked.connect(self.process_stop)
192 | self.btn_hang.clicked.connect(self.process_continue)
193 |
194 | self.progressring_signal.connect(self.progressring_msg)
195 | self.end_signal.connect(self.process_end)
196 | self.hang_signal.connect(self.process_hang)
197 | self.clear_signal.connect(self.clear_screen)
198 | self.cover_signal.connect(self.display_cover)
199 |
200 | self.progressRing.hide()
201 | self.btn_hang.hide()
202 | self.editline_hang.hide()
203 | self.btn_stop.setEnabled(False)
204 |
205 | sys.stdout = EmittingStr(textWritten=self.outputWritten)
206 | sys.stderr = EmittingStr(textWritten=self.outputWritten)
207 | self.text_screen.setText(self.parent.welcome_text)
208 |
209 | def process_start(self):
210 | self.label_cover.setImage(self.book_icon)
211 | self.label_cover.setFixedSize(self.cover_w, self.cover_h)
212 | self.btn_run.setEnabled(False)
213 | self.btn_run.setText('正在下载')
214 | self.btn_stop.setEnabled(True)
215 | self.main_thread = MainThread(self)
216 | self.main_thread.start()
217 |
218 | def process_end(self, input=None):
219 | self.btn_run.setEnabled(True)
220 | self.btn_run.setText('开始下载')
221 | self.btn_run.setShortcut(Qt.Key_Return)
222 | self.btn_stop.setEnabled(False)
223 | self.progressRing.hide()
224 | self.btn_hang.hide()
225 | self.editline_hang.clear()
226 | self.editline_hang.hide()
227 | if input=='refresh':
228 | self.label_cover.setImage(self.book_icon)
229 | self.label_cover.setFixedSize(self.cover_w, self.cover_h)
230 | self.clear_signal.emit('')
231 | self.text_screen.setText(self.parent.welcome_text)
232 |
233 | def outputWritten(self, text):
234 | cursor = self.text_screen.textCursor()
235 | scrollbar=self.text_screen.verticalScrollBar()
236 | is_bottom = (scrollbar.value()>=scrollbar.maximum() - 15)
237 | cursor.movePosition(QTextCursor.End)
238 | cursor.insertText(text)
239 | if is_bottom:
240 | self.text_screen.setTextCursor(cursor)
241 | # self.text_screen.ensureCursorVisible()
242 |
243 | def clear_screen(self):
244 | self.text_screen.clear()
245 |
246 | def display_cover(self, signal_msg):
247 | filepath, img_h, img_w = signal_msg
248 | self.label_cover.setImage(filepath)
249 | self.label_cover.setFixedSize(int(img_w*self.cover_h/img_h), self.cover_h)
250 |
251 | def progressring_msg(self, input):
252 | if input == 'start':
253 | self.label_cover.setImage(self.book_icon)
254 | self.label_cover.setFixedSize(self.cover_w, self.cover_h)
255 | self.progressRing.show()
256 | elif input == 'end':
257 | self.progressRing.hide()
258 | self.progressRing.setValue(0)
259 | else:
260 | self.progressRing.setValue(input)
261 |
262 | def process_hang(self, input=None):
263 | self.btn_hang.setEnabled(True)
264 | self.btn_hang.setShortcut(Qt.Key_Return)
265 | self.btn_hang.show()
266 | self.editline_hang.show()
267 |
268 | def process_continue(self, input=None):
269 | self.btn_hang.hide()
270 | self.btn_hang.setEnabled(False)
271 | self.editline_hang.hide()
272 |
273 |
274 | def process_stop(self):
275 | self.main_thread.terminate()
276 | self.end_signal.emit('refresh')
277 |
278 |
279 |
280 |
281 | class Window(FluentWindow):
282 | def __init__(self):
283 | super().__init__()
284 |
285 | self.out_path = os.path.join(os.path.expanduser('~'), 'Downloads')
286 | self.head = 'https://www.wenku8.net'
287 | split_str = '**************************************\n '
288 | self.welcome_text = f'使用说明(共4条,记得下拉):\n{split_str}1.轻小说文库{self.head},根据书籍网址输入书号以及下载的卷号,书号最多输入4位阿拉伯数字。\n{split_str}2.例如小说网址是{self.head}/book/3138.htm,则书号输入3138。\n{split_str}3.要查询书籍卷号卷名等信息,则可以只输入书号不输入卷号,点击确定会返回书籍卷名称和对应的卷号。\n{split_str}4.根据上一步返回的信息确定自己想下载的卷号,要下载编号[2]对应卷,则卷号输入2。想下载多卷比如[1]至[3]对应卷,则卷号输入1-3或1,2,3(英文逗号分隔,编号也可以不连续)并点击确定。'
289 | self.homeInterface = HomeWidget('Home Interface', self)
290 | self.settingInterface = SettingWidget('Setting Interface', self)
291 | self.initNavigation()
292 | self.initWindow()
293 |
294 | def initNavigation(self):
295 | self.addSubInterface(self.homeInterface, FIF.HOME, '主界面')
296 | self.addSubInterface(self.settingInterface, FIF.SETTING, '设置', NavigationItemPosition.BOTTOM)
297 |
298 | def initWindow(self):
299 | self.resize(700, 460)
300 | pixmap = QPixmap()
301 | pixmap.loadFromData(base64.b64decode(logo_base64))
302 | self.setWindowIcon(QIcon(pixmap))
303 | self.setWindowTitle('轻小说文库EPUB下载器')
304 | self.setFont(font_label)
305 |
306 | desktop = QApplication.desktop().availableGeometry()
307 | w, h = desktop.width(), desktop.height()
308 | self.move(w//2 - self.width()//2, h//2 - self.height()//2)
309 |
310 | def set_theme(self, mode=None):
311 | if mode=='亮':
312 | setTheme(Theme.LIGHT)
313 | elif mode=='暗':
314 | setTheme(Theme.DARK)
315 | elif mode=='跟随系统设置':
316 | setTheme(Theme.AUTO)
317 | theme = qconfig.theme
318 | if theme == Theme.DARK:
319 | self.homeInterface.label_book.setTextColor(QColor(255,255,255))
320 | self.homeInterface.label_volumn.setTextColor(QColor(255,255,255))
321 | elif theme == Theme.LIGHT:
322 | self.homeInterface.label_book.setTextColor(QColor(0,0,0))
323 | self.homeInterface.label_volumn.setTextColor(QColor(0,0,0))
324 |
325 |
326 |
327 | if __name__ == '__main__':
328 | QApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
329 | QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
330 | QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
331 |
332 | setTheme(Theme.DARK)
333 | setThemeColor('#559DCD')
334 | app = QApplication(sys.argv)
335 | w = Window()
336 | w.show()
337 | app.exec_()
338 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | # pip install -r requirements.txt -i https://pypi.org/simple/
2 | requests
3 | bs4
4 | rich
5 | pyqt5
6 | PyQt-Fluent-Widgets[full]
7 | selenium
--------------------------------------------------------------------------------
/resource/book.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShqWW/lightnovel-download/f42ff913a6ef9b1dd0e2331ac77a80a5b4d89bba/resource/book.png
--------------------------------------------------------------------------------
/resource/book.py:
--------------------------------------------------------------------------------
1 | book_base64 = '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'
--------------------------------------------------------------------------------
/resource/example1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShqWW/lightnovel-download/f42ff913a6ef9b1dd0e2331ac77a80a5b4d89bba/resource/example1.png
--------------------------------------------------------------------------------
/resource/example2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShqWW/lightnovel-download/f42ff913a6ef9b1dd0e2331ac77a80a5b4d89bba/resource/example2.png
--------------------------------------------------------------------------------
/resource/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShqWW/lightnovel-download/f42ff913a6ef9b1dd0e2331ac77a80a5b4d89bba/resource/logo.png
--------------------------------------------------------------------------------
/resource/logo.py:
--------------------------------------------------------------------------------
1 | logo_base64 = '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'
--------------------------------------------------------------------------------
/resource/logo_big.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShqWW/lightnovel-download/f42ff913a6ef9b1dd0e2331ac77a80a5b4d89bba/resource/logo_big.png
--------------------------------------------------------------------------------
/resource/trans_base64.py:
--------------------------------------------------------------------------------
1 | from PIL import Image
2 | import base64
3 | # from resource.logo import logo_base64
4 | import io
5 |
6 | # # 从Base64编码数据中获取图像数据
7 | # image_bytes = base64.b64decode(logo_base64)
8 |
9 | # # 将图像数据解码为Image对象
10 | # image = Image.open(io.BytesIO(image_bytes))
11 |
12 | # # 显示图像
13 | # image.show()
14 |
15 |
16 | def image_to_base64(image_path):
17 | with open(image_path, "rb") as image_file:
18 | encoded_string = base64.b64encode(image_file.read())
19 | return encoded_string.decode("utf-8")
20 |
21 | image_path = "resource/logo.png "
22 | base64_string = image_to_base64(image_path)
23 | print(base64_string)
--------------------------------------------------------------------------------
/utils.py:
--------------------------------------------------------------------------------
1 | def get_cover_html(img_w, img_h):
2 | img_htmls = []
3 | img_msg = ' \n'
4 | img_htmls.append('\n')
5 | img_htmls.append('\n')
7 | img_htmls.append('\n')
8 | img_htmls.append('\n')
9 | img_htmls.append(' Cover\n')
10 | img_htmls.append('\n')
11 | img_htmls.append('\n')
12 | img_htmls.append(' \n')
13 | img_htmls.append(' \n')
16 | img_htmls.append('
\n')
17 | img_htmls.append('\n')
18 | img_htmls.append('')
19 | return img_htmls
20 |
21 |
22 | def text2htmls(chap_name, text):
23 | text_lines = text.split('\n')
24 | text_body = []
25 | text_body.append('\n')
26 | text_body.append('' + chap_name + '
\n')
27 | for text_line in text_lines:
28 | if text_line.startswith('[img:'):
29 | img_no = text_line[5:7]
30 | text_line_html = f'
\n'
31 | else:
32 | text_line_html = '' + text_line + '
\n'
33 | text_body.append(text_line_html)
34 | text_body.append('\n')
35 | text_head = []
36 | text_head.append('\n')
37 | text_head.append('\n')
39 | text_head.append('\n')
40 | text_head.append('\n')
41 | text_head.append(''+ chap_name+'\n')
42 | text_head.append('\n')
43 | text_head.append('\n')
44 | text_htmls = text_head + text_body + ['']
45 | return text_htmls
46 |
47 | def get_toc_html(title, chap_names):
48 | toc_htmls = []
49 | toc_htmls.append('\n')
50 | toc_htmls.append('\n\n')
52 | toc_htmls.append('\n')
53 | toc_htmls.append(' \n')
54 | toc_htmls.append(' \n')
55 | toc_htmls.append(' \n')
56 | toc_htmls.append(' \n')
57 | toc_htmls.append(' \n')
58 | toc_htmls.append(' \n')
59 | toc_htmls.append('\n')
60 | toc_htmls.append(' '+ title +'\n')
61 | toc_htmls.append('\n')
62 | toc_htmls.append('\n')
63 | for chap_no, chap_name in enumerate(chap_names):
64 | toc_htmls.append(' \n')
65 | toc_htmls.append(' \n')
66 | toc_htmls.append(' '+ chap_name +'\n')
67 | toc_htmls.append(' \n')
68 | toc_htmls.append(' \n')
69 | toc_htmls.append(' \n')
70 | toc_htmls.append('\n')
71 | toc_htmls.append('')
72 | return toc_htmls
73 |
74 |
75 | def get_content_html(title, author, num_chap, num_img, img_exist=False):
76 | content_htmls = []
77 | content_htmls.append('\n')
78 | content_htmls.append('\n')
79 | content_htmls.append(' \n')
80 | content_htmls.append(' urn:uuid:942b8224-476b-463b-9078-cdfab0ee2686\n')
81 | content_htmls.append(' zh\n')
82 | content_htmls.append(' '+ title +'\n')
83 | content_htmls.append(' '+ author +'\n')
84 | content_htmls.append(' \n')
85 | content_htmls.append(' \n')
86 | content_htmls.append(' \n')
87 | content_htmls.append(' \n')
88 | content_htmls.append(' \n')
89 | if img_exist:
90 | content_htmls.append(' \n')
91 | for chap_no in range(num_chap):
92 | content_htmls.append(' - \n')
93 |
94 |
95 | for img_no in range(num_img):
96 | content_htmls.append('
- \n')
97 |
98 | content_htmls.append('
\n')
99 | content_htmls.append(' \n')
100 |
101 |
102 | content_htmls.append(' \n')
103 | content_htmls.append(' \n')
104 | for chap_no in range(num_chap):
105 | content_htmls.append(' \n')
106 |
107 | content_htmls.append(' \n')
108 | content_htmls.append(' \n')
109 | content_htmls.append(' \n')
110 | content_htmls.append(' \n')
111 | content_htmls.append('\n')
112 | return content_htmls
113 |
114 |
115 | def get_container_html():
116 | container_htmls = []
117 | container_htmls.append('\n')
118 | container_htmls.append('\n')
119 | container_htmls.append(' \n')
120 | container_htmls.append(' \n')
121 | container_htmls.append(' \n')
122 | container_htmls.append('\n')
123 | return container_htmls
124 |
125 |
126 | def get_color_html(colorimg_num):
127 | color_htmls = []
128 | color_htmls.append('\n')
129 | color_htmls.append('\n')
130 | color_htmls.append('\n')
131 |
132 | color_htmls.append(' 彩插\n')
133 | color_htmls.append('\n')
134 | color_htmls.append('\n')
135 | for i in range(1, colorimg_num):
136 | color_htmls.append('
\n')
137 | color_htmls.append('\n')
138 | color_htmls.append('')
139 | return color_htmls
140 |
141 | def check_chars(win_chars):
142 | win_illegal_chars = '?*"<>|:/\\'
143 | new_chars = ''
144 | for char in win_chars:
145 | if char in win_illegal_chars:
146 | new_chars += '\u25A0'
147 | else:
148 | new_chars += char
149 | return new_chars
150 |
--------------------------------------------------------------------------------