Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Develop #127

Merged
merged 20 commits into from
Mar 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
069a6cd
:bug: 修复章节名称带方括号识别库存异常
shadlc Dec 7, 2023
b5b2177
🐛 修复B站二维码登入请求412异常;以及子线程单独调用QMessageBox引起的parent错误
Zeal-L Dec 9, 2023
773e7ca
:sparkles: 如果配置文件json解析异常,捕获并告知,然后正常启动,而不是抛错
shadlc Jan 1, 2024
49c42e0
:art: 经过测试,完善对MacOS的支持
shadlc Jan 23, 2024
0bfd907
:sparkles: cbz格式多作者识别优化
shadlc Jan 27, 2024
75fffee
🎨 Linter 由原来的 black 改为 ruff
Zeal-L Feb 25, 2024
e597d84
🐛 Update setup.sh command in workflow
Zeal-L Feb 25, 2024
3d2bc2c
✨ 添加 mac 环境下 的窗口检测(多开)
Zeal-L Feb 25, 2024
ad26bd0
🎨 在UI中添加exif的设置选项
Zeal-L Feb 27, 2024
bf3f5eb
✨ 实现 exif 设置相关逻辑;更新版权信息
Zeal-L Feb 27, 2024
bb79372
⬆️ 更新依赖
Zeal-L Feb 27, 2024
7cfe25d
🐛尽力修正标题的错误替换
spr-equinox Mar 23, 2024
b579894
🐛修复一个运行时错误
spr-equinox Mar 23, 2024
4af7a60
Merge pull request #122 from spr-equinox/fix-title
Zeal-L Mar 25, 2024
0a67afd
🎨换用更好的写法
spr-equinox Mar 25, 2024
c6566f6
Merge pull request #123 from spr-equinox/fix-runtime-error
Zeal-L Mar 26, 2024
a11b9d7
修复BiliPlus Cookie (#126)
dataacat Mar 30, 2024
655c49f
⬆️ 更新依赖
Zeal-L Mar 30, 2024
e16dcc7
✨ 为序号功能做好前置准备
Zeal-L Mar 30, 2024
0075418
Merge branch 'main' of https://github.com/Zeal-L/BiliBili-Manga-Downl…
Zeal-L Mar 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/pylint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
sh setup.sh
bash setup.sh

- name: Analysing the code with pylint
id: pylint
- name: Analysing the code with ruff
id: ruff
run: |
poetry run pylint $(git ls-files '*.py' | grep -v "PySide_src")
poetry run ruff format -q $(git ls-files '*.py' | grep -v "PySide_src")
continue-on-error: true
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
- **特别提示: 毕竟是要提供 Cookie 给第三方网站托管,因此可能会有潜在的安全风险。敏感人群请不要使用自己主账号的 Cookie**
- `BiliPlus` 的 `Cookie` 获取方法跟上述一致,在 [ComicWebReader](https://www.biliplus.com/manga/) 登入后在开发者工具中找到 `access_key` 粘贴到程序设置选项中的 `BiliPlus Cookie` 即可

- **兼容性:支持Windows与Linux平台,MacOs需要自行编译,发现问题的欢迎提Issues**
- **兼容性:支持Windows、MacOS与Linux平台,发现问题的欢迎提Issues**
- **搜索 / 选择章节 / 下载 的功能介绍我想已经不言而喻了,这就是图形化界面的好处!**
- **值得注意的是:本软件不支持断点续传和下载任务缓存的功能 ~~(毕竟一章漫画太小了,好像也没什么必要,断了不如重下)~~,所以请确保不要在下载中途关闭!**
- **程序缓存和日志历史文件存在 `C:\Users\AppData\Roaming\BiliBili-Manga-Downloader\` 目录下,可以通过"清空用户数据"功能一键删除**
Expand Down
33 changes: 31 additions & 2 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,48 @@


import ctypes
import subprocess
from sys import argv, exit, platform

from PySide6.QtWidgets import QApplication, QMessageBox

from src.ui.MainGUI import MainGUI
from src.Utils import __main_window_title__, __version__
from src.Utils import __main_window_title__, logger

if __name__ == "__main__":
app = QApplication.instance() or QApplication(argv)

if platform == "win32" and ctypes.windll.user32.FindWindowW(None, __main_window_title__) != 0:
box = QMessageBox.information(None, "提示", "有一个我已经不满足不了你吗?\n\t...(。•ˇ‸ˇ•。) ...")
box = QMessageBox.information(
None, "提示", "有一个我已经不满足不了你吗?\n\t...(。•ˇ‸ˇ•。) ..."
)
exit(0)
elif platform == "darwin":
script = """
set windowTitle to "{}"
tell application "System Events"
set listOfProcesses to every process whose visible is true
repeat with proc in listOfProcesses
try
if exists (window 1 of proc where the name contains windowTitle) then
return true
end if
end try
end repeat
end tell
return false
""".format(__main_window_title__)

try:
output = subprocess.check_output(["osascript", "-e", script], text=True).strip()
if output == "true":
QMessageBox.information(
None, "提示", "有一个我已经不满足不了你吗?\n\t...(。•ˇ‸ˇ•。) ..."
)
exit(1)
except subprocess.CalledProcessError as e:
logger.error("检查是否有重复窗口时出错:", e)
exit(1)

window = MainGUI(app)
window.show()
Expand Down
Empty file modified build.sh
100644 → 100755
Empty file.
1,183 changes: 568 additions & 615 deletions poetry.lock

Large diffs are not rendered by default.

43 changes: 21 additions & 22 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,40 +1,39 @@
[tool.poetry]
name = "bilibili-manga-downloader"
version = "1.5.0"
description = "一个好用的哔哩哔哩漫画下载器,拥有图形界面,支持关键词搜索漫画和二维码登入,黑科技下载未解锁章节,多线程下载,多种保存格式,本地漫画管理,一键检查更新!"
authors = ["Zeal-L <[email protected]>"]
description = "一个好用的哔哩哔哩漫画下载器,拥有图形界面,支持关键词搜索漫画和二维码登入,黑科技下载未解锁章节,多线程下载,多种保存格式,本地漫画管理,一键检查更新!"
license = "AGPL-3.0"
name = "bilibili-manga-downloader"
readme = "README.md"
version = "1.5.1"

[tool.poetry.dependencies]
python = ">=3.12,<3.13"
beautifulsoup4 = "^4.12.3"
piexif = "^1.1.3"
pillow = "10.2.0"
py7zr = "^0.20.8"
pypdf2 = "^3.0.1"
pyside6 = "^6.6.0"
requests = "^2.31.0"
retrying = "^1.3.4"
pillow = "^10.1.0"
pypdf = "^4.0.2"
pypinyin = "^0.49.0"
qt-material = "^2.14"
beautifulsoup4 = "^4.12.2"
pyside6 = "^6.6.2"
python = ">=3.12,<3.13"
qrcode = "^7.4.2"
qt-material = "^2.14"
requests = "^2.31.0"
retrying = "^1.3.4"

[tool.poetry.group.dev.dependencies]
auto-py-to-exe = "^2.42.0"
pyinstaller = "^6.4.0"
rich = "^13.7.0"
ruff = "^0.2.2"

[[tool.poetry.source]]
name = "mirrors"
url = "https://pypi.tuna.tsinghua.edu.cn/simple/"
priority = "supplemental"


[tool.poetry.group.dev.dependencies]
rich = "^13.7.0"
pylint = "^3.0.2"
pyinstaller = "^6.2.0"
auto-py-to-exe = "^2.42.0"
url = "https://pypi.tuna.tsinghua.edu.cn/simple/"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
requires = ["poetry-core"]

[tool.black]
line-length = 100
[tool.ruff]
line-length = 100
15 changes: 12 additions & 3 deletions setup.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,18 @@ poetry run pyside6-uic src/ui/PySide_src/myAbout.ui -o src/ui/PySide_src/myAbout
poetry run pyside6-uic src/ui/PySide_src/qrCode.ui -o src/ui/PySide_src/qrCode_ui.py

echo -e "\033[34m\n 修复UI文件中的导入问题 ... \n\033[0m"
sed -i 's/resource_rc/src.ui.PySide_src.resource_rc/' src/ui/PySide_src/mainWindow_ui.py
sed -i 's/resource_rc/src.ui.PySide_src.resource_rc/' src/ui/PySide_src/myAbout_ui.py
sed -i 's/resource_rc/src.ui.PySide_src.resource_rc/' src/ui/PySide_src/qrCode_ui.py
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS
SED_INPLACE=(-i '')
else
# 其他环境,假设使用 GNU sed
SED_INPLACE=(-i)
fi

sed "${SED_INPLACE[@]}" 's/resource_rc/src.ui.PySide_src.resource_rc/' src/ui/PySide_src/mainWindow_ui.py
sed "${SED_INPLACE[@]}" 's/resource_rc/src.ui.PySide_src.resource_rc/' src/ui/PySide_src/myAbout_ui.py
sed "${SED_INPLACE[@]}" 's/resource_rc/src.ui.PySide_src.resource_rc/' src/ui/PySide_src/qrCode_ui.py


echo -e "\033[34m\n 显示虚拟环境相关信息 ... \n\033[0m"
poetry debug info
Expand Down
35 changes: 21 additions & 14 deletions src/BiliPlus.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def __init__(self, comic_id: int, mainGUI: MainGUI) -> None:
self.access_key = mainGUI.getConfig("biliplus_cookie")
self.headers = {
"User-Agent": f"{__app_name__}/{__version__}",
"cookie": f"manga_pic_format=jpg-full;login=2;access_key={self.access_key}",
"cookie": f"manga_pic_format=jpg-full;manga_sharing=on;login=2;access_key={self.access_key}",
}

############################################################
Expand All @@ -51,13 +51,9 @@ def getEpisodesInfo(self) -> list[Episode]:
# ?###########################################################
# ? 解析 Biliplus 章节
biliplus_ep_list = self.data["ep_list"]
for episode in reversed(biliplus_ep_list):
for idx, episode in enumerate(reversed(biliplus_ep_list), start=1):
epi = BiliPlusEpisode(
episode,
self.headers,
self.comic_id,
self.data,
self.mainGUI,
episode, self.headers, self.comic_id, self.data, self.mainGUI, idx
)
self.episodes.append(epi)
if epi.isDownloaded():
Expand Down Expand Up @@ -97,7 +93,7 @@ def _(url: str) -> str | None:

try:
biliplus_html = _(biliplus_detail_url)
if None == biliplus_html:
if None is biliplus_html:
self.mainGUI.signal_message_box.emit(
"BiliPlus无法解析任何章节,可能有如下两种可能\n"
"1、您的BiliPlus Cookie无效,请更新您的BiliPlus Cookie\n"
Expand All @@ -123,7 +119,9 @@ def _(url: str) -> str | None:
total_ep = total_ep_element.contents[0].split("/")[1]
total_pages = int(int(total_ep) / 200) + 1
for pages in range(2, total_pages + 1):
self.mainGUI.signal_resolve_status.emit(f"正在解析漫画章节({pages}/{total_pages})...")
self.mainGUI.signal_resolve_status.emit(
f"正在解析漫画章节({pages}/{total_pages})..."
)
page_html = _(f"{biliplus_detail_url}&page={pages}")
document = BeautifulSoup(page_html, "html.parser")
ep_items = document.find_all("div", {"class": "episode-item"})
Expand Down Expand Up @@ -152,8 +150,9 @@ def __init__(
comic_id: str,
comic_info: dict,
mainGUI: MainGUI,
idx: int,
) -> None:
super().__init__(episode, comic_id, comic_info, mainGUI)
super().__init__(episode, comic_id, comic_info, mainGUI, idx)
self.headers = headers
self.comic_id = comic_id

Expand All @@ -180,7 +179,9 @@ def _() -> list[dict]:
timeout=TIMEOUT_SMALL,
)
except requests.RequestException as e:
logger.warning(f"《{self.comic_name}》章节:{self.title},从BiliPlus获取图片列表失败! 重试中...\n{e}")
logger.warning(
f"《{self.comic_name}》章节:{self.title},从BiliPlus获取图片列表失败! 重试中...\n{e}"
)
raise e
if res.status_code != 200:
logger.warning(
Expand All @@ -193,7 +194,9 @@ def _() -> list[dict]:
try:
biliplus_html = _()
except requests.RequestException as e:
logger.error(f"《{self.comic_name}》章节:{self.title} 从BiliPlus重复获取图片列表多次后失败!,跳过!\n{e}")
logger.error(
f"《{self.comic_name}》章节:{self.title} 从BiliPlus重复获取图片列表多次后失败!,跳过!\n{e}"
)
logger.exception(e)
self.mainGUI.signal_message_box.emit(
f"《{self.comic_name}》章节:{self.title} 从BiliPlus重复获取图片列表多次后失败!\n"
Expand All @@ -215,13 +218,17 @@ def _() -> list[dict]:
biliplus_imgs_token.append({"url": url, "token": token})
self.imgs_token = biliplus_imgs_token
if not biliplus_imgs_token:
logger.error(f"《{self.comic_name}》章节:{self.title} 在处理BiliPlus地址时因获取的Token无效导致失败!")
logger.error(
f"《{self.comic_name}》章节:{self.title} 在处理BiliPlus地址时因获取的Token无效导致失败!"
)
self.mainGUI.signal_message_box.emit(
f"《{self.comic_name}》章节:{self.title} 在处理BiliPlus解锁章节图片地址时因获取的Token无效导致失败!"
)
return False
except requests.RequestException as e:
logger.error(f"《{self.comic_name}》章节:{self.title} 在处理BiliPlus解锁章节图片地址时失败!\n{e}")
logger.error(
f"《{self.comic_name}》章节:{self.title} 在处理BiliPlus解锁章节图片地址时失败!\n{e}"
)
logger.exception(e)
self.mainGUI.signal_message_box.emit(
f"《{self.comic_name}》章节:{self.title} 在处理BiliPlus解锁章节图片地址时失败!\n\n"
Expand Down
29 changes: 21 additions & 8 deletions src/BiliQrCode.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ def __init__(self, mainGUI: MainGUI) -> None:
self.code_url = None
self.qrcode_key = None
self.close_flag = False
self.headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
"origin": "https://manga.bilibili.com",
}

def generate(self) -> str | None:
"""生成登入二维码
Expand All @@ -42,12 +46,14 @@ def generate(self) -> str | None:
@retry(stop_max_delay=MAX_RETRY_SMALL, wait_exponential_multiplier=RETRY_WAIT_EX)
def _() -> dict:
try:
res = requests.get(self.generate_url, timeout=TIMEOUT_SMALL)
res = requests.get(self.generate_url, headers=self.headers, timeout=TIMEOUT_SMALL)
except requests.RequestException as e:
logger.warning(f"获取登入二维码失败! 重试中...\n {e}")
raise e
if res.status_code != 200:
logger.warning(f"获取登入二维码失败! 状态码:{res.status_code}, 理由: {res.reason} 重试中...")
logger.warning(
f"获取登入二维码失败! 状态码:{res.status_code}, 理由: {res.reason} 重试中..."
)
raise requests.HTTPError()
return res.json()["data"]

Expand All @@ -57,11 +63,13 @@ def _() -> dict:
data = _()
self.code_url = data["url"]
self.qrcode_key = data["qrcode_key"]
except requests.RequestException as e:
except (requests.RequestException, requests.HTTPError) as e:
logger.error(f"重复获取登入二维码多次后失败! {e}")
logger.exception(e)
QMessageBox.warning(
self.mainGUI, "警告", "重复获取登入二维码多次后失败!\n请检查网络连接或者重启软件!\n\n更多详细信息请查看日志文件"
self.mainGUI,
"警告",
"重复获取登入二维码多次后失败!\n请检查网络连接或者重启软件!\n\n更多详细信息请查看日志文件",
)
return None

Expand All @@ -85,6 +93,7 @@ def _() -> dict:
try:
res = requests.get(
self.poll_url,
headers=self.headers,
params={
"qrcode_key": self.qrcode_key,
},
Expand All @@ -94,16 +103,20 @@ def _() -> dict:
logger.warning(f"确认二维码登入失败! 重试中...\n {e}")
raise e
if res.status_code != 200:
logger.warning(f"确认二维码登入失败! 状态码:{res.status_code}, 理由: {res.reason} 重试中...")
logger.warning(
f"确认二维码登入失败! 状态码:{res.status_code}, 理由: {res.reason} 重试中..."
)
raise requests.HTTPError()
return res.json()["data"]

try:
data = _()
except requests.RequestException as e:
except (requests.RequestException, requests.HTTPError) as e:
logger.error(f"重复确认登入多次后失败! {e}")
logger.exception(e)
QMessageBox.warning(self.mainGUI, "警告", "重复确认登入多次后失败!\n请检查网络连接或者重启软件!\n\n更多详细信息请查看日志文件")
self.mainGUI.signal_message_box.emit(
"重复确认登入多次后失败!\n请检查网络连接或者重启软件!\n\n更多详细信息请查看日志文件"
)
return None

return data
Expand All @@ -120,7 +133,7 @@ def get_cookie(self, qr_res: SignalInstance) -> None:
data = self.confirm()

# 扫码登录成功或者二维码过期或者请求失败
if data["code"] in [0, 86038] or data is None:
if data is None or data["code"] in [0, 86038]:
qr_res.emit(data)
break

Expand Down
20 changes: 14 additions & 6 deletions src/Comic.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ def _() -> dict:
# ? 解析漫画信息
self.data["title"] = myStrFilter(self.data["title"])
self.data["author_name"] = ",".join(self.data["author_name"])
self.data["author_name"] = self.data["author_name"].replace("作者:", "").replace("出品:", "")
self.data["author_name"] = (
self.data["author_name"].replace("作者:", "").replace("出品:", "")
)
self.data["author_name"] = myStrFilter(self.data["author_name"])
self.data["styles"] = ",".join(self.data["styles"])
if self.comic_id in self.mainGUI.my_library:
Expand All @@ -112,11 +114,15 @@ def _() -> bytes:
logger.warning(f"获取封面图片失败! 重试中...\n{e}")
raise e
if res.status_code != 200:
logger.warning(f"获取封面图片失败! 状态码:{res.status_code}, 理由: {res.reason} 重试中...")
logger.warning(
f"获取封面图片失败! 状态码:{res.status_code}, 理由: {res.reason} 重试中..."
)
raise requests.HTTPError()
isValid, md5 = isCheckSumValid(res.headers["Etag"], res.content)
if not isValid:
logger.warning(f"图片内容 Checksum 不正确! 重试中...\n\t{res.headers['Etag']} ≠ {md5}")
logger.warning(
f"图片内容 Checksum 不正确! 重试中...\n\t{res.headers['Etag']} ≠ {md5}"
)
raise requests.HTTPError()
return res.content

Expand All @@ -127,7 +133,9 @@ def _() -> bytes:
except RetryError as e:
logger.error(f"获取封面图片多次后失败,跳过!\n{e}")
self.mainGUI.signal_message_box.emit(
"获取封面图片多次后失败!\n" "请检查网络连接或者重启软件!\n\n" "更多详细信息请查看日志文件, 或联系开发者!"
"获取封面图片多次后失败!\n"
"请检查网络连接或者重启软件!\n\n"
"更多详细信息请查看日志文件, 或联系开发者!"
)
return open(":/imgs/fail_img.jpg", encoding="utf-8")

Expand All @@ -146,8 +154,8 @@ def getEpisodesInfo(self) -> list[Episode]:
# ?###########################################################
# ? 解析章节
ep_list = self.data["ep_list"]
for episode in reversed(ep_list):
epi = Episode(episode, self.comic_id, self.data, self.mainGUI)
for idx, episode in enumerate(reversed(ep_list), start=1):
epi = Episode(episode, self.comic_id, self.data, self.mainGUI, idx)
self.episodes.append(epi)
if epi.isDownloaded():
self.num_downloaded += 1
Expand Down
Loading
Loading