Greasy Fork

Greasy Fork is available in English.

提取章节内容1.0

提取章节标题和内容,展示状态框,提供操作按钮

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         提取章节内容1.0
// @namespace    http://tampermonkey.net/
// @version      2024-12-25
// @description  提取章节标题和内容,展示状态框,提供操作按钮
// @author       cheniyy
// @match        *://*.bqgui.cc/*
// @match        *://*.bq01.cc/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=bqgui.cc
// @grant        none
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // 提取页面的章节标题和内容
    const chapterTitleElement = document.querySelector('h1.wap_none');
    const chapterContentElement = document.querySelector('div#chaptercontent');

    const chapterTitle = chapterTitleElement ? chapterTitleElement.textContent.trim() : null;
    const chapterContent = chapterContentElement ? chapterContentElement.innerHTML.trim() : null;
    let currentUrl = window.location.href; // 获取当前页面的URL

    // 处理题目提取,去掉所有 HTML 标签
    function getTextFromHTML1(html) {
        const doc = new DOMParser().parseFromString(html, 'text/html');
        return doc.body.textContent || "";
    }

    function getTextFromHTML2(html) {
        const doc = new DOMParser().parseFromString(html, 'text/html');
        let text = doc.body.innerHTML
            .replace(/<br\s*\/?>/gi, '\n')  // 替换 <br> 标签为换行符
            .replace(/<\/p>/gi, '\n\n')  // 将 </p> 标签转换为换行符,并保留段落间距
            .replace(/<\/div>/gi, '\n\n')  // 将 </div> 标签转换为换行符,并保留段落间距
            .replace(/&nbsp;/gi, ' ')  // 替换 &nbsp; 为普通空格
            .replace(/<[^>]+>/g, '');  // 去除所有HTML标签

        // 去掉首尾空白
        text = text.trim();

        // 处理文本缩进(去掉多余的空格或空行)
        text = text.replace(/^[\s]+/gm, '');  // 去掉每行前面的空白字符(防止多重缩进)

        // 添加首行缩进(例如,四个空格)
        text = text.replace(/^(.+)/gm, '    $1');  // 给每行加上四个空格,模拟首行缩进

        return text;
    }

    // 如果没有章节内容或标题,设置错误提示
    let statusMessage = `章节标题:${chapterTitle || '未找到标题'}`;
    if (!chapterTitle || !chapterContent) {
        statusMessage = `未找到章节内容,请检查页面结构!`;
    }

    // 合并标题和内容
    const pureTextContent = chapterTitle && chapterContent ? getTextFromHTML1(chapterTitle) + "\n\n" + getTextFromHTML2(chapterContent) : '';

    // 创建一个浮动框,用于显示脚本状态
    const statusBox = document.createElement('div');
    statusBox.style.position = 'fixed';
    statusBox.style.top = '20px';
    statusBox.style.left = '20px';
    statusBox.style.width = '300px';
    statusBox.style.padding = '15px';
    statusBox.style.backgroundColor = '#fff';
    statusBox.style.border = '1px solid #ccc';
    statusBox.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.1)';
    statusBox.style.zIndex = '9999';
    statusBox.style.borderRadius = '8px';
    statusBox.style.fontFamily = 'Arial, sans-serif';

    // 使 statusBox 可拖动
    let isDragging = false;
    let offsetX, offsetY;

    statusBox.style.position = 'absolute';
    statusBox.style.cursor = 'move';

    statusBox.addEventListener('mousedown', (e) => {
        isDragging = true;
        offsetX = e.clientX - statusBox.offsetLeft;
        offsetY = e.clientY - statusBox.offsetTop;
    });

    document.addEventListener('mousemove', (e) => {
        if (isDragging) {
            statusBox.style.left = `${e.clientX - offsetX}px`;
            statusBox.style.top = `${e.clientY - offsetY}px`;
        }
    });

    document.addEventListener('mouseup', () => {
        isDragging = false;
    });

    // 显示脚本状态文字
    const statusText = document.createElement('div');
    statusText.textContent = statusMessage;

    // 添加按钮
    const copyButton = document.createElement('button');
    copyButton.textContent = '复制';
    copyButton.style.marginTop = '10px';
    copyButton.style.padding = '8px 12px';
    copyButton.style.backgroundColor = '#4CAF50';
    copyButton.style.color = '#fff';
    copyButton.style.border = 'none';
    copyButton.style.borderRadius = '5px';
    copyButton.style.cursor = 'pointer';
    copyButton.onclick = async () => {
        try {
            await navigator.clipboard.writeText(pureTextContent);
            alert('章节内容已复制!');
        } catch (err) {
            alert('复制失败,请检查剪贴板权限');
        }
    };

    const viewButton = document.createElement('button');
    viewButton.textContent = '查看复制内容';
    viewButton.style.marginTop = '10px';
    viewButton.style.padding = '8px 12px';
    viewButton.style.backgroundColor = '#2196F3';
    viewButton.style.color = '#fff';
    viewButton.style.border = 'none';
    viewButton.style.borderRadius = '5px';
    viewButton.style.cursor = 'pointer';
    viewButton.onclick = () => {
        const newWindow = window.open('', '_blank', 'width=800,height=600');
        const contentHTML = `
            <html>
                <head>
                    <title>章节内容</title>
                    <style>
                        body { font-family: Arial, sans-serif; margin: 20px; background-color: #f9f9f9; }
                        .content-container { background-color: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); max-height: 100%; overflow-y: auto; }
                        h1 { font-size: 24px; color: #333; }
                        pre { font-family: 'Courier New', monospace; white-space: pre-wrap; word-wrap: break-word; background-color: #f0f0f0; padding: 10px; border-radius: 5px; }
                    </style>
                </head>
                <body>
                    <div class="content-container">
                        <h1>章节内容</h1>
                        <pre>${pureTextContent}</pre>
                    </div>
                </body>
            </html>
        `;
        newWindow.document.write(contentHTML);
        newWindow.document.close();
    };

    const saveButton = document.createElement('button');
    saveButton.textContent = '保存为文件';
    saveButton.style.marginTop = '10px';
    saveButton.style.padding = '8px 12px';
    saveButton.style.backgroundColor = '#FF5722';
    saveButton.style.color = '#fff';
    saveButton.style.border = 'none';
    saveButton.style.borderRadius = '5px';
    saveButton.style.cursor = 'pointer';
    saveButton.onclick = () => {
        const blob = new Blob([pureTextContent], { type: 'text/plain' });
        const link = document.createElement('a');
        link.href = URL.createObjectURL(blob);
        link.download = `${chapterTitle || '章节'} - 内容.txt`;
        link.click();
    };

    const settingsButton = document.createElement('button');
    settingsButton.textContent = '设置';
    settingsButton.style.marginTop = '10px';
    settingsButton.style.padding = '8px 12px';
    settingsButton.style.backgroundColor = '#FFC107';
    settingsButton.style.color = '#fff';
    settingsButton.style.border = 'none';
    settingsButton.style.borderRadius = '5px';
    settingsButton.style.cursor = 'pointer';
    settingsButton.onclick = () => {
        const shouldDisplayTitle = confirm("是否显示章节标题?");
        if (shouldDisplayTitle) {
            statusText.textContent = `章节标题:${chapterTitle}`;
        } else {
            statusText.textContent = "章节内容";
        }
    };

    // 将状态框元素添加到页面
    statusBox.appendChild(statusText);
    statusBox.appendChild(copyButton);
    statusBox.appendChild(viewButton);
    statusBox.appendChild(saveButton);
    statusBox.appendChild(settingsButton);
    document.body.appendChild(statusBox);

    // 将提取的内容存储到本地存储(例如使用 localStorage)
    if (chapterTitle && chapterContent) {
        localStorage.setItem('chapterTitle', chapterTitle);
        localStorage.setItem('chapterContent', chapterContent);
    } else {
        localStorage.removeItem('chapterTitle');
        localStorage.removeItem('chapterContent');
    }

    // 自动加载下一章节(优化滚动判断)
    let autoLoadEnabled = true;
    window.addEventListener('scroll', () => {
        if (autoLoadEnabled && (window.innerHeight + window.scrollY >= document.body.offsetHeight - 100)) {
            autoLoadEnabled = false;
            const parts = currentUrl.split('/');
            const lastPart = parts.pop();
            const chapNumber = parseInt(lastPart, 10);
            if (!isNaN(chapNumber)) {
                let nextUrl = parts.join('/') + '/' + (chapNumber + 1);
                if (currentUrl.includes('.html')) {
                    nextUrl += '.html';
                }
                window.location.href = nextUrl;
            }
        }
    });

})();