Compare commits

..

No commits in common. "dfea060407798bd0c6a1122e569392abca48b0c4" and "d5f1410a03b06b3b56a29dfd2b7b718f46d7d432" have entirely different histories.

11 changed files with 113333 additions and 115846 deletions

File diff suppressed because it is too large Load diff

222214
M3U8/TV.xml

File diff suppressed because one or more lines are too long

View file

@ -13,7 +13,7 @@ http://23.237.104.106:8080/USA_ACCN/mpegts
https://turnerlive.warnermediacdn.com/hls/live/2023183/aseast/noslate/VIDEO_1_5128000.m3u8
#EXTINF:-1 tvg-chno="5" tvg-id="Altitude.Sports.us2" tvg-name="Altitude Sports" tvg-logo="http://schedulesdirect-api20141201-logos.s3.dualstack.us-east-1.amazonaws.com/stationLogos/s44263_dark_360w_270h.png" group-title="TV",Altitude Sports
http://mytvstream.net:8080/live/8434692955/judgen64@yahoo.com/79545.m3u8
http://mytvstream.net:8080/live/56481106/11185695/79545.m3u8
#EXTINF:-1 tvg-chno="6" tvg-id="AMC.HD.us2" tvg-name="AMC" tvg-logo="https://schedulesdirect-api20141201-logos.s3.dualstack.us-east-1.amazonaws.com/stationLogos/s10021_dark_360w_270h.png" group-title="TV",AMC
http://23.239.31.26:8989/amc/index.m3u8
@ -22,7 +22,7 @@ http://23.239.31.26:8989/amc/index.m3u8
http://23.237.104.106:8080/USA_ANIMAL_PLANET/index.m3u8
#EXTINF:-1 tvg-chno="8" tvg-id="Antenna.TV.us2" tvg-name="Antenna TV" tvg-logo="https://cdn.tvpassport.com/image/station/240x135/v3/70248-224D0.png" group-title="TV",Antenna TV
http://mytvstream.net:8080/live/8434692955/judgen64@yahoo.com/20180.m3u8
http://mytvstream.net:8080/live/56481106/11185695/20180.m3u8
#EXTINF:-1 tvg-chno="9" tvg-id="ASPiRE.HD.us2" tvg-name="Aspire" tvg-logo="https://i.gyazo.com/0dec42cc5ef48c489cc10db906dc5b9b.png" group-title="TV",Aspire
http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/150605.ts
@ -157,7 +157,7 @@ http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/296662.ts
http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/58557.ts
#EXTINF:-1 tvg-chno="53" tvg-id="FanDuel.Sports.Network.North.us" tvg-name="FDSN North" tvg-logo="https://schedulesdirect-api20141201-logos.s3.dualstack.us-east-1.amazonaws.com/stationLogos/s10977_dark_360w_270h.png" group-title="TV",FDSN North
http://mytvstream.net:8080/live/8434692955/judgen64@yahoo.com/20928.m3u8
http://mytvstream.net:8080/live/56481106/11185695/20928.m3u8
#EXTINF:-1 tvg-chno="54" tvg-id="FanDuel.Sports.Network.Ohio.-.Cleveland.us" tvg-name="FDSN Ohio" tvg-logo="http://schedulesdirect-api20141201-logos.s3.dualstack.us-east-1.amazonaws.com/stationLogos/s49691_dark_360w_270h.png" group-title="TV",FDSN Ohio
http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/296675.ts
@ -217,7 +217,7 @@ http://23.237.104.106:8080/USA_FUSE/index.m3u8
http://23.237.104.106:8080/USA_FX/index.m3u8
#EXTINF:-1 tvg-chno="73" tvg-id="FX.Movie.Channel.HD.us2" tvg-name="FX Movie Channel" tvg-logo="http://schedulesdirect-api20141201-logos.s3.dualstack.us-east-1.amazonaws.com/stationLogos/s70253_dark_360w_270h.png" group-title="TV",FX Movie Channel
http://mytvstream.net:8080/live/8434692955/judgen64@yahoo.com/10260.m3u8
http://mytvstream.net:8080/live/56481106/11185695/10260.m3u8
#EXTINF:-1 tvg-chno="74" tvg-id="FXX.HD.us2" tvg-name="FXX" tvg-logo="https://raw.githubusercontent.com/tv-logo/tv-logos/refs/heads/main/countries/united-states/fxx-us.png" group-title="TV",FXX
http://23.237.104.106:8080/USA_FXX/index.m3u8
@ -397,13 +397,13 @@ http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/8585.ts
http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/5831.ts
#EXTINF:-1 tvg-chno="133" tvg-id="Space.City.Home.Network.HD.us2" tvg-name="Space City Home Network" tvg-logo="http://schedulesdirect-api20141201-logos.s3.dualstack.us-east-1.amazonaws.com/stationLogos/s77744_dark_360w_270h.png" group-title="TV",Space City Home Network
http://mytvstream.net:8080/live/8434692955/judgen64@yahoo.com/213668.m3u8
http://mytvstream.net:8080/live/56481106/11185695/213668.m3u8
#EXTINF:-1 tvg-chno="134" tvg-id="Spectrum.SportsNet.LA.Dodgers.HD.us2" tvg-name="Spectrum SportsNet LA Dodgers" tvg-logo="https://i.gyazo.com/765cce528ddda366695bb178d9dee6da.png" group-title="TV",Spectrum SportsNet LA Dodgers
http://mytvstream.net:8080/live/8434692955/judgen64@yahoo.com/31636.m3u8
http://mytvstream.net:8080/live/56481106/11185695/31636.m3u8
#EXTINF:-1 tvg-chno="135" tvg-id="Spectrum.SportsNet.Lakers.HD.us2" tvg-name="Spectrum SportsNet Lakers" tvg-logo="http://schedulesdirect-api20141201-logos.s3.dualstack.us-east-1.amazonaws.com/stationLogos/s77422_dark_360w_270h.png" group-title="TV",Spectrum SportsNet Lakers
http://mytvstream.net:8080/live/8434692955/judgen64@yahoo.com/21842.m3u8
http://mytvstream.net:8080/live/56481106/11185695/21842.m3u8
#EXTINF:-1 tvg-chno="136" tvg-id="Sportsman.Channel.us2" tvg-name="Sportsman Channel" tvg-logo="https://schedulesdirect-api20141201-logos.s3.dualstack.us-east-1.amazonaws.com/stationLogos/s33930_dark_360w_270h.png" group-title="TV",Sportsman Channel
http://212.102.60.231/SPORTSMAN_CHANNEL/index.m3u8

