Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
30 changes: 0 additions & 30 deletions .github/workflows/pypi.yml

This file was deleted.

64 changes: 61 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,73 @@
name: Release
name: Release
permissions:
contents: write
pull-requests: write
id-token: write
on:
push:
tags:
- 'v*.*.*'
- 'v*.*.*'
workflow_dispatch:

jobs:
build:
validate-and-publish-pypi:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 1

- name: Install uv
uses: astral-sh/setup-uv@v4
with:
enable-cache: false

- name: Build package
run: uv build

- name: Smoke test wheel before publish
env:
QT_QPA_PLATFORM: offscreen
CGS_CHECK_MODE: "1"
run: |
uv venv .venv-smoke
source .venv-smoke/bin/activate
uv pip install dist/*.whl
python - <<'PY'
import os
import subprocess
import sys

env = os.environ.copy()
tests = [
("entrypoint import", [sys.executable, "-c", "import CGS"]),
("check mode startup", [sys.executable, "-c", "import CGS; CGS.start()"]),
("cli help", ["cgs-cli", "--help"]),
]

failures = []
for name, cmd in tests:
print(f"::group::{name}")
result = subprocess.run(cmd, env=env, text=True, capture_output=True)
if result.stdout:
print(result.stdout)
if result.stderr:
print(result.stderr, file=sys.stderr)
print(f"exit_code={result.returncode}")
print("::endgroup::")
if result.returncode != 0:
failures.append(name)

if failures:
raise SystemExit(f"Smoke tests failed: {', '.join(failures)}")
PY

- name: Publish to PyPI
run: uv publish

create-release:
needs: validate-and-publish-pypi
runs-on: ubuntu-latest
container:
image: python:3.12
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/schedule_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ jobs:
params: "-w 3 -k ミモネル -i 2 -sp 50051"
- crawler_name: "mangabz"
params: "-w 5 -k 海贼王 -i 1 -i2 -1 -sp 50081"
- crawler_name: "h_comic"
params: "-w 8 -k ミモネル -i 2 -sp 50111"

steps:
- name: Use Python 3.13
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,11 @@ docs/*.html
deploy/launcher/mac/*.html
# self/bug-report
_bug_log
# ide
# AI
.cursor
.claude
.kilocode
/plans
.ace-tool/
openspec/
nul
91 changes: 72 additions & 19 deletions CGS.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,84 @@
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
import os
import sys
import traceback
from datetime import datetime
from multiprocessing import freeze_support
from pathlib import Path

from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import Qt

# 自己项目用到的
from GUI.gui import SpiderGUI
import GUI.src.material_ct
from PyQt5.QtWidgets import QApplication, QMessageBox

# from multiprocessing.managers import RemoteError
# sys.setrecursionlimit(5000)


def _append_fatal_log(phase, trace_text):
log_path = Path.cwd().joinpath("cgs_fatal.log")
timestamp = datetime.now().isoformat()
payload = (
f"\n=== Fatal error at {timestamp} ({phase}) ===\n"
f"python: {sys.executable}\n"
f"cwd: {Path.cwd()}\n"
f"{trace_text}"
)
try:
with open(log_path, "a", encoding="utf-8") as log_file:
log_file.write(payload)
except OSError:
pass
return log_path


def _raise_with_context(exc, phase):
trace_text = "".join(traceback.format_exception(type(exc), exc, exc.__traceback__))
log_path = _append_fatal_log(phase, trace_text)
msg = (
f"[CGS Fatal] startup failed\n"
f"phase: {phase}\n"
f"error: {type(exc).__name__}: {exc}\n\n"
f"trace log: {log_path}\n"
)
if sys.stderr is not None:
try:
sys.stderr.write("\n" + msg + "\n")
except OSError:
pass
app = QApplication.instance() or QApplication(sys.argv)
box = QMessageBox()
box.setWindowFlags(box.windowFlags() | Qt.WindowStaysOnTopHint)
box.setIcon(QMessageBox.Critical)
box.setWindowTitle("CGS Fatal Error")
box.setText(msg)
box.exec_()
raise


def start():
freeze_support()
QApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
app = QApplication(sys.argv)
app.setStyle("Fusion")
app.setPalette(app.style().standardPalette())
ui = SpiderGUI()
sys.excepthook = ui.hook_exception
QApplication.processEvents()
app.exec_()


if __name__ == '__main__':
try:
from GUI.gui import SpiderGUI
import GUI.src.material_ct # noqa: F401
except Exception as exc:
_raise_with_context(exc, "import GUI modules")

if os.environ.get("CGS_CHECK_MODE") == "1":
return

try:
QApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
app = QApplication(sys.argv)
app.setStyle("Fusion")
app.setPalette(app.style().standardPalette())
ui = SpiderGUI()
sys.excepthook = ui.hook_exception
QApplication.processEvents()
app.exec_()
except Exception as exc:
_raise_with_context(exc, "initialize QApplication")


if __name__ == "__main__":
start()
6 changes: 3 additions & 3 deletions ComicSpider/pipelines.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from scrapy.pipelines.images import ImagesPipeline, ImageException

from utils import conf, TaskObj
from utils.core import sanitize_for_path
from utils.website import JmUtils, MangabzUtils, set_author_ahead
from utils.config.rule import CgsRuleMgr
from assets import res
Expand All @@ -34,7 +35,6 @@ def __call__(self, taskid, page, info):

class ComicPipeline(ImagesPipeline):
err_flag = 0
_sub = re.compile(r'([|:<>?*"\\/])')
_sub_index = re.compile(r"^\(.*?\)")

@classmethod
Expand All @@ -45,8 +45,8 @@ def from_crawler(cls, crawler):

# 图片存储前调用
def file_path(self, request, response=None, info=None, *, item=None):
title = self._sub.sub('-', item.get('title'))
section = self._sub.sub('-', item.get('section') or '')
title = sanitize_for_path(item.get('title'))
section = sanitize_for_path(item.get('section') or '')
taskid = item.get('uuid_md5')
page = self.page_naming(taskid, item.get('page'), info)
spider = self.spiderinfo.spider
Expand Down
4 changes: 2 additions & 2 deletions ComicSpider/spiders/basecomicspider.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ def frame_book_print(self, rets, fm=None, url=None, extra=None, make_preview=Fal
{font_color(extra, cls='theme-tip')}</p><br>""")
self("[ShowKeepBooks]") # 由于 keep_books 现放在 gui 上,所以最后用 flag 形式触发
else:
self(f"""{'✈' * 15}
{font_color(self.res.frame_book_print_retry_tip, cls='theme-err', size=5)}""")
self(f"<br>{'✈' * 15}<br>"
f"{font_color(self.res.frame_book_print_retry_tip, cls='theme-err', size=5)}")
return rets

def frame_section_print(self, rets, fm, print_limit=5, extra=None):
Expand Down
50 changes: 50 additions & 0 deletions ComicSpider/spiders/h_comic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
from utils.website import HComicUtils
from .basecomicspider import BaseComicSpider2, font_color

domain = "h-comic.com"


class HComicSpider(BaseComicSpider2):
custom_settings = {
"DOWNLOADER_MIDDLEWARES": {
"ComicSpider.middlewares.ComicDlAllProxyMiddleware": 5,
"ComicSpider.middlewares.UAMiddleware": 6,
"ComicSpider.middlewares.RefererMiddleware": 10,
}
}
name = "h_comic"
num_of_row = 4
domain = domain
search_url_head = f"https://{domain}/?q="
turn_page_info = (r"page=\d+",)
book_id_url = f"https://{domain}/comics/1?id=%s"
mappings = {}

@property
def ua(self):
return HComicUtils.headers

def frame_book(self, response):
frame_results = {}
self.say(self.say_fm.format("序号", "漫画名") + "<br>")
books = self.ut.parse_search(response.text)
for idx, book in enumerate(books, 1):
book.idx = idx
frame_results[idx] = book
return self.say.frame_book_print(frame_results, url=response.url, make_preview=True)

def frame_section(self, response):
book = self.ut.parse_book(response.text)
pages = int(book.pages or 0)
if pages <= 0:
self.say(font_color("未解析到页面信息,请稍后重试", cls="theme-err"))
return {}
media_id = getattr(book, "media_id", "")
comic_source = getattr(book, "comic_source", "")
image_prefix = HComicUtils._get_image_prefix(comic_source)
frame_results = {}
for page in range(1, pages + 1):
frame_results[page] = f"{image_prefix}/{media_id}/pages/{page}"
self.say("📢" + font_color(" 这本已经扔进任务了", cls="theme-tip"))
return frame_results
12 changes: 5 additions & 7 deletions GUI/browser_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,15 +221,13 @@ def setupUi(self, _window):
self.set_btn()
self.set_html()
self.patch_tip()
if self.is_publish_page():
FluentMonkeyPatch.rbutton_menu_PulishPage(self)
else:
FluentMonkeyPatch.rbutton_menu_WebEngine(self)
self.set_rbtn_menu()

def is_publish_page(self) -> bool:
def set_rbtn_menu(self):
if hasattr(self.gui, 'tf') and self.gui.tf:
return 'publish' in str(self.gui.tf).lower()
return False
if 'publish' in str(self.gui.tf).lower():
return FluentMonkeyPatch.rbutton_menu_PulishPage(self)
FluentMonkeyPatch.rbutton_menu_WebEngine(self)

def _setup_frameless_chrome(self):
self.titleBar.hide()
Expand Down
3 changes: 3 additions & 0 deletions GUI/core/theme.py → GUI/core/theme/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from PyQt5.QtWidgets import QApplication
from qfluentwidgets import setTheme, Theme
from utils import conf
from .mid import create_light_mid_colors, create_dark_mid_colors, MidNodeColors


class CustTheme(Enum):
Expand Down Expand Up @@ -45,6 +46,7 @@ def __init__(self):
self.palette.setColor(QPalette.HighlightedText, QColor(255, 255, 255))
self.font_color = LightFontColor
self.c = Theme.LIGHT
self.mid_colors = create_light_mid_colors()


class Dark:
Expand All @@ -64,6 +66,7 @@ def __init__(self):
self.palette.setColor(QPalette.HighlightedText, QColor(0, 0, 0))
self.font_color = DarkFontColor
self.c = Theme.DARK
self.mid_colors = create_dark_mid_colors()


_DEFAULT_COLORS = {
Expand Down
Loading