Spaces:
Running
Running
| import os | |
| import sys | |
| import io | |
| import warnings | |
| # 1. Environment & Protobuf Fixes (Must be at the absolute top) | |
| try: | |
| import google.protobuf.runtime_version as rv | |
| rv.ValidateProtobufRuntimeVersion = lambda *args, **kwargs: None | |
| except (ImportError, AttributeError): | |
| pass | |
| os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" | |
| os.environ["TF_ENABLE_ONEDNN_OPTS"] = "0" | |
| os.environ["PYTHONUTF8"] = "1" | |
| os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "python" | |
| warnings.filterwarnings("ignore", category=FutureWarning) | |
| warnings.filterwarnings("ignore", message=".*The name tf.losses.*") | |
| warnings.filterwarnings("ignore", message=".*use_fast is unset.*") | |
| warnings.filterwarnings("ignore", message=".*does not have a fast version.*") | |
| warnings.filterwarnings("ignore", message=".*Multiple distributions found for package optimum.*") | |
| # 1.1 DLL Bypass: Block broken torchaudio at sys.modules level (WinError 127 fix) | |
| # The AST feature extractor does `import torchaudio.compliance.kaldi` at module scope. | |
| # On this Windows system, torchaudio's native DLL is broken. We insert dummy modules | |
| # into sys.modules so the import succeeds silently, then tell transformers that | |
| # torchaudio/speech is unavailable so it uses its built-in numpy fallback instead. | |
| import types | |
| import importlib.machinery as _ilm | |
| def _block_torchaudio(): | |
| """Prevent broken torchaudio DLL from crashing the process.""" | |
| _dummy = types.ModuleType("torchaudio") | |
| _dummy.__version__ = "0.0.0" | |
| _dummy.__path__ = [] | |
| _dummy.__file__ = "blocked_by_fakeshield" | |
| _dummy.__loader__ = None | |
| _dummy.__package__ = "torchaudio" | |
| _dummy.__spec__ = _ilm.ModuleSpec("torchaudio", None, origin="blocked") | |
| sys.modules["torchaudio"] = _dummy | |
| for _sub in [ | |
| "torchaudio.functional", "torchaudio.transforms", | |
| "torchaudio.compliance", "torchaudio.compliance.kaldi", | |
| "torchaudio.sox_effects", "torchaudio.backend", | |
| "torchaudio._extension", "torchaudio._extension.utils", | |
| ]: | |
| _m = types.ModuleType(_sub) | |
| _m.__spec__ = _ilm.ModuleSpec(_sub, None, origin="blocked") | |
| _m.__path__ = [] | |
| _m.__package__ = _sub.rsplit(".", 1)[0] | |
| sys.modules[_sub] = _m | |
| # DLL Bypass: Attempt to import torchaudio. If it fails with a DLL error, apply the block. | |
| try: | |
| import torchaudio | |
| except OSError: | |
| _block_torchaudio() | |
| # Patch transformers availability checks so AST uses numpy mel-filterbank path | |
| try: | |
| import transformers.utils.import_utils as _tf_import_utils | |
| _tf_import_utils.is_torchaudio_available = lambda: False | |
| _tf_import_utils._torchaudio_available = False | |
| # is_speech_available checks torchaudio ΓÇö must also be False | |
| if hasattr(_tf_import_utils, "is_speech_available"): | |
| _tf_import_utils.is_speech_available = lambda: False | |
| except Exception: | |
| pass | |
| # 2. Force UTF-8 encoding for Windows terminals | |
| if sys.platform == "win32": | |
| try: | |
| sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') | |
| sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8') | |
| except Exception: | |
| pass | |
| def robust_print(msg, **kwargs): | |
| """Prints message while handling potential UnicodeEncodeErrors on legacy terminals.""" | |
| try: | |
| print(msg, **kwargs) | |
| except UnicodeEncodeError: | |
| # Fallback: strip non-ascii | |
| clean_msg = msg.encode('ascii', 'ignore').decode('ascii') | |
| print(clean_msg, **kwargs) | |
| # 3. Suppress Heavy Logging from AI Libraries | |
| import transformers | |
| transformers.logging.set_verbosity_error() | |
| from dotenv import load_dotenv | |
| load_dotenv() | |
| import os | |
| import importlib | |
| import importlib.metadata | |
| # importlib.metadata (already handled) | |
| import sys | |
| def _patch_metadata(mod): | |
| _orig_version = mod.version | |
| def _patched_version(pkg_name): | |
| try: | |
| v = _orig_version(pkg_name) | |
| if v: return v | |
| except Exception: | |
| pass | |
| name = pkg_name.lower() | |
| if name == "torch": | |
| try: | |
| import torch | |
| return torch.__version__.split('+')[0] | |
| except Exception: return "2.2.1" | |
| if name == "transformers": return "4.38.2" | |
| if name == "protobuf": return "4.25.3" | |
| return "1.0.0" | |
| mod.version = _patched_version | |
| _patch_metadata(importlib.metadata) | |
| # Also patch importlib_metadata backport if it exists | |
| try: | |
| import importlib_metadata | |
| _patch_metadata(importlib_metadata) | |
| except ImportError: | |
| pass | |
| from fastapi import FastAPI | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from app.routers.text_router import router as text_router | |
| from app.routers.image_router import router as image_router | |
| from app.routers.video_router import router as video_router | |
| from app.routers.audio_router import router as audio_router | |
| from app.routers.auth_router import router as auth_router | |
| from app.routers.dashboard_router import router as dashboard_router | |
| from app.routers.legacy_router import router as legacy_router | |
| # Forensic warm-up functions moved to background task to prevent startup hangs | |
| # (Imports moved inside the task below) | |
| app = FastAPI( | |
| title="FakeShield API", | |
| description="Industry-level multimodal deepfake detection", | |
| version="2.0.0", | |
| docs_url="/docs", | |
| redoc_url="/redoc" | |
| ) | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| app.include_router(text_router) | |
| app.include_router(image_router) | |
| app.include_router(video_router, prefix="/api/v1") | |
| app.include_router(audio_router, prefix="/api/v1") | |
| app.include_router(auth_router) | |
| app.include_router(dashboard_router) | |
| app.include_router(legacy_router) | |
| import asyncio | |
| async def startup_event(): | |
| from app.database import init_db | |
| try: | |
| await init_db() | |
| except Exception as e: | |
| robust_print(f"[DB] Database initialization failed: {e}") | |
| robust_print("[DB] Proceeding in OFFLINE mode (history and users will be unavailable).") | |
| robust_print("[START] Initializing FakeShield Engine (Background Warmup Enabled)...", flush=True) | |
| async def run_universal_warmup(): | |
| """Pre-loads all forensic labs in background to ensure zero-latency navigation.""" | |
| if os.environ.get("FAKESHIELD_SKIP_WARMUP") == "1": | |
| robust_print("[WARMUP] Skipped by environment flag.") | |
| return | |
| # 1. Text Lab (v16 Elite) | |
| try: | |
| from app.models.new_forensic_engine import load_models as load_text_models | |
| robust_print("[WARMUP] [1/3] Pre-loading Text Forensic Suite...", flush=True) | |
| await asyncio.to_thread(load_text_models) | |
| robust_print("[WARMUP] Text Lab ready.", flush=True) | |
| except Exception as e: | |
| robust_print(f"[WARMUP] Text load error: {e}") | |
| # 2. Image Lab (DINOv2 + ViT Ensemble) | |
| try: | |
| from app.models.image_detector import load_image_models | |
| robust_print("[WARMUP] [2/3] Pre-loading Image Forensic Suite...", flush=True) | |
| await asyncio.to_thread(load_image_models) | |
| robust_print("[WARMUP] Image Lab ready.", flush=True) | |
| except Exception as e: | |
| robust_print(f"[WARMUP] Image load error: {e}") | |
| # 3. Audio Lab (WavLM ITW) | |
| try: | |
| from app.models.audio.signal_wavlm import _load_model as load_wavlm | |
| robust_print("[WARMUP] [3/3] Pre-loading Audio Forensic Suite...", flush=True) | |
| await asyncio.to_thread(load_wavlm) | |
| robust_print("[WARMUP] Audio Lab ready.", flush=True) | |
| except Exception as e: | |
| robust_print(f"[WARMUP] Audio load error: {e}") | |
| robust_print("="*50) | |
| robust_print("--- [ALL ENGINES WARMED UP] ---") | |
| robust_print("="*50) | |
| # Launch universal warmup in background | |
| asyncio.create_task(run_universal_warmup()) | |
| # DEBUG: print all registered routes for troubleshooting | |
| try: | |
| robust_print("[ROUTES] Registered FastAPI routes:") | |
| try: | |
| openapi_paths = app.openapi().get("paths", {}).keys() | |
| for path in sorted(openapi_paths): | |
| robust_print(f"[OPENAPI] path={path}") | |
| except Exception as e: | |
| robust_print(f"[OPENAPI] path listing failed: {e}") | |
| def print_route(route): | |
| try: | |
| if hasattr(route, 'routes') and getattr(route, 'routes'): | |
| for child in route.routes: | |
| print_route(child) | |
| else: | |
| methods = ",".join(sorted(list(route.methods))) if hasattr(route, 'methods') and route.methods else '' | |
| robust_print(f"[ROUTE] path={getattr(route, 'path', str(route))} name={getattr(route, 'name', '')} methods={methods}") | |
| except Exception as e: | |
| robust_print(f"[ROUTE] path={getattr(route, 'path', str(route))} name={getattr(route, 'name', '')} methods=UNKNOWN error={e}") | |
| for r in app.routes: | |
| print_route(r) | |
| except Exception as e: | |
| robust_print(f"[ROUTES] debug failed: {e}") | |
| port = int(os.environ.get("PORT", os.environ.get("API_PORT", "7860"))) | |
| robust_print("-" * 50, flush=True) | |
| robust_print(f"FakeShield API is now ONLINE and listening on port {port}.", flush=True) | |
| robust_print("-" * 50, flush=True) | |
| def root(): | |
| return { | |
| "project": "FakeShield", | |
| "version": "2.0.0", | |
| "docs": "/docs", | |
| "panels": ["text", "image", "audio", "video"] | |
| } | |
| if __name__ == "__main__": | |
| import uvicorn | |
| port = int(os.environ.get("PORT", os.environ.get("API_PORT", "7860"))) | |
| uvicorn.run(app, host="0.0.0.0", port=port) | |