File diff suppressed because it is too large Load diff

View file

@ -8,11 +8,12 @@ from scrapers import (
cdnlivetv,
embedhd,
fawa,
fsports,
istreameast,
livetvsx,
mainportal,
ovogoal,
pawa,
ppv,
roxie,
shark,
streamcenter,
@ -61,7 +62,7 @@ async def main() -> None:
pw_tasks = [
asyncio.create_task(cdnlivetv.scrape(xtrnl_brwsr)),
asyncio.create_task(embedhd.scrape(hdl_brwsr)),
asyncio.create_task(fsports.scrape(xtrnl_brwsr)),
# asyncio.create_task(ppv.scrape(xtrnl_brwsr)),
asyncio.create_task(roxie.scrape(hdl_brwsr)),
asyncio.create_task(streamhub.scrape(xtrnl_brwsr)),
asyncio.create_task(watchfooty.scrape(xtrnl_brwsr)),
@ -71,6 +72,7 @@ async def main() -> None:
asyncio.create_task(fawa.scrape()),
asyncio.create_task(istreameast.scrape()),
asyncio.create_task(mainportal.scrape()),
# asyncio.create_task(ovogoal.scrape()),
asyncio.create_task(pawa.scrape()),
asyncio.create_task(shark.scrape()),
asyncio.create_task(streamcenter.scrape()),
@ -95,11 +97,12 @@ async def main() -> None:
cdnlivetv.urls
| embedhd.urls
| fawa.urls
| fsports.urls
| istreameast.urls
| livetvsx.urls
| mainportal.urls
| ovogoal.urls
| pawa.urls
| ppv.urls
| roxie.urls
| shark.urls
| streamcenter.urls

View file

@ -123,7 +123,7 @@ async def scrape(browser: Browser) -> None:
entry = {
"url": url,
"logo": logo,
"base": "https://exposestrat.com/",
"base": "https://vividmosaica.com/",
"timestamp": ts,
"id": tvg_id or "Live.Event.us",
"link": link,

View file

@ -1,125 +0,0 @@
from functools import partial
from urllib.parse import urljoin
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 = "FSPRTS"
CACHE_FILE = Cache(TAG, exp=5_400)
BASE_URL = "https://fsportshdd.club"
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 card in soup.css(".media.btn.btn-default.btn-lg.btn-block"):
if card.css_first('[id^="countdown-"]'):
continue
if not (name_elem := card.css_first("h4")):
continue
if sport_elem := card.css_first("h5"):
if (sport := sport_elem.text(strip=True)).lower() == "no league":
sport = "Live Event"
if not (a_elem := card.css_first("a")) or not (
href := a_elem.attributes.get("href")
):
continue
name = name_elem.text(strip=True)
if f"[{sport}] {name} ({TAG})" in cached_keys:
continue
events.append(
{
"sport": sport,
"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}"')
if events := await get_events(cached_urls.keys()):
log.info(f"Processing {len(events)} new URL(s)")
now = Time.clean(Time.now())
async with network.event_context(browser, stealth=False) as context:
for i, ev in enumerate(events, start=1):
async with network.event_page(context) as page:
handler = partial(
network.process_event,
url=(link := ev["link"]),
url_num=i,
page=page,
log=log,
)
url = await network.safe_process(
handler,
url_num=i,
semaphore=network.PW_S,
log=log,
)
sport, event = ev["sport"], ev["event"]
key = f"[{sport}] {event} ({TAG})"
tvg_id, logo = leagues.get_tvg_info(sport, event)
entry = {
"url": url,
"logo": logo,
"base": "https://exposestrat.com/",
"timestamp": now.timestamp(),
"id": tvg_id or "Live.Event.us",
"link": link,
}
cached_urls[key] = entry
if url:
valid_count += 1
urls[key] = entry
log.info(f"Collected and cached {valid_count - cached_count} new event(s)")
else:
log.info("No new events found")
CACHE_FILE.write(cached_urls)

144
M3U8/scrapers/ovogoal.py Normal file
View file

@ -0,0 +1,144 @@
import re
from functools import partial
from urllib.parse import urljoin
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 = "OVO"
CACHE_FILE = Cache(TAG, exp=28_800)
BASE_URL = "https://ovogoaal.com"
async def process_event(url: str, url_num: int) -> tuple[str | None, str | None]:
nones = None, None
if not (html_data := await network.request(url, log=log)):
log.warning(f"URL {url_num}) Failed to load url.")
return nones
soup = HTMLParser(html_data.content)
iframe = soup.css_first("iframe")
if not iframe or not (iframe_src := iframe.attributes.get("src")):
log.warning(f"URL {url_num}) No iframe element found.")
return nones
if not (
iframe_src_data := await network.request(
iframe_src,
headers={"Referer": url},
log=log,
)
):
log.warning(f"URL {url_num}) Failed to load iframe source.")
return nones
valid_m3u8 = re.compile(r'(var|const)\s+(\w+)\s*=\s*"([^"]*)"', re.I)
if not (match := valid_m3u8.search(iframe_src_data.text)):
log.warning(f"URL {url_num}) No Clappr source found.")
return nones
log.info(f"URL {url_num}) Captured M3U8")
return match[3], iframe_src
async def get_events() -> 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 card in soup.css(".main-content .stream-row"):
if (not (watch_btn_elem := card.css_first(".watch-btn"))) or (
not (onclick := watch_btn_elem.attributes.get("onclick"))
):
continue
if not (event_name_elem := card.css_first(".stream-info")):
continue
href = onclick.split(".href=")[-1].replace("'", "")
event_name = event_name_elem.text(strip=True)
events.append(
{
"sport": sport,
"event": event_name,
"link": urljoin(f"{html_data.url}", href),
}
)
return events
async def scrape() -> None:
if cached_urls := CACHE_FILE.load():
urls.update({k: v for k, v in cached_urls.items() if v["url"]})
log.info(f"Loaded {len(urls)} event(s) from cache")
return
log.info(f'Scraping from "{BASE_URL}"')
if events := await get_events():
log.info(f"Processing {len(events)} URL(s)")
now = Time.clean(Time.now())
for i, ev in enumerate(events, start=1):
handler = partial(
process_event,
url=(link := ev["link"]),
url_num=i,
)
url, iframe = await network.safe_process(
handler,
url_num=i,
semaphore=network.HTTP_S,
log=log,
)
sport, event = ev["sport"], ev["event"]
key = f"[{sport}] {event} ({TAG})"
tvg_id, logo = leagues.get_tvg_info(sport, event)
entry = {
"url": url,
"logo": logo,
"base": iframe,
"timestamp": now.timestamp(),
"id": tvg_id or "Live.Event.us",
"link": link,
}
cached_urls[key] = entry
if url:
urls[key] = entry
log.info(f"Collected and cached {len(urls)} event(s)")
else:
log.info("No events found")
CACHE_FILE.write(cached_urls)

165
M3U8/scrapers/ppv.py Normal file
View file

@ -0,0 +1,165 @@
import re
from functools import partial
from playwright.async_api import Browser
from .utils import Cache, Time, get_logger, leagues, network
log = get_logger(__name__)
urls: dict[str, dict[str, str | float]] = {}
TAG = "PPV"
CACHE_FILE = Cache(TAG, exp=10_800)
API_FILE = Cache(f"{TAG}-api", exp=19_800)
API_MIRRORS = [
"https://api.ppv.to/api/streams",
"https://api.ppv.cx/api/streams",
# "https://api.ppv.sh/api/streams",
# "https://api.ppv.la/api/streams",
]
def fix_url(s: str) -> str:
pattern = re.compile(r"index\.m3u8$", re.I)
return pattern.sub(r"tracks-v1a1/mono.ts.m3u8", s)
async def get_events(url: str, cached_keys: list[str]) -> list[dict[str, str]]:
now = Time.clean(Time.now())
if not (api_data := API_FILE.load(per_entry=False)):
log.info("Refreshing API cache")
api_data = {"timestamp": now.timestamp()}
if r := await network.request(url, log=log):
api_data: dict = r.json()
API_FILE.write(api_data)
events = []
start_dt = now.delta(hours=-1)
end_dt = now.delta(minutes=5)
for stream_group in api_data.get("streams", []):
sport = stream_group["category"]
if sport == "24/7 Streams":
continue
for event in stream_group.get("streams", []):
name = event.get("name")
start_ts = event.get("starts_at")
logo = event.get("poster")
iframe = event.get("iframe")
if not (name and start_ts and iframe):
continue
if f"[{sport}] {name} ({TAG})" in cached_keys:
continue
event_dt = Time.from_ts(start_ts)
if not start_dt <= event_dt <= end_dt:
continue
events.append(
{
"sport": sport,
"event": name,
"link": f"{iframe}#player=clappr#autoplay=true",
"logo": logo,
"timestamp": event_dt.timestamp(),
}
)
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")
if not (api_url := await network.get_base(API_MIRRORS)):
log.warning("No working PPV mirrors")
CACHE_FILE.write(cached_urls)
return
log.info(f'Scraping from "{api_url}"')
if events := await get_events(api_url, cached_urls.keys()):
log.info(f"Processing {len(events)} new URL(s)")
async with network.event_context(browser, stealth=False) as context:
for i, ev in enumerate(events, start=1):
async with network.event_page(context) as page:
handler = partial(
network.process_event,
url=(link := ev["link"]),
url_num=i,
page=page,
timeout=6,
log=log,
)
url = await network.safe_process(
handler,
url_num=i,
semaphore=network.PW_S,
log=log,
)
sport, event, logo, ts = (
ev["sport"],
ev["event"],
ev["logo"],
ev["timestamp"],
)
key = f"[{sport}] {event} ({TAG})"
tvg_id, pic = leagues.get_tvg_info(sport, event)
entry = {
"url": url,
"logo": logo or pic,
"base": link,
"timestamp": ts,
"id": tvg_id or "Live.Event.us",
"link": link,
}
cached_urls[key] = entry
if url:
valid_count += 1
entry["url"] = fix_url(url)
urls[key] = entry
log.info(f"Collected and cached {valid_count - cached_count} new event(s)")
else:
log.info("No new events found")
CACHE_FILE.write(cached_urls)

View file

@ -51,7 +51,7 @@ async def process_ts3(ifr_src: str, url_num: int) -> str | None:
soup_2 = HTMLParser(ifr_1_src_data.content)
ifr_2 = soup_2.css_first("iframe[width='100%']")
ifr_2 = soup_2.css_first("iframe")
if not ifr_2 or not (ifr_2_src := ifr_2.attributes.get("src")):
log.warning(f"URL {url_num}) No iframe element found. (IFR2)")
@ -130,10 +130,7 @@ async def get_events(cached_keys: list[str]) -> list[dict[str, str]]:
if not (time_node := node.css_first(".col-3 span")):
continue
if time_node.text(strip=True).lower() not in [
"matchstarted",
"1minfrom now",
]:
if time_node.text(strip=True).lower() != "matchstarted":
continue
event_name = fix_txt(" vs ".join(teams))

View file

@ -1,13 +1,16 @@
## Base Log @ 2026-04-25 15:23 UTC
## Base Log @ 2026-04-24 16:21 UTC
### ✅ Working Streams: 157<br>❌ Dead Streams: 4
### ✅ Working Streams: 154<br>❌ Dead Streams: 7
| Channel | Error (Code) | Link |
| ------- | ------------ | ---- |
| Disney | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/2303.ts` |
| FDSN Florida | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/296662.ts` |
| FDSN SoCal | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/296681.ts` |
| FDSN South | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/277374.ts` |
| FDSN Southwest | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/296685.ts` |
| FDSN West | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/3367.ts` |
| Pop TV | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/305494.ts` |
| TV One | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/13010.ts` |
---
#### Base Channels URL
```