sansan 7 luni în urmă
părinte
comite
0c2f6fa065
3 a modificat fișierele cu 286 adăugiri și 22 ștergeri
  1. 273 18
      main.py
  2. 12 3
      readme.md
  3. 1 1
      version

+ 273 - 18
main.py

@@ -20,7 +20,7 @@ import requests
 import yaml
 
 
-VERSION = "2.4.0"
+VERSION = "2.4.1"
 
 
 # === SMTP邮件配置 ===
@@ -1632,10 +1632,15 @@ def render_html_content(
                 position: relative;
             }
             
-            .save-btn {
+            .save-buttons {
                 position: absolute;
                 top: 16px;
                 right: 16px;
+                display: flex;
+                gap: 8px;
+            }
+            
+            .save-btn {
                 background: rgba(255, 255, 255, 0.2);
                 border: 1px solid rgba(255, 255, 255, 0.3);
                 color: white;
@@ -1646,6 +1651,7 @@ def render_html_content(
                 font-weight: 500;
                 transition: all 0.2s ease;
                 backdrop-filter: blur(10px);
+                white-space: nowrap;
             }
             
             .save-btn:hover {
@@ -1658,6 +1664,11 @@ def render_html_content(
                 transform: translateY(0);
             }
             
+            .save-btn:disabled {
+                opacity: 0.6;
+                cursor: not-allowed;
+            }
+            
             .header-title {
                 font-size: 22px;
                 font-weight: 700;
@@ -2001,13 +2012,17 @@ def render_html_content(
                 .news-item { gap: 8px; }
                 .new-item { gap: 8px; }
                 .news-number { width: 20px; height: 20px; font-size: 12px; }
-                .save-btn {
+                .save-buttons {
                     position: static;
                     margin-bottom: 16px;
-                    display: block;
-                    width: fit-content;
-                    margin-left: auto;
-                    margin-right: auto;
+                    display: flex;
+                    gap: 8px;
+                    justify-content: center;
+                    flex-direction: column;
+                    width: 100%;
+                }
+                .save-btn {
+                    width: 100%;
                 }
             }
         </style>
@@ -2015,7 +2030,10 @@ def render_html_content(
     <body>
         <div class="container">
             <div class="header">
-                <button class="save-btn" onclick="saveAsImage()">保存为图片</button>
+                <div class="save-buttons">
+                    <button class="save-btn" onclick="saveAsImage()">保存为图片</button>
+                    <button class="save-btn" onclick="saveAsMultipleImages()">分段保存</button>
+                </div>
                 <div class="header-title">热点新闻分析</div>
                 <div class="header-info">
                     <div class="info-item">
@@ -2267,7 +2285,7 @@ def render_html_content(
         
         <script>
             async function saveAsImage() {
-                const button = document.querySelector('.save-btn');
+                const button = event.target;
                 const originalText = button.textContent;
                 
                 try {
@@ -2279,17 +2297,14 @@ def render_html_content(
                     await new Promise(resolve => setTimeout(resolve, 200));
                     
                     // 截图前隐藏按钮
-                    button.style.visibility = 'hidden';
+                    const buttons = document.querySelector('.save-buttons');
+                    buttons.style.visibility = 'hidden';
                     
                     // 再次等待确保按钮完全隐藏
                     await new Promise(resolve => setTimeout(resolve, 100));
                     
                     const container = document.querySelector('.container');
                     
-                    // 获取容器的精确位置和尺寸
-                    const rect = container.getBoundingClientRect();
-                    const computedStyle = window.getComputedStyle(container);
-                    
                     const canvas = await html2canvas(container, {
                         backgroundColor: '#ffffff',
                         scale: 1.5,
@@ -2309,7 +2324,7 @@ def render_html_content(
                         windowHeight: window.innerHeight
                     });
                     
-                    button.style.visibility = 'visible';
+                    buttons.style.visibility = 'visible';
                     
                     const link = document.createElement('a');
                     const now = new Date();
@@ -2330,7 +2345,239 @@ def render_html_content(
                     }, 2000);
                     
                 } catch (error) {
-                    button.style.visibility = 'visible';
+                    const buttons = document.querySelector('.save-buttons');
+                    buttons.style.visibility = 'visible';
+                    button.textContent = '保存失败';
+                    setTimeout(() => {
+                        button.textContent = originalText;
+                        button.disabled = false;
+                    }, 2000);
+                }
+            }
+            
+            async function saveAsMultipleImages() {
+                const button = event.target;
+                const originalText = button.textContent;
+                const container = document.querySelector('.container');
+                const scale = 1.5; 
+                const maxHeight = 5000 / scale;
+                
+                try {
+                    button.textContent = '分析中...';
+                    button.disabled = true;
+                    
+                    // 获取所有可能的分割元素
+                    const newsItems = Array.from(container.querySelectorAll('.news-item'));
+                    const wordGroups = Array.from(container.querySelectorAll('.word-group'));
+                    const newSection = container.querySelector('.new-section');
+                    const errorSection = container.querySelector('.error-section');
+                    const header = container.querySelector('.header');
+                    const footer = container.querySelector('.footer');
+                    
+                    // 计算元素位置和高度
+                    const containerRect = container.getBoundingClientRect();
+                    const elements = [];
+                    
+                    // 添加header作为必须包含的元素
+                    elements.push({
+                        type: 'header',
+                        element: header,
+                        top: 0,
+                        bottom: header.offsetHeight,
+                        height: header.offsetHeight
+                    });
+                    
+                    // 添加错误信息(如果存在)
+                    if (errorSection) {
+                        const rect = errorSection.getBoundingClientRect();
+                        elements.push({
+                            type: 'error',
+                            element: errorSection,
+                            top: rect.top - containerRect.top,
+                            bottom: rect.bottom - containerRect.top,
+                            height: rect.height
+                        });
+                    }
+                    
+                    // 按word-group分组处理news-item
+                    wordGroups.forEach(group => {
+                        const groupRect = group.getBoundingClientRect();
+                        const groupNewsItems = group.querySelectorAll('.news-item');
+                        
+                        // 添加word-group的header部分
+                        const wordHeader = group.querySelector('.word-header');
+                        if (wordHeader) {
+                            const headerRect = wordHeader.getBoundingClientRect();
+                            elements.push({
+                                type: 'word-header',
+                                element: wordHeader,
+                                parent: group,
+                                top: groupRect.top - containerRect.top,
+                                bottom: headerRect.bottom - containerRect.top,
+                                height: headerRect.height
+                            });
+                        }
+                        
+                        // 添加每个news-item
+                        groupNewsItems.forEach(item => {
+                            const rect = item.getBoundingClientRect();
+                            elements.push({
+                                type: 'news-item',
+                                element: item,
+                                parent: group,
+                                top: rect.top - containerRect.top,
+                                bottom: rect.bottom - containerRect.top,
+                                height: rect.height
+                            });
+                        });
+                    });
+                    
+                    // 添加新增新闻部分
+                    if (newSection) {
+                        const rect = newSection.getBoundingClientRect();
+                        elements.push({
+                            type: 'new-section',
+                            element: newSection,
+                            top: rect.top - containerRect.top,
+                            bottom: rect.bottom - containerRect.top,
+                            height: rect.height
+                        });
+                    }
+                    
+                    // 添加footer
+                    const footerRect = footer.getBoundingClientRect();
+                    elements.push({
+                        type: 'footer',
+                        element: footer,
+                        top: footerRect.top - containerRect.top,
+                        bottom: footerRect.bottom - containerRect.top,
+                        height: footer.offsetHeight
+                    });
+                    
+                    // 计算分割点
+                    const segments = [];
+                    let currentSegment = { start: 0, end: 0, height: 0, includeHeader: true };
+                    let headerHeight = header.offsetHeight;
+                    currentSegment.height = headerHeight;
+                    
+                    for (let i = 1; i < elements.length; i++) {
+                        const element = elements[i];
+                        const potentialHeight = element.bottom - currentSegment.start;
+                        
+                        // 检查是否需要创建新分段
+                        if (potentialHeight > maxHeight && currentSegment.height > headerHeight) {
+                            // 在前一个元素结束处分割
+                            currentSegment.end = elements[i - 1].bottom;
+                            segments.push(currentSegment);
+                            
+                            // 开始新分段
+                            currentSegment = {
+                                start: currentSegment.end,
+                                end: 0,
+                                height: element.bottom - currentSegment.end,
+                                includeHeader: false
+                            };
+                        } else {
+                            currentSegment.height = potentialHeight;
+                            currentSegment.end = element.bottom;
+                        }
+                    }
+                    
+                    // 添加最后一个分段
+                    if (currentSegment.height > 0) {
+                        currentSegment.end = container.offsetHeight;
+                        segments.push(currentSegment);
+                    }
+                    
+                    button.textContent = `生成中 (0/${segments.length})...`;
+                    
+                    // 隐藏保存按钮
+                    const buttons = document.querySelector('.save-buttons');
+                    buttons.style.visibility = 'hidden';
+                    
+                    // 为每个分段生成图片
+                    const images = [];
+                    for (let i = 0; i < segments.length; i++) {
+                        const segment = segments[i];
+                        button.textContent = `生成中 (${i + 1}/${segments.length})...`;
+                        
+                        // 创建临时容器用于截图
+                        const tempContainer = document.createElement('div');
+                        tempContainer.style.cssText = `
+                            position: absolute;
+                            left: -9999px;
+                            top: 0;
+                            width: ${container.offsetWidth}px;
+                            background: white;
+                        `;
+                        tempContainer.className = 'container';
+                        
+                        // 克隆容器内容
+                        const clonedContainer = container.cloneNode(true);
+                        
+                        // 移除克隆内容中的保存按钮
+                        const clonedButtons = clonedContainer.querySelector('.save-buttons');
+                        if (clonedButtons) {
+                            clonedButtons.style.display = 'none';
+                        }
+                        
+                        tempContainer.appendChild(clonedContainer);
+                        document.body.appendChild(tempContainer);
+                        
+                        // 等待DOM更新
+                        await new Promise(resolve => setTimeout(resolve, 100));
+                        
+                        // 使用html2canvas截取特定区域
+                        const canvas = await html2canvas(clonedContainer, {
+                            backgroundColor: '#ffffff',
+                            scale: scale,
+                            useCORS: true,
+                            allowTaint: false,
+                            imageTimeout: 10000,
+                            logging: false,
+                            width: container.offsetWidth,
+                            height: segment.end - segment.start,
+                            x: 0,
+                            y: segment.start,
+                            windowWidth: window.innerWidth,
+                            windowHeight: window.innerHeight
+                        });
+                        
+                        images.push(canvas.toDataURL('image/png', 1.0));
+                        
+                        // 清理临时容器
+                        document.body.removeChild(tempContainer);
+                    }
+                    
+                    // 恢复按钮显示
+                    buttons.style.visibility = 'visible';
+                    
+                    // 下载所有图片
+                    const now = new Date();
+                    const baseFilename = `TrendRadar_热点新闻分析_${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}_${String(now.getHours()).padStart(2, '0')}${String(now.getMinutes()).padStart(2, '0')}`;
+                    
+                    for (let i = 0; i < images.length; i++) {
+                        const link = document.createElement('a');
+                        link.download = `${baseFilename}_part${i + 1}.png`;
+                        link.href = images[i];
+                        document.body.appendChild(link);
+                        link.click();
+                        document.body.removeChild(link);
+                        
+                        // 延迟一下避免浏览器阻止多个下载
+                        await new Promise(resolve => setTimeout(resolve, 100));
+                    }
+                    
+                    button.textContent = `已保存 ${segments.length} 张图片!`;
+                    setTimeout(() => {
+                        button.textContent = originalText;
+                        button.disabled = false;
+                    }, 2000);
+                    
+                } catch (error) {
+                    console.error('分段保存失败:', error);
+                    const buttons = document.querySelector('.save-buttons');
+                    buttons.style.visibility = 'visible';
                     button.textContent = '保存失败';
                     setTimeout(() => {
                         button.textContent = originalText;
@@ -3480,10 +3727,18 @@ def send_to_ntfy(
     mode: str = "daily",
 ) -> bool:
     """发送到ntfy(支持分批发送,严格遵守4KB限制)"""
+    # 避免 HTTP header 编码问题
+    report_type_en_map = {
+        "当日汇总": "Daily Summary",
+        "当前榜单汇总": "Current Ranking",
+        "增量更新": "Incremental Update",
+    }
+    report_type_en = report_type_en_map.get(report_type, report_type)
+
     headers = {
         "Content-Type": "text/plain; charset=utf-8",
         "Markdown": "yes",
-        "Title": f"TrendRadar 热点分析报告 - {report_type}",
+        "Title": f"TrendRadar Report - {report_type_en}",
         "Priority": "default",
         "Tags": "newspaper,📰",
     }
@@ -3526,7 +3781,7 @@ def send_to_ntfy(
             batch_header = f"**[第 {i}/{len(batches)} 批次]**\n\n"
             batch_content = batch_header + batch_content
             current_headers["Title"] = (
-                f"TrendRadar 热点分析报告 - {report_type} ({i}/{len(batches)})"
+                f"TrendRadar Report - {report_type_en} ({i}/{len(batches)})"
             )
 
         try:

+ 12 - 3
readme.md

@@ -11,7 +11,7 @@
 [![GitHub Stars](https://img.shields.io/github/stars/sansan0/TrendRadar?style=flat-square&logo=github&color=yellow)](https://github.com/sansan0/TrendRadar/stargazers)
 [![GitHub Forks](https://img.shields.io/github/forks/sansan0/TrendRadar?style=flat-square&logo=github&color=blue)](https://github.com/sansan0/TrendRadar/network/members)
 [![License](https://img.shields.io/badge/license-GPL--3.0-blue.svg?style=flat-square)](LICENSE)
-[![Version](https://img.shields.io/badge/version-v2.4.0-green.svg?style=flat-square)](https://github.com/sansan0/TrendRadar)
+[![Version](https://img.shields.io/badge/version-v2.4.1-green.svg?style=flat-square)](https://github.com/sansan0/TrendRadar)
 
 [![企业微信通知](https://img.shields.io/badge/企业微信-通知-00D4AA?style=flat-square)](https://work.weixin.qq.com/)
 [![Telegram通知](https://img.shields.io/badge/Telegram-通知-00D4AA?style=flat-square)](https://telegram.org/)
@@ -119,6 +119,8 @@ platforms:
     name: "华尔街见闻"
   # 添加更多平台...
 ```
+如果不会看的话,就直接复制他人整理好的部分[平台配置](https://github.com/sansan0/TrendRadar/issues/95)
+
 </details>
 
 ### **智能推送策略**
@@ -462,6 +464,15 @@ GitHub 一键 Fork 即可使用,无需编程基础。
 - **小版本更新**:从 v2.x 升级到 v2.y, 用本项目的 `main.py` 代码替换你 fork 仓库中的对应文件 
 - **大版本升级**:从 v1.x 升级到 v2.y, 建议删除现有 fork 后重新 fork,这样更省力且避免配置冲突
 
+### 2025/10/8 - v2.4.1
+
+- 修复 ntfy 推送编码问题
+- 增加 github page 图片分段导出功能
+
+
+<details>
+<summary><strong>👉 历史更新</strong></summary>
+
 ### 2025/10/2 - v2.4.0
 
 **新增 ntfy 推送通知**
@@ -478,8 +489,6 @@ GitHub 一键 Fork 即可使用,无需编程基础。
 - **更新提示**:
   - 建议使用【大版本更新】
 
-<details>
-<summary><strong>👉 历史更新</strong></summary>
 
 ### 2025/09/26 - v2.3.2
 

+ 1 - 1
version

@@ -1 +1 @@
-2.4.0
+2.4.1