jack 3 сар өмнө
parent
commit
4c3c801522
8 өөрчлөгдсөн 478 нэмэгдсэн , 0 устгасан
  1. 1 0
      data/targets.txt
  2. 63 0
      downloader.py
  3. 71 0
      main.py
  4. 6 0
      requirements.txt
  5. 102 0
      static/script.js
  6. 185 0
      static/style.css
  7. 50 0
      templates/index.html
  8. 0 0
      utils.py

+ 1 - 0
data/targets.txt

@@ -0,0 +1 @@
+https://e-hentai.org/g/3550066/47d6393550

+ 63 - 0
downloader.py

@@ -0,0 +1,63 @@
+import aiofiles
+import httpx
+from typing import Optional
+import os
+
+class Downloader:
+    def __init__(self):
+        self.output_dir = "downloads"
+        os.makedirs(self.output_dir, exist_ok=True)
+    
+    async def download(self, ip: str, port: str, url: str) -> str:
+        """
+        下载文件的主要逻辑
+        这里你需要替换成你自己的下载逻辑
+        """
+        try:
+            # 这里是你原来的 Go 代码对应的 Python 逻辑
+            # 示例:使用代理下载文件
+            
+            # 构建代理
+            proxy = f"http://{ip}:{port}"
+            
+            # 使用 httpx 异步下载
+            async with httpx.AsyncClient(proxies=proxy, timeout=30.0) as client:
+                response = await client.get(url)
+                response.raise_for_status()
+                
+                # 获取文件名
+                filename = self._get_filename(url, response)
+                filepath = os.path.join(self.output_dir, filename)
+                
+                # 保存文件
+                async with aiofiles.open(filepath, 'wb') as f:
+                    await f.write(response.content)
+                
+                return f"下载成功: {filename}\n保存路径: {filepath}\n文件大小: {len(response.content)} bytes"
+                
+        except Exception as e:
+            raise Exception(f"下载过程中出错: {str(e)}")
+    
+    def _get_filename(self, url: str, response: httpx.Response) -> str:
+        """从 URL 或响应头中获取文件名"""
+        # 从 URL 中提取文件名
+        if '/' in url:
+            filename = url.split('/')[-1]
+            if '?' in filename:
+                filename = filename.split('?')[0]
+        else:
+            filename = "downloaded_file"
+        
+        # 如果没有扩展名,尝试从 Content-Type 推断
+        if '.' not in filename:
+            content_type = response.headers.get('content-type', '')
+            if 'image' in content_type:
+                ext = content_type.split('/')[-1]
+                filename = f"{filename}.{ext}"
+        
+        return filename or "downloaded_file"
+    
+    async def download_image(self, ip: str, port: str, url: str) -> str:
+        """专门下载图片的方法"""
+        # 这里可以添加图片下载的特殊逻辑
+        return await self.download(ip, port, url)

+ 71 - 0
main.py

@@ -0,0 +1,71 @@
+from fastapi import FastAPI, Request, Form
+from fastapi.staticfiles import StaticFiles
+from fastapi.templating import Jinja2Templates
+from fastapi.responses import JSONResponse
+import uvicorn
+import os
+
+app = FastAPI(title="下载工具", version="1.0.0")
+
+# 挂载静态文件和模板
+app.mount("/static", StaticFiles(directory="static"), name="static")
+templates = Jinja2Templates(directory="templates")
+
+@app.get("/")
+async def home(request: Request):
+    """主页面"""
+    return templates.TemplateResponse("index.html", {"request": request})
+
+@app.post("/load_urls")
+async def load_urls():
+    """读取 targets.txt 文件中的URL"""
+    try:
+        file_path = "data/targets.txt"
+        
+        # 检查文件是否存在
+        if not os.path.exists('data'):
+            os.mkdir('data')
+            return JSONResponse({
+                "success": False,
+                "message": f"文件 {file_path} 不存在",
+                "urls": []
+            })
+        # 检查导入url配置是否存在
+        if not os.path.exists(file_path):
+            return JSONResponse({
+                "success": False,
+                "message": f"文件 {file_path} 不存在",
+                "urls": []
+            })
+        
+        # 读取文件内容
+        with open(file_path, 'r', encoding='utf-8') as f:
+            urls = [line.strip() for line in f.readlines() if line.strip()]
+        
+        # 过滤掉空行和注释行(以#开头的行)
+        urls = [url for url in urls if url and not url.startswith('#')]
+        
+        return JSONResponse({
+            "success": True,
+            "message": f"成功读取 {len(urls)} 个URL",
+            "urls": urls
+        })
+        
+    except Exception as e:
+        return JSONResponse({
+            "success": False,
+            "message": f"读取文件时出错: {str(e)}",
+            "urls": []
+        })
+
+@app.post("/clear")
+async def clear_output():
+    """清除输出"""
+    return JSONResponse({
+        "success": True,
+        "message": "输出已清除",
+        "output": ""
+    })
+
+if __name__ == "__main__":
+    uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)

