搞了一周的全自动发文的代码。太长了,下周分一分。

import sys
import os
from PyQt6.QtWidgets import *
from PyQt6.QtCore import *
from PyQt6.QtGui import *
from config.config_manager import ConfigManager

from modules.database_handler import DatabaseHandler
from modules.logger import Logger
import win32gui
import win32con
# 在文件顶部添加导入
import asyncio
from playwright.async_api import async_playwright

import json

import ctypes
import win32gui
from ctypes import wintypes
class MainApp(QMainWindow):
    # 在类顶部声明信号
    load_started = pyqtSignal(str)
    load_finished = pyqtSignal(str)

    pids = {}  # 存储进程 ID
    window_handles = {}  # 存储窗口句柄
    def __init__(self):
        super().__init__()
        self.setWindowTitle("多平台发布工具")
        self.setWindowTitle("多平台发布工具")
        self.setFixedSize(980, 720)  # 固定窗口尺寸
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowType.WindowMaximizeButtonHint)  # 禁用最大化
        # 启动自动检查定时器
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.check_login_status)
        self.timer.start(60000)  # 每60秒执行一次

        # 在 __init__ 方法中添加
        self.setSizePolicy(
            QSizePolicy.Policy.Fixed,
            QSizePolicy.Policy.Expanding
        )
        self.config = ConfigManager()

        # 初始化 Playwright 相关属性
        self.playwright = None
        self.browser = None
        self.pages = {}  # 存储页面对象

        # 启动 Playwright(异步初始化)
        asyncio.get_event_loop().run_until_complete(self.start_playwright())

        # 设置主窗口尺寸
        # self.setGeometry(100, 100, 1440, 1220)
        # self.setFixedSize(980,720)

        self.current_browser = None
        self.browsers = {}

        self.create_project_dirs()


        self.logger = Logger()
        self.db = DatabaseHandler()

        self.init_ui()
        self.load_sites_config()
        # 在 __init__ 中添加:
        self.load_started.connect(self.on_load_started)
        self.load_finished.connect(self.on_load_finished)

    # 新增槽函数:
    def on_load_started(self, url):
        self.update_status(url, "正在打开")

    def on_load_finished(self, url):
        self.update_status(url, "加载完成")
        self.update_cell_color(self.get_row_by_url(url), "黄色")

    def get_row_by_url(self, url):
        for row in range(self.site_table.rowCount()):
            site_url = self.config.get_site_url(row)
            if site_url == url:
                return row
        return -1

    async def start_playwright(self):
        self.playwright = await async_playwright().start()
        self.browser = await self.playwright.chromium.launch(headless=False,
             args=[
                 # f"--app={url}",  # 应用模式
                 # "--disable-infobars",  # 隐藏自动化提示
                 # "--no-default-browser-check",  # 禁用默认浏览器检查
                 # "--disable-extensions",  # 禁用扩展
                 # "--disable-notifications",  # 禁用通知
                 # "--disable-session-crashed-bubble",  # 隐藏崩溃提示
                 # "--overscroll-history-navigation=0",  # 禁用滑动导航
                 # "--disable-features=TranslateUI",  # 禁用翻译工具栏
                 # "--hide-scrollbars",  # 隐藏滚动条
                 # "--kiosk"  # 全屏无边框(可选)
             ]
             )  # 返回 Browser 对象

    def create_project_dirs(self):
        required_dirs = [
            "cookies",  # 主目录
            "scripts",
            "user_data"  # 用户数据主目录
        ]
        for d in required_dirs:
            dir_path = os.path.join(os.getcwd(), d)
            os.makedirs(dir_path, exist_ok=True)

        # 为每个站点创建子目录(如user_data/douban)
        sites = self.config.load_sites()
        for site in sites:
            user_data_dir = site.get('user_data_dir')
            if user_data_dir:
                full_path = os.path.join(os.getcwd(), user_data_dir)
                os.makedirs(full_path, exist_ok=True)

    def init_ui(self):
        print("初始化UI...")
        main_widget = QWidget()
        main_layout = QHBoxLayout(main_widget)
        # main_layout.setContentsMargins(1, 1, 1, 1)  # ✅ 清除主布局的边距


        # 左侧配置表格
        self.site_table = QTableWidget()
        self.site_table.setColumnCount(4)
        self.site_table.setHorizontalHeaderLabels(['选择', '网站', '状态', '操作'])
        self.site_table.setColumnWidth(0, 30)
        self.site_table.setColumnWidth(1, 50)
        self.site_table.setColumnWidth(2, 80)
        self.site_table.setColumnWidth(3, 80)
        # 计算左侧表格宽度
        left_table_width = 230
        self.site_table.setFixedWidth(left_table_width)



        # 右侧信息框
        self.status_box = QTextEdit()
        self.status_box.setStyleSheet("background-color: #000; color: #0f0;")
        self.status_box.setReadOnly(True)
        # # 计算信息框高度
        status_box_height = 720 - 540 - 80  # 主窗口高度720 - 浏览器高度540 - 按钮布局高度30
        self.status_box.setFixedHeight(status_box_height)




        right_layout = QVBoxLayout()

        right_layout.addWidget(self.status_box)
        # 添加缩放按钮和保存按钮的布局
        button_layout = QHBoxLayout()


        # 新增保存按钮
        self.save_btn = QPushButton("保存登录信息")
        self.save_btn.setStyleSheet("background-color: initial;")  # 固定默认背景色
        button_layout.addWidget(self.save_btn)  # 添加到布局
        # 在 init_ui 方法中
        self.save_btn.clicked.connect(self.handle_save_button)  # 正确连接

        right_layout.addLayout(button_layout)
        # ✅ 新增:清除右侧布局的边距
        # right_layout.setContentsMargins(0, 0, 0, 0)
        right_layout.setSpacing(0)  # 移除布局间隙

        main_layout.addWidget(self.site_table)
        main_layout.addLayout(right_layout)

        self.setCentralWidget(main_widget)

        # 新增状态提示标签(替代浏览器容器)
        self.browser_status_label = QLabel("外部浏览器正在加载...")
        self.browser_status_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        right_layout.addWidget(self.browser_status_label)

    def handle_save_button(self):
        """处理保存按钮点击事件"""
        selected_rows = []
        for row in range(self.site_table.rowCount()):
            checkbox = self.site_table.cellWidget(row, 0)
            if checkbox.isChecked():
                selected_rows.append(row)

        if not selected_rows:
            QMessageBox.warning(self, "提示", "请选择要保存的网站")
            return

        # 异步执行保存操作
        asyncio.get_event_loop().run_until_complete(
            self.async_save_selected(selected_rows)
        )

    async def async_save_selected(self, rows):
        """异步保存所有选中的行"""
        for row in rows:
            await self.save_storage_state(row)

    async def save_storage_state(self, row):
        """保存指定行的存储状态"""
        url = self.config.get_site_url(row)
        if url not in self.pages:
            QMessageBox.warning(self, "提示", f"页面未加载:{url}")
            return

        page = self.pages[url]
        try:
            # 获取存储状态
            state = await page.context.storage_state()

            # 获取路径并创建目录
            path = self.config.get_cookies_path(row)
            dir_name = os.path.dirname(path)
            os.makedirs(dir_name, exist_ok=True)

            # 写入文件
            with open(path, 'w') as f:
                json.dump(state, f)

            # 更新状态提示
            self.update_status(url, "登录信息已保存")
            self.update_cell_color(row, "绿色")

        except Exception as e:
            self.update_status(url, f"保存失败:{str(e)}")
            print(f"保存失败:{str(e)}")

    def load_sites_config(self):
        print("加载网站配置...")
        sites = self.config.load_sites()
        self.site_table.setRowCount(len(sites))

        for row, site in enumerate(sites):
            checkbox = QCheckBox()  # 使用复选框替代单选按钮
            checkbox.clicked.connect(lambda checked, current_row=row: self.on_checkbox_toggled(checked, current_row))
            self.site_table.setCellWidget(row, 0, checkbox)

            site_name = QTableWidgetItem(site['name'])
            site_name.setBackground(QColor(255, 0, 0))

            status_item = QTableWidgetItem("未登录")

            action_btn = QPushButton("刷新")
            action_btn.clicked.connect(lambda _, url=site['url']: self.refresh_site(url))

            self.site_table.setCellWidget(row, 0, checkbox)
            self.site_table.setItem(row, 1, site_name)
            self.site_table.setItem(row, 2, status_item)
            self.site_table.setCellWidget(row, 3, action_btn)

            # 检查登录状态并更新单元格
            if self.is_logged_in(row):
                status_text = "已登录"
                color = "绿色"
            else:
                status_text = "未登录"
                color = "红色"

            status_item = QTableWidgetItem(status_text)
            self.site_table.setItem(row, 2, status_item)
            self.update_cell_color(row, color)  # 初始化时设置颜色

    def on_checkbox_toggled(self, checked: bool, row: int):
        site_url = self.config.get_site_url(row)
        print(f"复选框状态:{checked}, 网址:{site_url}")

        try:
            if not self.browser:
                asyncio.get_event_loop().run_until_complete(self.start_playwright())
            if checked:
                asyncio.get_event_loop().run_until_complete(
                    self.show_or_create_page(site_url, row)
                )
            else:
                asyncio.get_event_loop().run_until_complete(
                    self.hide_page(site_url)
                )
        except Exception as e:
            print(f"复选框事件错误:{str(e)}")

    async def show_or_create_page(self, url: str, row: int):
        try:
            if not self.browser:
                await self.start_playwright()
            if url not in self.pages:
                await self.create_playwright_page(url, row)
            page = self.pages[url]
            await page.bring_to_front()
            self.update_cell_color(row, "黄色")
        except Exception as e:
            print(f"页面操作失败:{str(e)}")
            await self.start_playwright()  # 异常时重新启动
            await self.show_or_create_page(url, row)  # 重试

    async def hide_page(self, url: str):
        page = self.pages.get(url)
        if page:
            await page.close()
            del self.pages[url]
            hwnd = self.window_handles.pop(url, None)
            if hwnd:
                win32gui.ShowWindow(hwnd, win32con.SW_HIDE)
            self.update_cell_color(self.get_row_by_url(url), "红色")
        # 不要调用 self.browser.close()!

    async def create_playwright_page(self, url, row):

        if not self.browser:  # 检查浏览器是否存在
            await self.start_playwright()  # 若不存在则重新启动

        # 获取路径并确保目录存在
        path = self.config.get_cookies_path(row)
        dir_name = os.path.dirname(path)
        os.makedirs(dir_name, exist_ok=True)  # 确保目录存在

        # 检查文件是否存在,决定是否使用storage_state
        storage_state = path if os.path.exists(path) else None

        # 创建页面时使用storage_state(如果存在)
        page = await self.browser.new_page(storage_state=storage_state)
        self.pages[url] = page


        await page.goto(url)
        browser_widget = QLabel(f"外部窗口:{url}")
        self.browsers[url] = browser_widget
        # self.browser_stack.addWidget(browser_widget)

        page.on("load", lambda: self.load_finished.emit(url))
        page.on("frame navigated", lambda: self.load_started.emit(url))

        self.update_cell_color(row, "黄色")



    def manage_window(self, url, action='show'):
        """窗口管理函数"""
        hwnd = self.window_handles.get(url)
        if not hwnd:
            return

        if action == 'hide':
            win32gui.ShowWindow(hwnd, win32con.SW_HIDE)
        elif action == 'show':
            win32gui.ShowWindow(hwnd, win32con.SW_SHOW)
            win32gui.SetForegroundWindow(hwnd)
        elif action == 'minimize':
            win32gui.ShowWindow(hwnd, win32con.SW_MINIMIZE)

    # 使用时
    async def show_or_create_page(self, url: str, row: int):
        if url not in self.pages:
            await self.create_playwright_page(url, row)
        self.manage_window(url, 'show')
        self.update_cell_color(row, "yellow")

    def on_site_selected(self, row):
        site_url = self.config.get_site_url(row)
        cookies_path= self.config.get_cookies_path_url(row)
        print(f"选中网站:{site_url}")
        # 创建新页面
        asyncio.get_event_loop().run_until_complete(self.create_playwright_page(site_url, row))

        if site_url in self.pages:
            # 若存在,无需操作(Playwright 页面已打开)
            pass
        else:
            # 2. 创建新页面
            asyncio.get_event_loop().run_until_complete(
                self.create_playwright_page(site_url, row)
            )
        # 3. 更新当前选中的 URL(用于后续操作)
        self.current_url = site_url

    def refresh_site(self, url):
        if self.current_browser and self.current_browser.url == url:
            self.current_browser.reload()
            self.current_browser.resize_browser()



    # 文件:main.py
    def update_status(self, url, message):
        """更新状态栏文本"""
        current_text = self.status_box.toPlainText()
        new_text = f"{current_text}\n{message}: {url}"
        self.status_box.setPlainText(new_text)

    def update_cell_color(self, row, color):
        item = self.site_table.item(row, 1)
        if item:
            if color == "黄色":
                item.setBackground(QColor(255, 255, 0))  # 黄色
            elif color == "绿色":
                item.setBackground(QColor(0, 255, 0))  # 绿色
            else:
                item.setBackground(QColor(255, 0, 0))  # 默认红色

    def is_logged_in(self, row):
        """检查指定行的站点是否已登录"""
        cookies_path = self.config.get_cookies_path(row)
        login_key = self.config.get_login_cookie_key(row)

        if not os.path.exists(cookies_path) or not login_key:
            return False

        try:
            with open(cookies_path, 'r') as f:
                cookies = json.load(f)

            # 检查指定的登录标识 Cookie 是否存在且非空
            for cookie in cookies.get('cookies', []):
                if cookie['name'] == login_key and cookie['value']:
                    return True
        except Exception as e:
            print(f"读取 Cookies 失败:{str(e)}")

        return False

    def on_load_finished(self, url):
        """页面加载完成时更新登录状态"""
        row = self.get_row_by_url(url)
        if row != -1:
            if self.is_logged_in(row):
                self.update_cell_color(row, "绿色")
                self.site_table.item(row, 2).setText("已登录")
            else:
                self.update_cell_color(row, "红色")
                self.site_table.item(row, 2).setText("未登录")

    def check_login_status(self):
        """自动检查并保存未登录的站点"""
        try:
            for row in range(self.site_table.rowCount()):
                if not self.browser:  # 浏览器未启动则跳过
                    continue

                # 获取站点URL
                url = self.config.get_site_url(row)
                if url not in self.pages:
                    continue  # 页面未加载,无法保存

                # 自动触发保存
                asyncio.get_event_loop().run_until_complete(
                    self.save_storage_state(row)
                )
        except Exception as e:
            print(f"定时检查失败:{str(e)}")

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PyAIGCMaster

1毛钱也是爱

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值