Team Ai
Apppublic

selfserv/shellfish

sourceHugging Facemitupdated 5mo agoView on Hugging Face
0likes
app.py344 linesDownload Raw Back to root
1import re2import time3import json4import asyncio5import urllib36from contextlib import asynccontextmanager7from fastapi import FastAPI, Request8from fastapi.responses import HTMLResponse, StreamingResponse9from playwright.async_api import async_playwright10 11# Standard requests for KB12import requests13# curl_cffi for TQ14from curl_cffi import requests as cffi_requests15from bs4 import BeautifulSoup16 17# Suppress insecure request warnings for KB18urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)19 20# --- GLOBAL VARIABLES ---21playwright_instance = None22browser = None23 24@asynccontextmanager25async def lifespan(app: FastAPI):26    global playwright_instance, browser27    playwright_instance = await async_playwright().start()28    29    # Launch browser for KL in background30    browser = await playwright_instance.chromium.launch(31        headless=True,32        args=[33            "--no-sandbox",34            "--disable-setuid-sandbox",35            "--disable-dev-shm-usage",36            "--disable-blink-features=AutomationControlled"37        ]38    )39    yield40    41    # Clean up on shutdown42    await browser.close()43    await playwright_instance.stop()44 45app = FastAPI(lifespan=lifespan)46 47# --- FORMATTING HELPER ---48def format_inr(number):49    """Formats a number into the Indian numbering system pattern (e.g., 00,00,000)"""50    try:51        if number is None or number == "N/A":52            return "N/A"53            54        # Round the float and convert to int to drop decimals safely55        num_str = str(int(round(float(number))))56        57        if len(num_str) <= 3:58            return num_str59            60        last_three = num_str[-3:]61        rest = num_str[:-3]62        chunks = []63        64        while rest:65            chunks.append(rest[-2:])66            rest = rest[:-2]67            68        return ",".join(reversed(chunks)) + "," + last_three69    except Exception:70        return str(number)71 72# --- SCRAPING LOGIC ---73def fetch_kb():74    start_time = time.time()75    TARGET_URL = "https://bcast.kartikeybullion.com:7768/VOTSBroadcastStreaming/Services/xml/GetLiveRateByTemplateID/kartikeyallcity"76    77    try:78        current_timestamp = int(time.time() * 1000)79        url = f"{TARGET_URL}?_={current_timestamp}"80        headers = {'User-Agent': 'Mozilla/5.0'}81        82        response = requests.get(url, headers=headers, timeout=10, verify=False)83        response.raise_for_status()84        85        kb1 = "N/A"86        kb2 = "N/A"87        88        for line in response.text.splitlines():89            if "5391" in line and "GOLD 995" in line:90                numbers = re.findall(r'\b\d{5,7}\b', line)91                if numbers:92                    kb1 = format_inr(int(numbers[0]))93            elif "4998" in line and "GOLD 995" in line:94                numbers = re.findall(r'\b\d{5,7}\b', line)95                if numbers:96                    kb2 = format_inr(int(numbers[0]))97            98            if kb1 != "N/A" and kb2 != "N/A":99                break100                101        elapsed = round(time.time() - start_time, 2)102        103        return {104            "source": "KB",105            "kb1": kb1,106            "kb2": kb2,107            "elapsed_sec": elapsed,108            "status": "success"109        }110    except Exception as e:111        elapsed = round(time.time() - start_time, 2)112        return {113            "source": "KB",114            "error": str(e),115            "elapsed_sec": elapsed,116            "status": "error"117        }118 119async def fetch_kl():120    start_time = time.time()121    URL = "https://store.kalyanjewellers.net/gold-rate/Samastipur"122    global browser123    124    context = await browser.new_context(125        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"126    )127    page = await context.new_page()128    129    async def route_intercept(route):130        if route.request.resource_type in ["image", "stylesheet", "font", "media"]:131            await route.abort()132        else:133            await route.continue_()134            135    await page.route("**/*", route_intercept)136    137    try:138        await page.goto(URL, wait_until="domcontentloaded", timeout=45000)139        await page.wait_for_selector("text=gold", timeout=15000)140        body_text = await page.inner_text("body")141        142        match_24k = re.search(r'₹\s*([\d,]+)[^₹]*?24k gold', body_text, re.IGNORECASE | re.DOTALL)143        kl24 = format_inr(match_24k.group(1).replace(',', '').strip()) if match_24k else "N/A"144        145        match_22k = re.search(r'₹\s*([\d,]+)[^₹]*?22k gold', body_text, re.IGNORECASE | re.DOTALL)146        kl22 = format_inr(match_22k.group(1).replace(',', '').strip()) if match_22k else "N/A"147        148        elapsed = round(time.time() - start_time, 2)149        return {150            "source": "KL",151            "kl24": kl24,152            "kl22": kl22,153            "elapsed_sec": elapsed,154            "status": "success"155        }156    except Exception as e:157        elapsed = round(time.time() - start_time, 2)158        return {159            "source": "KL",160            "error": str(e),161            "elapsed_sec": elapsed,162            "status": "error"163        }164    finally:165        await context.close()166 167def fetch_tq():168    start_time = time.time()169    url = "https://www.tanishq.co.in/gold-rate.html?lang=en_IN"170    171    try:172        response = cffi_requests.get(url, impersonate="chrome", timeout=10)173        if response.status_code != 200:174            return {175                "source": "TQ",176                "error": f"HTTP {response.status_code}",177                "elapsed_sec": round(time.time() - start_time, 2),178                "status": "error"179            }180            181        soup = BeautifulSoup(response.text, "lxml")182        gram_cells = soup.find_all(lambda tag: tag.name == "td" and "1 G" in tag.text.upper())183        prices = []184        185        for gram_cell in gram_cells:186            sibling = gram_cell.find_next_sibling("td")187            if sibling:188                match = re.search(r'[\d,]+', sibling.text.strip())189                if match:190                    prices.append(int(match.group(0).replace(',', '')))191                    192        elapsed = round(time.time() - start_time, 2)193        194        if prices:195            tq22_raw = min(prices) * 10196            tq22 = format_inr(tq22_raw)197            return {198                "source": "TQ",199                "tq22": tq22,200                "elapsed_sec": elapsed,201                "status": "success"202            }203            204        return {205            "source": "TQ",206            "error": "HTML parse error",207            "elapsed_sec": elapsed,208            "status": "error"209        }210    except Exception as e:211        elapsed = round(time.time() - start_time, 2)212        return {213            "source": "TQ",214            "error": str(e),215            "elapsed_sec": elapsed,216            "status": "error"217        }218 219# --- API ENDPOINTS ---220@app.get("/api/kb")221def api_kb():222    return fetch_kb()223 224@app.get("/api/kl")225async def api_kl():226    return await fetch_kl()227 228@app.get("/api/tq")229def api_tq():230    return fetch_tq()231 232# --- FRONTEND & STREAMING ROUTE ---233def get_html_dashboard():234    html_content = """<!DOCTYPE html>235<html>236<head>237    <title>API Status</title>238    <style>239        body { background-color: #0e0e0e; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; margin: 16px; }240        .line { margin: 0; padding: 0; line-height: 1.4; white-space: pre; }241    </style>242</head>243<body>244    <div class="line">KB : <span id="kb-val">loading . . .</span></div>245    <div class="line">KL : <span id="kl-val">loading . . .</span></div>246    <div class="line">TQ : <span id="tq-val">loading . . .</span></div>247    248    <script>249        function fetchKB() {250            fetch('/api/kb')251                .then(response => response.json())252                .then(data => {253                    if (data.status === "error") {254                        document.getElementById('kb-val').innerText = 'Error (' + data.elapsed_sec + ' sec)';255                    } else {256                        document.getElementById('kb-val').innerText = '"' + data.kb1 + '" / "' + data.kb2 + '" (' + data.elapsed_sec + ' sec)';257                    }258                })259                .catch(err => {260                    document.getElementById('kb-val').innerText = 'error loading data';261                });262        }263        264        function fetchKL() {265            fetch('/api/kl')266                .then(response => response.json())267                .then(data => {268                    if (data.status === "error") {269                        document.getElementById('kl-val').innerText = 'Error (' + data.elapsed_sec + ' sec)';270                    } else {271                        document.getElementById('kl-val').innerText = '24K "' + data.kl24 + '" / 22K "' + data.kl22 + '" (' + data.elapsed_sec + ' sec)';272                    }273                })274                .catch(err => {275                    document.getElementById('kl-val').innerText = 'error loading data';276                });277        }278        279        function fetchTQ() {280            fetch('/api/tq')281                .then(response => response.json())282                .then(data => {283                    if (data.status === "error") {284                        document.getElementById('tq-val').innerText = 'Error (' + data.elapsed_sec + ' sec)';285                    } else {286                        document.getElementById('tq-val').innerText = '22K "' + data.tq22 + '" (' + data.elapsed_sec + ' sec)';287                    }288                })289                .catch(err => {290                    document.getElementById('tq-val').innerText = 'error loading data';291                });292        }293        294        // Start all background fetches295        fetchKB();296        fetchKL();297        fetchTQ();298    </script>299</body>300</html>"""301    return HTMLResponse(content=html_content)302 303@app.get("/api/all")304async def api_all():305    """Fetches all data concurrently and returns a single JSON object. Ideal for mobile apps."""306    loop = asyncio.get_running_loop()307    308    # Run the synchronous fetchers in threads, and await the async one309    kb_task = loop.run_in_executor(None, fetch_kb)310    kl_task = fetch_kl()311    tq_task = loop.run_in_executor(None, fetch_tq)312    313    # Wait for all of them to finish (takes about 3-5 seconds)314    kb_res, kl_res, tq_res = await asyncio.gather(kb_task, kl_task, tq_task)315    316    return {317        "kb": kb_res,318        "kl": kl_res,319        "tq": tq_res320    }321 322@app.get("/")323async def index(request: Request):324    accept_header = request.headers.get("accept", "")325    326    if "text/html" in accept_header:327        return get_html_dashboard()328        329    async def generate_stream():330        loop = asyncio.get_running_loop()331        tasks = [332            asyncio.ensure_future(loop.run_in_executor(None, fetch_kb)),333            asyncio.ensure_future(fetch_kl()),334            asyncio.ensure_future(loop.run_in_executor(None, fetch_tq))335        ]336        337        for coro in asyncio.as_completed(tasks):338            try:339                result = await coro340                yield json.dumps(result) + "\n"341            except Exception as e:342                yield json.dumps({"error": str(e), "status": "error"}) + "\n"343                344    return StreamingResponse(generate_stream(), media_type="application/x-ndjson")