#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 玩家投稿图片展示服务器 使用 Python 实现文件监控和 HTTP 服务 """ import os import json import time import threading from http.server import HTTPServer, SimpleHTTPRequestHandler from socketserver import ThreadingMixIn from urllib.parse import parse_qs, urlparse from datetime import datetime import mimetypes import hashlib # 配置 PORT = 3000 PLAYER_ART_DIR = os.path.join(os.path.dirname(__file__), 'player_art') ALLOWED_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.gif', '.webp'} # 全局变量 images_cache = [] last_cache_time = 0 CACHE_DURATION = 2 # 缓存持续时间(秒) class ImageCache: """图片缓存管理""" def __init__(self): self.images = [] self.last_update = 0 self.lock = threading.Lock() def update_cache(self): """更新缓存""" with self.lock: current_time = time.time() if current_time - self.last_update < CACHE_DURATION: return self.images self.images = self._load_images() self.last_update = current_time return self.images def _load_images(self): """加载图片列表""" images = [] try: if not os.path.exists(PLAYER_ART_DIR): print(f"警告: 目录 {PLAYER_ART_DIR} 不存在") return images files = os.listdir(PLAYER_ART_DIR) for filename in files: ext = os.path.splitext(filename)[1].lower() if ext in ALLOWED_EXTENSIONS: filepath = os.path.join(PLAYER_ART_DIR, filename) stat = os.stat(filepath) images.append({ 'name': filename, 'filename': filename, 'url': f'/player_art/{filename}', 'size': stat.st_size, 'date': datetime.fromtimestamp(stat.st_mtime).isoformat(), 'type': ext[1:] # 移除点号 }) # 按修改时间降序排列 images.sort(key=lambda x: x['date'], reverse=True) print(f"已加载 {len(images)} 张图片") except Exception as e: print(f"加载图片失败: {e}") return images class APIHandler(SimpleHTTPRequestHandler): """自定义 HTTP 请求处理器""" def __init__(self, *args, **kwargs): self.image_cache = image_cache super().__init__(*args, **kwargs) def do_GET(self): """处理 GET 请求""" parsed_path = urlparse(self.path) if parsed_path.path == '/api/images': self.handle_api_request() elif parsed_path.path == '/api/status': self.handle_status_request() else: # 处理静态文件 super().do_GET() def handle_api_request(self): """处理图片列表 API 请求""" try: images = self.image_cache.update_cache() response = { 'success': True, 'data': images, 'count': len(images), 'timestamp': time.time() } self.send_response(200) self.send_header('Content-Type', 'application/json') self.send_header('Access-Control-Allow-Origin', '*') self.end_headers() self.wfile.write(json.dumps(response, ensure_ascii=False).encode('utf-8')) except Exception as e: print(f"API 请求错误: {e}") self.send_error(500, str(e)) def handle_status_request(self): """处理状态检查请求""" try: images = self.image_cache.update_cache() response = { 'success': True, 'status': 'running', 'image_count': len(images), 'timestamp': time.time() } self.send_response(200) self.send_header('Content-Type', 'application/json') self.send_header('Access-Control-Allow-Origin', '*') self.end_headers() self.wfile.write(json.dumps(response, ensure_ascii=False).encode('utf-8')) except Exception as e: self.send_error(500, str(e)) def end_headers(self): """添加 CORS 头""" self.send_header('Access-Control-Allow-Origin', '*') self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') self.send_header('Access-Control-Allow-Headers', 'Content-Type') super().end_headers() def log_message(self, format, *args): """自定义日志输出""" timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') print(f"[{timestamp}] {format % args}") class ThreadedHTTPServer(ThreadingMixIn, HTTPServer): """多线程 HTTP 服务器""" daemon_threads = True def start_file_monitor(): """启动文件监控线程""" print("文件监控线程已启动") last_images = set() while True: try: images = image_cache.update_cache() current_images = {img['filename'] for img in images} if current_images != last_images: if len(current_images) != len(last_images): print(f"检测到图片变化: {len(last_images)} -> {len(current_images)} 张") else: print("检测到图片内容变化") last_images = current_images time.sleep(3) # 每3秒检查一次 except Exception as e: print(f"文件监控错误: {e}") time.sleep(5) # 创建全局缓存实例 image_cache = ImageCache() def main(): """主函数""" print("=" * 50) print("玩家投稿图片展示服务器") print("=" * 50) print(f"服务器地址: http://localhost:{PORT}") print(f"监控目录: {PLAYER_ART_DIR}") print(f"支持格式: {', '.join(ALLOWED_EXTENSIONS)}") print("=" * 50) print() # 确保目录存在 if not os.path.exists(PLAYER_ART_DIR): print(f"创建目录: {PLAYER_ART_DIR}") os.makedirs(PLAYER_ART_DIR) # 启动文件监控线程 monitor_thread = threading.Thread(target=start_file_monitor, daemon=True) monitor_thread.start() # 启动 HTTP 服务器 try: server = ThreadedHTTPServer(('0.0.0.0', PORT), APIHandler) print(f"服务器正在运行,按 Ctrl+C 停止") print() server.serve_forever() except KeyboardInterrupt: print("\n正在关闭服务器...") server.shutdown() print("服务器已关闭") except Exception as e: print(f"服务器错误: {e}") if __name__ == '__main__': main()