"""
🛰️ Spy Satellite Simulator — Globe via base64 iframe
"""
import gradio as gr
import time, threading, logging, json, os, base64
from datetime import datetime
from data_fetcher import DataManager
logging.basicConfig(level=logging.INFO)
log = logging.getLogger("app")
CESIUM_TOKEN = os.environ.get("CESIUM_TOKEN",
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJmMzQ3OTJjZi01NmI0LTQ2MDItOWM1MC05ZWJjNWFlMDNiZTciLCJpZCI6MTkzMzUwLCJpYXQiOjE3NzQ4ODQ1OTR9.9AvXB2qj1fxNQzll4GVfKsqQtFBtpkGV9zUAedZe_fs")
dm = DataManager()
# ═══ Initial fetch at startup ═══
log.info("🚀 Starting initial data fetch...")
dm.fetch_all()
log.info("🚀 Initial fetch complete!")
# ═══ Background auto-refresh ═══
def bg_fetch():
while True:
time.sleep(300) # Wait 5 min before next
try: dm.fetch_all()
except Exception as e: log.error(f"BG: {e}")
threading.Thread(target=bg_fetch, daemon=True).start()
# ═══ Globe HTML ═══
def make_globe_html(data_json):
return '''
🛰️ نظام المراقبة الفضائية
✈️ 0🚢 0⚔️ 0📰 0
⚔️ أحداث
✈️ طيران
📰 أخبار
📡 مصادر
🛰️ Spy Satellite — Real Data
'''.replace('TOKEN_PLACEHOLDER', CESIUM_TOKEN).replace('DATA_PLACEHOLDER', data_json)
# ═══ Globe as base64 iframe (GUARANTEED to work) ═══
def get_globe():
data_json = dm.to_json()
html = make_globe_html(data_json)
b64 = base64.b64encode(html.encode('utf-8')).decode('utf-8')
return f''
def build_stats():
ss = dm.status
now = datetime.now().strftime("%H:%M:%S")
src_list = [
("OpenSky Network", "✈️", ss.get("OpenSky",{}).get("c",0), ss.get("OpenSky",{}).get("s","offline")),
("AIS/DigiTraffic", "🚢", ss.get("Ships",{}).get("c",0), ss.get("Ships",{}).get("s","offline")),
("GDELT", "⚔️", ss.get("GDELT",{}).get("c",0), ss.get("GDELT",{}).get("s","offline")),
("RSS (13 مصدر)", "📰", ss.get("RSS",{}).get("c",0), ss.get("RSS",{}).get("s","offline")),
("Insecam", "📷", ss.get("Cameras",{}).get("c",0), ss.get("Cameras",{}).get("s","offline")),
]
items = ""
for name, icon, count, status in src_list:
color = "#22c55e" if status == "online" else "#ef4444"
label = "متصل ✅" if status == "online" else "خطأ ❌"
items += f'''
{count}
{icon} {name}
{label}
'''
return f'{items}
🕐 {now}
'
def build_log():
return "\n".join(list(dm.logs)[-30:]) or "⏳ لم يبدأ بعد"
def manual_refresh():
dm.fetch_all()
return get_globe(), build_stats(), build_log()
CSS = """
@import url('https://fonts.googleapis.com/css2?family=Cairo:wght@400;600;700;800&display=swap');
* {font-family:'Cairo',sans-serif!important}
.gradio-container {max-width:100%!important;background:#0a0e17!important;color:#e2e8f0!important;padding:8px!important}
.dark {background:#0a0e17!important}
.tab-nav button {background:#1a2332!important;color:#94a3b8!important;border:1px solid #2a3a4e!important;border-radius:8px!important;font-weight:700!important}
.tab-nav button.selected {background:rgba(14,165,233,0.15)!important;color:#0ea5e9!important;border-color:#0ea5e9!important}
.block {background:transparent!important;border:none!important}
label {color:#94a3b8!important}
textarea {background:#0f172a!important;color:#94a3b8!important;border:1px solid #2a3a4e!important;border-radius:8px!important;font-size:11px!important}
button.primary {background:linear-gradient(135deg,#0ea5e9,#06b6d4)!important;border:none!important;font-weight:700!important;border-radius:8px!important;color:white!important}
footer {display:none!important}
"""
with gr.Blocks(css=CSS, title="🛰️ نظام المراقبة الفضائية", theme=gr.themes.Base(primary_hue="sky", neutral_hue="slate")) as app:
gr.HTML("""
🛰️
مركز رصد النزاعات — محاكي الأقمار الاصطناعية
CesiumJS 3D · OpenSky Network · AISStream · GDELT · 16 RSS · Insecam — Real APIs
""")
stats_display = gr.HTML(value=build_stats())
refresh_btn = gr.Button("📡 تحديث من المصادر", variant="primary", size="lg")
with gr.Tabs():
with gr.Tab("🌍 الكرة الأرضية ثلاثية الأبعاد"):
globe_display = gr.HTML(value=get_globe())
with gr.Tab("📋 سجل التحديث"):
log_display = gr.Textbox(value=build_log(), label="سجل جمع البيانات", lines=25, interactive=False)
refresh_btn.click(fn=manual_refresh, outputs=[globe_display, stats_display, log_display])
if __name__ == "__main__":
app.launch(server_name="0.0.0.0", server_port=7860, show_error=True)