from flask import Blueprint, render_template, redirect, request, Response import requests main_bp = Blueprint('main', __name__) @main_bp.route('/') def index(): from models import SiteConfig config = {} configs = SiteConfig.query.all() for c in configs: config[c.config_key] = c.config_value return render_template('index.html', config=config) @main_bp.route('/api-docs') def api_docs(): """API 文档页面""" return render_template('api_docs.html') @main_bp.route('/favicon.ico') def favicon(): """处理 favicon 请求""" from models import SiteConfig favicon_config = SiteConfig.query.filter_by(config_key='site_favicon').first() if favicon_config and favicon_config.config_value: return redirect(favicon_config.config_value) # 如果没有设置 favicon,尝试使用 logo logo_config = SiteConfig.query.filter_by(config_key='site_logo').first() if logo_config and logo_config.config_value: return redirect(logo_config.config_value) # 返回空响应避免 404 return '', 204 @main_bp.route('/proxy/download') def proxy_download(): """代理下载视频/图片,绕过防盗链,支持 Range 请求""" url = request.args.get('url') if not url: return 'Missing url parameter', 400 # 限制只能代理特定域名 allowed_domains = [ 'weibocdn.com', 'sinaimg.cn', 'weibo.com', 'ppxvod.com', 'byteimg.com', 'pipix.com', '365yg.com', 'ixigua.com', 'kwimgs.com', 'kwaicdn.com', 'kuaishou.com', 'gifshow.com', 'yximgs.com', 'douyinvod.com', 'douyin.com', 'tiktokcdn.com', 'bytedance.com', 'bilivideo.com', 'bilibili.com', 'hdslb.com', 'zjcdn.com', 'douyinpic.com' ] from urllib.parse import urlparse parsed = urlparse(url) domain = parsed.netloc.lower() is_allowed = any(d in domain for d in allowed_domains) if not is_allowed: return f'Domain not allowed: {domain}', 403 # 根据域名设置不同的 Referer referer_map = { 'weibo': 'https://weibo.com/', 'sina': 'https://weibo.com/', 'pipix': 'https://www.pipix.com/', '365yg': 'https://www.pipix.com/', 'ixigua': 'https://www.ixigua.com/', 'kuaishou': 'https://www.kuaishou.com/', 'kwimgs': 'https://www.kuaishou.com/', 'kwaicdn': 'https://www.kuaishou.com/', 'yximgs': 'https://www.kuaishou.com/', 'douyin': 'https://www.douyin.com/', 'bytedance': 'https://www.douyin.com/', 'zjcdn': 'https://www.douyin.com/', 'douyinpic': 'https://www.douyin.com/', 'bilibili': 'https://www.bilibili.com/', 'hdslb': 'https://www.bilibili.com/', } referer = 'https://www.google.com/' for key, ref in referer_map.items(): if key in domain: referer = ref break try: headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Referer': referer } # 转发 Range 请求头,支持视频进度条拖动 range_header = request.headers.get('Range') if range_header: headers['Range'] = range_header resp = requests.get(url, headers=headers, stream=True, timeout=30) # 获取文件类型 content_type = resp.headers.get('Content-Type', 'application/octet-stream') # 设置下载文件名 if 'video' in content_type: filename = f'video_{int(__import__("time").time())}.mp4' elif 'image' in content_type: filename = f'image_{int(__import__("time").time())}.jpg' else: filename = f'file_{int(__import__("time").time())}' def generate(): for chunk in resp.iter_content(chunk_size=8192): yield chunk # 构建响应头 response_headers = { 'Content-Type': content_type, 'Accept-Ranges': 'bytes' } # 转发相关响应头 if resp.headers.get('Content-Length'): response_headers['Content-Length'] = resp.headers.get('Content-Length') if resp.headers.get('Content-Range'): response_headers['Content-Range'] = resp.headers.get('Content-Range') # 根据是否是 Range 请求返回不同状态码 status_code = resp.status_code return Response( generate(), status=status_code, headers=response_headers ) except Exception as e: return f'Download failed: {str(e)}', 500