From 00000d9844836be08aa384881a8c78c275a46b2c Mon Sep 17 00:00:00 2001 From: doms9 <96013514+doms9@users.noreply.github.com> Date: Tue, 27 Jan 2026 20:48:44 -0500 Subject: [PATCH] e re-add totalsportek.py --- M3U8/fetch.py | 3 + M3U8/scrapers/totalsportek.py | 146 ++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 M3U8/scrapers/totalsportek.py diff --git a/M3U8/fetch.py b/M3U8/fetch.py index 3be0565f..fcae88c3 100644 --- a/M3U8/fetch.py +++ b/M3U8/fetch.py @@ -21,6 +21,7 @@ from scrapers import ( streamfree, streamhub, streamsgate, + totalsportek, tvpass, watchfooty, webcast, @@ -70,6 +71,7 @@ async def main() -> None: asyncio.create_task(streamcenter.scrape(xtrnl_brwsr)), # asyncio.create_task(streamhub.scrape(xtrnl_brwsr)), asyncio.create_task(streamsgate.scrape(xtrnl_brwsr)), + asyncio.create_task(totalsportek.scrape(hdl_brwsr)), asyncio.create_task(webcast.scrape(hdl_brwsr)), asyncio.create_task(watchfooty.scrape(xtrnl_brwsr)), ] @@ -112,6 +114,7 @@ async def main() -> None: | streamfree.urls | streamhub.urls | streamsgate.urls + | totalsportek.urls | tvpass.urls | watchfooty.urls | webcast.urls diff --git a/M3U8/scrapers/totalsportek.py b/M3U8/scrapers/totalsportek.py new file mode 100644 index 00000000..3ec911f0 --- /dev/null +++ b/M3U8/scrapers/totalsportek.py @@ -0,0 +1,146 @@ +from functools import partial +from urllib.parse import urljoin, urlparse + +from playwright.async_api import Browser +from selectolax.parser import HTMLParser + +from .utils import Cache, Time, get_logger, leagues, network + +log = get_logger(__name__) + +urls: dict[str, dict[str, str | float]] = {} + +TAG = "TOTALSPRTK" + +CACHE_FILE = Cache(TAG, exp=28_800) + +BASE_URL = "https://live3.totalsportek777.com/" + + +def fix_txt(s: str) -> str: + s = " ".join(s.split()) + + return s.upper() if s.islower() else s + + +async def get_events(cached_keys: list[str]) -> list[dict[str, str]]: + events = [] + + if not (html_data := await network.request(BASE_URL, log=log)): + return events + + soup = HTMLParser(html_data.content) + + sport = "Live Event" + + for node in soup.css("a"): + if not node.attributes.get("class"): + continue + + if (parent := node.parent) and "my-1" in parent.attributes.get("class", ""): + if span := node.css_first("span"): + sport = span.text(strip=True) + + sport = fix_txt(sport) + + if not (teams := [t.text(strip=True) for t in node.css(".col-7 .col-12")]): + continue + + if not (href := node.attributes.get("href")): + continue + + href = urlparse(href).path if href.startswith("http") else href + + if not (time_node := node.css_first(".col-3 span")): + continue + + if time_node.text(strip=True) != "MatchStarted": + continue + + event_name = fix_txt(" vs ".join(teams)) + + if f"[{sport}] {event_name} ({TAG})" in cached_keys: + continue + + events.append( + { + "sport": sport, + "event": event_name, + "link": urljoin(BASE_URL, href), + } + ) + + return events + + +async def scrape(browser: Browser) -> None: + cached_urls = CACHE_FILE.load() + + valid_urls = {k: v for k, v in cached_urls.items() if v["url"]} + + valid_count = cached_count = len(valid_urls) + + urls.update(valid_urls) + + log.info(f"Loaded {cached_count} event(s) from cache") + + log.info(f'Scraping from "{BASE_URL}"') + + events = await get_events(cached_urls.keys()) + + log.info(f"Processing {len(events)} new URL(s)") + + if events: + now = Time.clean(Time.now()) + + async with network.event_context(browser) as context: + for i, ev in enumerate(events, start=1): + async with network.event_page(context) as page: + handler = partial( + network.process_event, + url=ev["link"], + url_num=i, + page=page, + log=log, + ) + + url = await network.safe_process( + handler, + url_num=i, + semaphore=network.HTTP_S, + log=log, + ) + + sport, event, link = ( + ev["sport"], + ev["event"], + ev["link"], + ) + + key = f"[{sport}] {event} ({TAG})" + + tvg_id, logo = leagues.get_tvg_info(sport, event) + + entry = { + "url": url, + "logo": logo, + "base": link, + "timestamp": now.timestamp(), + "id": tvg_id or "Live.Event.us", + "link": link, + } + + cached_urls[key] = entry + + if url: + valid_count += 1 + + urls[key] = entry + + if new_count := valid_count - cached_count: + log.info(f"Collected and cached {new_count} new event(s)") + + else: + log.info("No new events found") + + CACHE_FILE.write(cached_urls)