down / app.py
P01yH3dr0n's picture
Update app.py
3c39f84 verified
import gradio as gr
import gdown
import os
import requests
import zipfile
from pathlib import Path
from urllib.parse import urlparse
import re
from datetime import datetime
# 创建下载目录
DOWNLOAD_DIR = Path("download")
DOWNLOAD_DIR.mkdir(exist_ok=True)
def is_google_drive_link(url):
"""检查是否为Google Drive链接"""
patterns = [
r'drive\.google\.com',
r'docs\.google\.com'
]
return any(re.search(pattern, url) for pattern in patterns)
def get_file_size_str(size_bytes):
"""将字节转换为可读的文件大小"""
for unit in ['B', 'KB', 'MB', 'GB']:
if size_bytes < 1024.0:
return f"{size_bytes:.2f} {unit}"
size_bytes /= 1024.0
return f"{size_bytes:.2f} TB"
def compress_folder(folder_path):
"""压缩文件夹为zip文件"""
zip_path = f"{folder_path}.zip"
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
for root, dirs, files in os.walk(folder_path):
for file in files:
file_path = os.path.join(root, file)
arcname = os.path.relpath(file_path, os.path.dirname(folder_path))
zipf.write(file_path, arcname)
return zip_path
def download_file(url, headers_dict=None, progress=gr.Progress()):
"""下载文件的主函数"""
try:
progress(0, desc="开始下载...")
# 解析请求头
headers = {}
if headers_dict:
for key, value in headers_dict.items():
if key and value:
headers[key] = value
# 判断是否为Google Drive链接
if is_google_drive_link(url):
progress(0.3, desc="检测到Google Drive链接,使用gdown下载...")
# 使用gdown下载
output_path = str(DOWNLOAD_DIR) + '/'
# 检测是否为文件夹链接
is_folder = 'folders' in url
if is_folder:
downloaded = gdown.download_folder(url, output=output_path, quiet=False)
if downloaded:
folder_path = os.path.dirname(downloaded[0])
progress(0.8, desc="压缩文件夹...")
zip_path = compress_folder(folder_path)
file_size = os.path.getsize(zip_path)
return (
zip_path,
f"✅ 下载成功!\n文件: {os.path.basename(zip_path)}\n大小: {get_file_size_str(file_size)}"
)
else:
output_file = gdown.download(url, output=output_path, quiet=False, fuzzy=True)
if output_file:
file_size = os.path.getsize(output_file)
return (
output_file,
f"✅ 下载成功!\n文件: {os.path.basename(output_file)}\n大小: {get_file_size_str(file_size)}"
)
else:
return None, "❌ Google Drive下载失败"
else:
progress(0.3, desc="使用HTTP方式下载...")
# 普通HTTP下载
response = requests.get(url, headers=headers, stream=True, timeout=30)
response.raise_for_status()
# 获取文件名
content_disposition = response.headers.get('content-disposition')
if content_disposition:
filename = re.findall('filename="(.+?)"', content_disposition)
filename = filename[0] if filename else None
else:
filename = os.path.basename(urlparse(url).path)
if not filename:
filename = f"download_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
# 保存文件
file_path = DOWNLOAD_DIR / filename
total_size = int(response.headers.get('content-length', 0))
downloaded = 0
with open(file_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=256*1024):
if chunk:
f.write(chunk)
downloaded += len(chunk)
if total_size:
progress(0.3 + 0.6 * (downloaded / total_size),
desc=f"下载中... {get_file_size_str(downloaded)}/{get_file_size_str(total_size)}")
file_size = os.path.getsize(file_path)
return (
str(file_path),
f"✅ 下载成功!\n文件: {filename}\n大小: {get_file_size_str(file_size)}"
)
except Exception as e:
return None, f"❌ 下载失败: {str(e)}"
def get_downloaded_files():
"""获取已下载的文件列表"""
files = []
for item in DOWNLOAD_DIR.iterdir():
if item.is_file():
size = item.stat().st_size
modified = datetime.fromtimestamp(item.stat().st_mtime).strftime('%Y-%m-%d %H:%M:%S')
files.append({
'name': item.name,
'path': str(item),
'size': get_file_size_str(size),
'modified': modified
})
return sorted(files, key=lambda x: x['modified'], reverse=True)
def delete_file(file_path):
"""删除文件"""
try:
if os.path.exists(file_path):
os.remove(file_path)
return f"✅ 已删除: {os.path.basename(file_path)}", refresh_file_list()
else:
return "❌ 文件不存在", refresh_file_list()
except Exception as e:
return f"❌ 删除失败: {str(e)}", refresh_file_list()
def refresh_file_list():
"""刷新文件列表"""
files = get_downloaded_files()
if not files:
return "📁 暂无已下载的文件"
html = "<div style='max-height: 400px; overflow-y: auto;'>"
for file in files:
html += f"""
<div style='border: 1px solid #ddd; padding: 10px; margin: 5px 0; border-radius: 5px;'>
<strong>📄 {file['name']}</strong><br>
<small>大小: {file['size']} | 修改时间: {file['modified']}</small><br>
<small style='color: #666;'>路径: {file['path']}</small>
</div>
"""
html += "</div>"
return html
def parse_headers(headers_text):
"""解析请求头文本"""
headers_dict = {}
if headers_text.strip():
for line in headers_text.strip().split('\n'):
if ':' in line:
key, value = line.split(':', 1)
headers_dict[key.strip()] = value.strip()
return headers_dict
# 创建Gradio界面
with gr.Blocks(title="文件下载器", theme=gr.themes.Soft()) as app:
gr.Markdown("# 🌐 多功能文件下载器")
gr.Markdown("支持Google Drive和普通HTTP链接下载")
with gr.Tab("📥 下载文件"):
with gr.Row():
with gr.Column(scale=2):
url_input = gr.Textbox(
label="下载链接",
placeholder="请输入Google Drive链接或其他下载链接...",
lines=2
)
with gr.Accordion("⚙️ 高级选项", open=False):
gr.Markdown("### 自定义请求头(每行一个,格式: Header: Value)")
headers_input = gr.Textbox(
label="请求头",
placeholder="Cookie: your_cookie_here\nAuthorization: Bearer your_token_here\nUser-Agent: Custom User Agent",
lines=5
)
gr.Markdown("**常用示例:**\n- `Cookie: session=xxx`\n- `Authorization: Bearer token123`\n- `User-Agent: Mozilla/5.0`")
download_btn = gr.Button("🚀 开始下载", variant="primary", size="lg")
with gr.Column(scale=1):
status_output = gr.Textbox(label="下载状态", lines=4)
file_output = gr.File(label="下载的文件")
# 下载按钮事件
download_btn.click(
fn=lambda url, headers: download_file(url, parse_headers(headers)),
inputs=[url_input, headers_input],
outputs=[file_output, status_output]
)
with gr.Tab("📂 文件浏览器"):
gr.Markdown("### 已下载的文件")
with gr.Row():
refresh_btn = gr.Button("🔄 刷新列表", size="sm")
file_list_html = gr.HTML(value=refresh_file_list())
gr.Markdown("### 文件操作")
with gr.Row():
file_path_input = gr.Textbox(
label="文件路径",
placeholder="粘贴文件路径或从上面复制...",
scale=3
)
delete_btn = gr.Button("🗑️ 删除文件", variant="stop", scale=1)
operation_status = gr.Textbox(label="操作状态", lines=2)
download_file_output = gr.File(label="下载文件")
# 刷新按钮事件
refresh_btn.click(
fn=refresh_file_list,
outputs=[file_list_html]
)
# 删除按钮事件
delete_btn.click(
fn=delete_file,
inputs=[file_path_input],
outputs=[operation_status, file_list_html]
)
# 点击文件路径自动加载文件用于下载
file_path_input.change(
fn=lambda path: path if os.path.exists(path) else None,
inputs=[file_path_input],
outputs=[download_file_output]
)
gr.Markdown("""
---
### 📖 使用说明
**下载功能:**
- 自动识别Google Drive链接(支持文件和文件夹)
- 支持自定义请求头(Cookie、Token等)
- 文件夹会自动压缩为ZIP格式
**文件浏览器:**
- 查看所有已下载的文件
- 直接下载文件到本地
- 删除不需要的文件
**支持的链接格式:**
- Google Drive: `https://drive.google.com/file/d/xxx` 或 `https://drive.google.com/folders/xxx`
- 普通HTTP/HTTPS下载链接
""")
# 启动应用
if __name__ == "__main__":
app.launch(
auth=(os.environ.get('username'), os.environ.get('password')),
ssr_mode=False
)