+ 6 - 0
requirements.txt

@@ -0,0 +1,6 @@
+fastapi==0.104.1
+uvicorn==0.24.0
+aiofiles==23.2.1
+httpx==0.25.2
+python-multipart==0.0.6
+jinja2==3.1.2

+ 102 - 0
static/script.js

@@ -0,0 +1,102 @@
+class DownloadTool {
+    constructor() {
+        this.form = document.getElementById('downloadForm');
+        this.output = document.getElementById('output');
+        this.loadUrlsBtn = document.getElementById('loadUrls');
+        this.urlListTextarea = document.getElementById('urlList');
+        this.downloadUrlBtn = document.getElementById('downloadUrl');
+        this.downloadImageBtn = document.getElementById('downloadImage');
+        this.clearOutputBtn = document.getElementById('clearOutput');
+        
+        this.initEvents();
+    }
+    
+    initEvents() {
+        // 读取URL按钮
+        this.loadUrlsBtn.addEventListener('click', () => {
+            this.loadTargetUrls();
+        });
+        
+        // 下载URL按钮
+        this.downloadUrlBtn.addEventListener('click', () => {
+            this.showOutput('下载URL被点击', 'success');
+        });
+        
+        // 下载图片按钮
+        this.downloadImageBtn.addEventListener('click', () => {
+            this.showOutput('下载IMG被点击', 'success');
+        });
+        
+        // 清除输出按钮
+        this.clearOutputBtn.addEventListener('click', () => {
+            this.clearOutput();
+        });
+    }
+    
+    async loadTargetUrls() {
+        try {
+            this.showOutput('正在读取 targets.txt...', '');
+            
+            const response = await fetch('/load_urls', {
+                method: 'POST'
+            });
+            
+            const result = await response.json();
+            
+            if (result.success) {
+                // 在URL列表文本框中显示读取的URL
+                this.urlListTextarea.value = result.urls.join('\n');
+                this.showOutput(`成功读取 ${result.urls.length} 个URL`, 'success');
+            } else {
+                this.showOutput(`读取失败: ${result.message}`, 'error');
+            }
+        } catch (error) {
+            this.showOutput(`读取URL时出错: ${error.message}`, 'error');
+        }
+    }
+    
+    async clearOutput() {
+        try {
+            const response = await fetch('/clear', {
+                method: 'POST'
+            });
+            
+            const result = await response.json();
+            if (result.success) {
+                this.showOutput('', 'success');
+                this.urlListTextarea.value = ''; // 同时清空URL列表
+            }
+        } catch (error) {
+            this.showOutput(`清除失败: ${error.message}`, 'error');
+        }
+    }
+    
+    showOutput(message, type = '') {
+        this.output.textContent = message;
+        this.output.className = 'output-area';
+        if (type) {
+            this.output.classList.add(type);
+        }
+        
+        // 自动滚动到底部
+        this.output.scrollTop = this.output.scrollHeight;
+    }
+    
+    setLoading(loading) {
+        const buttons = this.form.querySelectorAll('button');
+        buttons.forEach(button => {
+            button.disabled = loading;
+        });
+        
+        if (loading) {
+            document.body.classList.add('loading');
+        } else {
+            document.body.classList.remove('loading');
+        }
+    }
+}
+
+// 初始化应用
+document.addEventListener('DOMContentLoaded', () => {
+    new DownloadTool();
+});

+ 185 - 0
static/style.css

@@ -0,0 +1,185 @@
+* {
+    margin: 0;
+    padding: 0;
+    box-sizing: border-box;
+}
+
+body {
+    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+    min-height: 100vh;
+    padding: 20px;
+}
+
+.container {
+    max-width: 800px;
+    margin: 0 auto;
+    background: white;
+    border-radius: 10px;
+    padding: 30px;
+    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
+}
+
+h1 {
+    text-align: center;
+    color: #333;
+    margin-bottom: 30px;
+    font-size: 2.5em;
+}
+
+.form-group {
+    margin-bottom: 20px;
+}
+
+label {
+    display: block;
+    margin-bottom: 5px;
+    font-weight: 600;
+    color: #555;
+}
+
+input[type="text"],
+input[type="url"] {
+    width: 100%;
+    padding: 12px;
+    border: 2px solid #ddd;
+    border-radius: 5px;
+    font-size: 16px;
+    transition: border-color 0.3s;
+}
+
+input[type="text"]:focus,
+input[type="url"]:focus {
+    outline: none;
+    border-color: #667eea;
+}
+
+/* URL列表文本框 */
+.url-list {
+    width: 100%;
+    height: 120px;
+    padding: 12px;
+    border: 2px solid #ddd;
+    border-radius: 5px;
+    font-size: 12px;
+    font-family: 'Courier New', monospace;
+    background-color: #f8f9fa;
+    resize: vertical;
+    color: #555;
+}
+
+.url-list:focus {
+    outline: none;
+    border-color: #667eea;
+}
+
+.button-group {
+    display: flex;
+    gap: 10px;
+    margin-bottom: 30px;
+    flex-wrap: wrap;
+}
+
+.btn {
+    padding: 12px 24px;
+    border: none;
+    border-radius: 5px;
+    font-size: 16px;
+    cursor: pointer;
+    transition: all 0.3s;
+    font-weight: 600;
+}
+
+.btn-primary {
+    background: #667eea;
+    color: white;
+}
+
+.btn-primary:hover {
+    background: #5a6fd8;
+    transform: translateY(-2px);
+}
+
+.btn-secondary {
+    background: #764ba2;
+    color: white;
+}
+
+.btn-secondary:hover {
+    background: #6a4190;
+    transform: translateY(-2px);
+}
+
+.btn-danger {
+    background: #e74c3c;
+    color: white;
+}
+
+.btn-danger:hover {
+    background: #c0392b;
+    transform: translateY(-2px);
+}
+
+.btn-info {
+    background: #17a2b8;
+    color: white;
+}
+
+.btn-info:hover {
+    background: #138496;
+    transform: translateY(-2px);
+}
+
+.output-section h3 {
+    color: #333;
+    margin-bottom: 10px;
+}
+
+.output-area {
+    background: #f8f9fa;
+    border: 1px solid #e9ecef;
+    border-radius: 5px;
+    padding: 15px;
+    min-height: 150px;
+    max-height: 400px;
+    overflow-y: auto;
+    font-family: 'Courier New', monospace;
+    white-space: pre-wrap;
+    word-wrap: break-word;
+}
+
+.output-area.success {
+    border-left: 4px solid #28a745;
+}
+
+.output-area.error {
+    border-left: 4px solid #dc3545;
+}
+
+.loading {
+    opacity: 0.7;
+    pointer-events: none;
+}
+
+@media (max-width: 600px) {
+    .container {
+        padding: 20px;
+    }
+    
+    .button-group {
+        flex-direction: column;
+    }
+    
+    .btn {
+        width: 100%;
+    }
+}
+
+.form-row {
+    display: flex;
+    gap: 15px;
+}
+
+.form-row .form-group {
+    flex: 1;
+}

+ 50 - 0
templates/index.html

@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>下载工具</title>
+    <link rel="stylesheet" href="/static/style.css">
+</head>
+<body>
+    <div class="container">
+        <h1>下载工具</h1>
+        
+        <form id="downloadForm" class="form">
+            <div class="form-row">
+                <div class="form-group">
+                    <label for="ip">IP:</label>
+                    <input type="text" id="ip" name="ip" value="127.0.0.1" required>
+                </div>
+                
+                <div class="form-group">
+                    <label for="port">端口:</label>
+                    <input type="text" id="port" name="port" value="7890" required>
+                </div>
+            </div>
+            
+            <div class="form-group">
+                <button type="button" id="loadUrls" class="btn btn-info">读取目标URL</button>
+            </div>
+            
+            <div class="form-group">
+                <label for="urlList">目标URL列表:</label>
+                <textarea id="urlList" name="urlList" class="url-list" readonly placeholder="这里将显示从 targets.txt 读取的URL列表"></textarea>
+            </div>
+            
+            <div class="button-group">
+                <button type="button" id="downloadUrl" class="btn btn-primary">下载URL</button>
+                <button type="button" id="downloadImage" class="btn btn-secondary">下载图片</button>
+                <button type="button" id="clearOutput" class="btn btn-danger">清除输出</button>
+            </div>
+        </form>
+        
+        <div class="output-section">
+            <h3>输出将显示在这里...</h3>
+            <pre id="output" class="output-area"></pre>
+        </div>
+    </div>
+    
+    <script src="/static/script.js"></script>
+</body>
+</html>

+ 0 - 0
utils.py