Compare commits

..

No commits in common. "befe6e3a0a4da89fdd9bdd20c41aee659ccd5a76" and "22f3a6775f20fa1895533117f29733cd7e18f674" have entirely different histories.

12 changed files with 116403 additions and 118135 deletions

File diff suppressed because it is too large Load diff

229254
M3U8/TV.xml

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -64,8 +64,7 @@ async def main() -> None:
asyncio.create_task(embedhd.scrape(hdl_brwsr)), asyncio.create_task(embedhd.scrape(hdl_brwsr)),
# asyncio.create_task(ppv.scrape(xtrnl_brwsr)), # asyncio.create_task(ppv.scrape(xtrnl_brwsr)),
asyncio.create_task(roxie.scrape(hdl_brwsr)), asyncio.create_task(roxie.scrape(hdl_brwsr)),
asyncio.create_task(streamhub.scrape(xtrnl_brwsr)), # asyncio.create_task(streamhub.scrape(hdl_brwsr)),
asyncio.create_task(watchfooty.scrape(xtrnl_brwsr)),
] ]
httpx_tasks = [ httpx_tasks = [
@ -81,11 +80,14 @@ async def main() -> None:
asyncio.create_task(totalsportek.scrape()), asyncio.create_task(totalsportek.scrape()),
asyncio.create_task(tvapp.scrape()), asyncio.create_task(tvapp.scrape()),
asyncio.create_task(webcast.scrape()), asyncio.create_task(webcast.scrape()),
asyncio.create_task(livetvsx.scrape()),
] ]
await asyncio.gather(*(pw_tasks + httpx_tasks)) await asyncio.gather(*(pw_tasks + httpx_tasks))
# others
# await livetvsx.scrape(xtrnl_brwsr)
await watchfooty.scrape(xtrnl_brwsr)
finally: finally:
await hdl_brwsr.close() await hdl_brwsr.close()

View file

@ -1,7 +1,9 @@
import asyncio
import re import re
from functools import partial from functools import partial
from selectolax.parser import HTMLParser import feedparser
from playwright.async_api import Browser, Page, TimeoutError
from .utils import Cache, Time, get_logger, leagues, network from .utils import Cache, Time, get_logger, leagues, network
@ -9,119 +11,192 @@ log = get_logger(__name__)
urls: dict[str, dict[str, str | float]] = {} urls: dict[str, dict[str, str | float]] = {}
TAG = "LTVSX" TAG = "LIVETVSX"
CACHE_FILE = Cache(TAG, exp=10_800) CACHE_FILE = Cache(TAG, exp=10_800)
BASE_URL = "https://livetv.sx/export/webmasters.php" XML_CACHE = Cache(f"{TAG}-xml", exp=28_000)
BASE_URL = "https://cdn.livetv872.me/rss/upcoming_en.xml"
VALID_SPORTS = [
"MLB. Preseason",
"MLB",
"Basketball",
"Football",
"Ice Hockey",
"Wrestling",
]
async def process_event(url: str, url_num: int) -> tuple[str | None, str | None]: def fix_url(s: str) -> str | None:
nones = None, None pattern = re.compile(r"eventinfo\/(\d*)", re.I)
if not (ev_data_1 := await network.request(url, log=log)): if not (match := pattern.search(s)):
log.warning(f"URL {url_num}) Failed to load url. (EVD1)") return
return nones
soup_1 = HTMLParser(ev_data_1.content) elif not (event_id := match[1]).isalnum():
return
for a_elem in soup_1.css("a"): return f"https://cdn.livetv872.me/cache/links/en.{event_id}.html"
if not (src_title := a_elem.attributes.get("title")) or (
"aliez" not in src_title.lower()
):
continue
href = a_elem.attributes["href"]
event_url = href if href.startswith("http") else f"https:{href}" async def process_event(
break url: str,
url_num: int,
page: Page,
) -> str | None:
else: captured: list[str] = []
log.warning(f"URL {url_num}) No valid sources found.")
return nones
if not (ev_data_2 := await network.request(event_url, log=log)): got_one = asyncio.Event()
log.warning(f"URL {url_num}) Failed to load url. (EVD2)")
return nones
soup_2 = HTMLParser(ev_data_2.content) handler = partial(
network.capture_req,
ifr_1 = soup_2.css_first("tr > td > iframe") captured=captured,
got_one=got_one,
if not ifr_1 or not (ifr_1_src := ifr_1.attributes.get("src")):
log.warning(f"URL {url_num}) No iframe element found.")
return nones
ifr_1_src = "".join(
(ifr_1_src if ifr_1_src.startswith("http") else f"https:{ifr_1_src}").split()
) )
if not (ev_data_3 := await network.request(ifr_1_src, log=log)): page.on("request", handler)
log.warning(f"URL {url_num}) Failed to load url. (EVD3)")
return nones
pattern = re.compile(r'pl\.init\((\'|\")([^"]*)(\'|\")\)', re.I) try:
resp = await page.goto(
if not (match := pattern.search(ev_data_3.text)): url,
log.warning(f"URL {url_num}) No M3U8 source found.") wait_until="domcontentloaded",
return nones timeout=10_000,
log.info(f"URL {url_num}) Captured M3U8")
m3u: str = match[2] if match[2].startswith("http") else f"https:{match[2]}"
return m3u, ifr_1_src
async def get_events(cached_keys: list[str]) -> list[dict[str, str]]:
events = []
php_data = await network.unvd_client.get(BASE_URL, params={"lang": "en"})
if php_data.status_code != 200:
log.warning("Failed to get php data.")
return events
soup = HTMLParser(php_data.content)
if not (table := soup.css_first("table.tbl")):
return events
for row in table.css("tr > td"):
if not (event_tbl := row.css_first("table")):
continue
sport_elem = event_tbl.css_first(".spr")
league_elem = event_tbl.css_first(".cmp")
link_elem = event_tbl.css_first("a.title")
event_id_elem = row.css_first("div[id^='el']")
if not (league_elem and sport_elem and link_elem and event_id_elem):
continue
elif not (event_id := event_id_elem.attributes.get("id")):
continue
sport = sport_elem.text(strip=True)
league = league_elem.text(strip=True)
event_name = link_elem.text(strip=True)
if f"[{sport} - {league}] {event_name} ({TAG})" in cached_keys:
continue
events.append(
{
"sport": sport,
"league": league,
"event": event_name,
"link": f"https://cdn.livetv872.me/cache/links/en.{event_id[2:]}.html",
}
) )
if not resp or resp.status != 200:
log.warning(
f"URL {url_num}) Status Code: {resp.status if resp else 'None'}"
)
return
try:
event_a = page.locator('a[title*="Aliez"]').first
href = await event_a.get_attribute("href", timeout=1_250)
except TimeoutError:
log.warning(f"URL {url_num}) No valid sources found.")
return
event_url = href if href.startswith("http") else f"https:{href}"
await page.goto(
event_url,
wait_until="domcontentloaded",
timeout=5_000,
)
wait_task = asyncio.create_task(got_one.wait())
try:
await asyncio.wait_for(wait_task, timeout=6)
except asyncio.TimeoutError:
log.warning(f"URL {url_num}) Timed out waiting for M3U8.")
return
finally:
if not wait_task.done():
wait_task.cancel()
try:
await wait_task
except asyncio.CancelledError:
pass
if captured:
log.info(f"URL {url_num}) Captured M3U8")
return captured[0]
log.warning(f"URL {url_num}) No M3U8 captured after waiting.")
return
except Exception as e:
log.warning(f"URL {url_num}) {e}")
return
finally:
page.remove_listener("request", handler)
async def refresh_xml_cache(now_ts: float) -> dict[str, dict[str, str | float]]:
log.info("Refreshing XML cache")
events = {}
if not (xml_data := await network.request(BASE_URL, log=log)):
return events
feed = feedparser.parse(xml_data.content)
for entry in feed.entries:
if not (date := entry.get("published")):
continue
if (not (link := entry.get("link"))) or (not (fixed_link := fix_url(link))):
continue
if not (title := entry.get("title")):
continue
if not (sport_sum := entry.get("summary")):
continue
sprt = sport_sum.split(".", 1)
sport, league = sprt[0], "".join(sprt[1:]).strip()
event_dt = Time.from_str(date)
if (key := f"[{sport} - {league}] {title} ({TAG})") in events:
continue
events[key] = {
"sport": sport,
"league": league,
"event": title,
"link": fixed_link,
"event_ts": event_dt.timestamp(),
"timestamp": now_ts,
}
return events return events
async def scrape() -> None: async def get_events(cached_keys: list[str]) -> list[dict[str, str]]:
now = Time.clean(Time.now())
if not (events := XML_CACHE.load()):
events = await refresh_xml_cache(now.timestamp())
XML_CACHE.write(events)
start_ts = now.delta(hours=-1).timestamp()
end_ts = now.delta(minutes=5).timestamp()
live = []
for k, v in events.items():
if k in cached_keys:
continue
if (
v["sport"] not in VALID_SPORTS
and v["league"] not in VALID_SPORTS
and v["event"].lower() != "olympic games"
):
continue
if not start_ts <= v["event_ts"] <= end_ts:
continue
live.append(v)
return live
async def scrape(browser: Browser) -> None:
cached_urls = CACHE_FILE.load() cached_urls = CACHE_FILE.load()
valid_urls = {k: v for k, v in cached_urls.items() if v["url"]} valid_urls = {k: v for k, v in cached_urls.items() if v["url"]}
@ -137,47 +212,50 @@ async def scrape() -> None:
if events := await get_events(cached_urls.keys()): if events := await get_events(cached_urls.keys()):
log.info(f"Processing {len(events)} new URL(s)") log.info(f"Processing {len(events)} new URL(s)")
now = Time.clean(Time.now()) async with network.event_context(browser, ignore_https=True) as context:
for i, ev in enumerate(events, start=1):
async with network.event_page(context) as page:
handler = partial(
process_event,
url=(link := ev["link"]),
url_num=i,
page=page,
)
for i, ev in enumerate(events, start=1): url = await network.safe_process(
handler = partial( handler,
process_event, url_num=i,
url=(link := ev["link"]), semaphore=network.PW_S,
url_num=i, log=log,
) timeout=20,
)
url, iframe = await network.safe_process( sport, league, event, ts = (
handler, ev["sport"],
url_num=i, ev["league"],
semaphore=network.HTTP_S, ev["event"],
log=log, ev["event_ts"],
) )
sport, league, event = ( key = f"[{sport} - {league}] {event} ({TAG})"
ev["sport"],
ev["league"],
ev["event"],
)
key = f"[{sport} - {league}] {event} ({TAG})" tvg_id, logo = leagues.get_tvg_info(sport, event)
tvg_id, logo = leagues.get_tvg_info(sport, event) entry = {
"url": url,
"logo": logo,
"base": "https://livetv.sx/enx/",
"timestamp": ts,
"id": tvg_id or "Live.Event.us",
"link": link,
}
entry = { cached_urls[key] = 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:
valid_count += 1
if url: urls[key] = entry
valid_count += 1
urls[key] = entry
log.info(f"Collected and cached {valid_count - cached_count} new event(s)") log.info(f"Collected and cached {valid_count - cached_count} new event(s)")

View file

@ -163,7 +163,7 @@ async def scrape() -> None:
url = await network.safe_process( url = await network.safe_process(
handler, handler,
url_num=i, url_num=i,
semaphore=network.HTTP_S, semaphore=network.PW_S,
log=log, log=log,
) )

View file

@ -117,7 +117,7 @@ async def scrape() -> None:
url = await network.safe_process( url = await network.safe_process(
handler, handler,
url_num=i, url_num=i,
semaphore=network.HTTP_S, semaphore=network.PW_S,
log=log, log=log,
) )

View file

@ -13,7 +13,9 @@ urls: dict[str, dict[str, str | float]] = {}
TAG = "STRMHUB" TAG = "STRMHUB"
CACHE_FILE = Cache(TAG, exp=28_800) CACHE_FILE = Cache(TAG, exp=10_800)
HTML_FILE = Cache(f"{TAG}-html", exp=19_800)
BASE_URL = "https://livesports4u.net" BASE_URL = "https://livesports4u.net"
@ -114,78 +116,116 @@ async def process_event(
page.remove_listener("request", handler) page.remove_listener("request", handler)
async def get_events() -> list[dict[str, str]]: async def refresh_html_cache(
now = Time.clean(Time.now()) date: str,
sport_id: str,
ts: float,
) -> dict[str, dict[str, str | float]]:
tasks = [ events = {}
network.request(
if not (
html_data := await network.request(
urljoin(BASE_URL, f"events/{date}"), urljoin(BASE_URL, f"events/{date}"),
params={"sport_id": sport_id}, params={"sport_id": sport_id},
log=log, log=log,
) )
for date in [now.date(), now.delta(days=1).date()] ):
for sport_id in SPORT_ENDPOINTS
]
results = await asyncio.gather(*tasks)
events = []
if not (soups := [HTMLParser(html.content) for html in results if html]):
return events return events
for soup in soups: soup = HTMLParser(html_data.content)
for section in soup.css(".events-section"):
if not (sport_node := section.css_first(".section-titlte")): for section in soup.css(".events-section"):
if not (sport_node := section.css_first(".section-titlte")):
continue
sport = sport_node.text(strip=True)
for event in section.css(".section-event"):
event_name = "Live Event"
if teams := event.css_first(".event-competitors"):
home, away = teams.text(strip=True).split("vs.")
event_name = f"{away} vs {home}"
if not (event_button := event.css_first(".event-button a")) or not (
href := event_button.attributes.get("href")
):
continue continue
sport = sport_node.text(strip=True) event_date = event.css_first(".event-countdown").attributes.get(
"data-start"
)
for event in section.css(".section-event"): event_dt = Time.from_str(event_date, timezone="UTC")
event_name = "Live Event"
if teams := event.css_first(".event-competitors"): key = f"[{sport}] {event_name} ({TAG})"
home, away = teams.text(strip=True).split("vs.")
event_name = f"{away} vs {home}" events[key] = {
"sport": sport,
if not (event_button := event.css_first(".event-button a")) or not ( "event": event_name,
href := event_button.attributes.get("href") "link": href,
): "event_ts": event_dt.timestamp(),
continue "timestamp": ts,
}
event_date = event.css_first(".event-countdown").attributes.get(
"data-start"
)
event_dt = Time.from_str(event_date, timezone="UTC")
if event_dt.date() != now.date():
continue
events.append(
{
"sport": sport,
"event": event_name,
"link": href,
"timestamp": now.timestamp(),
}
)
return events return events
async def get_events(cached_keys: list[str]) -> list[dict[str, str]]:
now = Time.clean(Time.now())
if not (events := HTML_FILE.load()):
log.info("Refreshing HTML cache")
tasks = [
refresh_html_cache(
date,
sport_id,
now.timestamp(),
)
for date in [now.date(), now.delta(days=1).date()]
for sport_id in SPORT_ENDPOINTS
]
results = await asyncio.gather(*tasks)
events = {k: v for data in results for k, v in data.items()}
HTML_FILE.write(events)
live = []
start_ts = now.delta(minutes=-30).timestamp()
end_ts = now.delta(minutes=30).timestamp()
for k, v in events.items():
if k in cached_keys:
continue
if not start_ts <= v["event_ts"] <= end_ts:
continue
live.append(v)
return live
async def scrape(browser: Browser) -> None: async def scrape(browser: Browser) -> None:
if cached_urls := CACHE_FILE.load(): 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") valid_urls = {k: v for k, v in cached_urls.items() if v["url"]}
return 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}"') log.info(f'Scraping from "{BASE_URL}"')
if events := await get_events(): if events := await get_events(cached_urls.keys()):
log.info(f"Processing {len(events)} new URL(s)") log.info(f"Processing {len(events)} new URL(s)")
async with network.event_context(browser) as context: async with network.event_context(browser) as context:
@ -209,7 +249,7 @@ async def scrape(browser: Browser) -> None:
sport, event, ts = ( sport, event, ts = (
ev["sport"], ev["sport"],
ev["event"], ev["event"],
ev["timestamp"], ev["event_ts"],
) )
key = f"[{sport}] {event} ({TAG})" key = f"[{sport}] {event} ({TAG})"
@ -228,11 +268,13 @@ async def scrape(browser: Browser) -> None:
cached_urls[key] = entry cached_urls[key] = entry
if url: if url:
valid_count += 1
entry["url"] = url.split("?st")[0] entry["url"] = url.split("?st")[0]
urls[key] = entry urls[key] = entry
log.info(f"Collected and cached {len(urls)} new event(s)") log.info(f"Collected and cached {valid_count - cached_count} new event(s)")
else: else:
log.info("No new events found") log.info("No new events found")

View file

@ -2,6 +2,7 @@ import asyncio
import re import re
from functools import partial from functools import partial
from itertools import chain from itertools import chain
from typing import Any
from urllib.parse import urljoin from urllib.parse import urljoin
from selectolax.parser import HTMLParser from selectolax.parser import HTMLParser
@ -14,7 +15,9 @@ urls: dict[str, dict[str, str | float]] = {}
TAG = "STRMSGATE" TAG = "STRMSGATE"
CACHE_FILE = Cache(TAG, exp=28_800) CACHE_FILE = Cache(TAG, exp=10_800)
API_FILE = Cache(f"{TAG}-api", exp=19_800)
BASE_URL = "https://streamsgates.io" BASE_URL = "https://streamsgates.io"
@ -82,17 +85,36 @@ async def process_event(url: str, url_num: int) -> tuple[str | None, str | None]
return match[3], ifr_src return match[3], ifr_src
async def get_events() -> list[dict[str, str]]: async def refresh_api_cache(now_ts: float) -> list[dict[str, Any]]:
now = Time.clean(Time.now())
tasks = [network.request(url, log=log) for url in SPORT_URLS] tasks = [network.request(url, log=log) for url in SPORT_URLS]
results = await asyncio.gather(*tasks) results = await asyncio.gather(*tasks)
if not (data := [*chain.from_iterable(r.json() for r in results if r)]):
return [{"timestamp": now_ts}]
for ev in data:
ev["ts"] = ev.pop("timestamp")
data[-1]["timestamp"] = now_ts
return data
async def get_events(cached_keys: list[str]) -> list[dict[str, str]]:
now = Time.clean(Time.now())
if not (api_data := API_FILE.load(per_entry=False, index=-1)):
log.info("Refreshing API cache")
api_data = await refresh_api_cache(now.timestamp())
API_FILE.write(api_data)
events = [] events = []
if not (api_data := [*chain.from_iterable(r.json() for r in results if r)]): start_dt = now.delta(hours=-2.5)
return events end_dt = now.delta(minutes=30)
for stream_group in api_data: for stream_group in api_data:
date = stream_group.get("time") date = stream_group.get("time")
@ -101,30 +123,34 @@ async def get_events() -> list[dict[str, str]]:
t1, t2 = stream_group.get("away"), stream_group.get("home") t1, t2 = stream_group.get("away"), stream_group.get("home")
if not (date and sport):
continue
event_dt = Time.from_str(date, timezone="UTC")
if event_dt.date() != now.date():
continue
if not (streams := stream_group.get("streams")) or not (
url := streams[0].get("url")
):
continue
if not (t1 and t2): if not (t1 and t2):
continue continue
event = get_event(t1, t2) event = get_event(t1, t2)
if not (date and sport):
continue
if f"[{sport}] {event} ({TAG})" in cached_keys:
continue
event_dt = Time.from_str(date, timezone="UTC")
if not start_dt <= event_dt <= end_dt:
continue
if not (streams := stream_group.get("streams")):
continue
if not (url := streams[0].get("url")):
continue
events.append( events.append(
{ {
"sport": sport, "sport": sport,
"event": event, "event": event,
"link": url, "link": url,
"timestamp": now.timestamp(), "timestamp": event_dt.timestamp(),
} }
) )
@ -132,16 +158,19 @@ async def get_events() -> list[dict[str, str]]:
async def scrape() -> None: async def scrape() -> None:
if cached_urls := CACHE_FILE.load(): 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") valid_urls = {k: v for k, v in cached_urls.items() if v["url"]}
return 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}"') log.info(f'Scraping from "{BASE_URL}"')
if events := await get_events(): if events := await get_events(cached_urls.keys()):
log.info(f"Processing {len(events)} new URL(s)") log.info(f"Processing {len(events)} new URL(s)")
for i, ev in enumerate(events, start=1): for i, ev in enumerate(events, start=1):
@ -154,7 +183,7 @@ async def scrape() -> None:
url, iframe = await network.safe_process( url, iframe = await network.safe_process(
handler, handler,
url_num=i, url_num=i,
semaphore=network.HTTP_S, semaphore=network.PW_S,
log=log, log=log,
) )
@ -180,11 +209,11 @@ async def scrape() -> None:
cached_urls[key] = entry cached_urls[key] = entry
if url: if url:
entry["url"] = url.split("?st")[0] valid_count += 1
urls[key] = entry urls[key] = entry
log.info(f"Collected and cached {len(urls)} new event(s)") log.info(f"Collected and cached {valid_count - cached_count} new event(s)")
else: else:
log.info("No new events found") log.info("No new events found")

View file

@ -38,16 +38,12 @@ class Network:
PW_S = asyncio.Semaphore(3) PW_S = asyncio.Semaphore(3)
def __init__(self) -> None: def __init__(self) -> None:
client_params = { self.client = httpx.AsyncClient(
"timeout": httpx.Timeout(5.0), timeout=httpx.Timeout(5.0),
"follow_redirects": True, follow_redirects=True,
"headers": {"User-Agent": Network.UA}, headers={"User-Agent": Network.UA},
"http2": True, http2=True,
} )
self.client = httpx.AsyncClient(**client_params)
self.unvd_client = httpx.AsyncClient(**client_params, verify=False)
async def request( async def request(
self, self,

View file

@ -157,7 +157,7 @@ async def scrape() -> None:
url = await network.safe_process( url = await network.safe_process(
handler, handler,
url_num=i, url_num=i,
semaphore=network.HTTP_S, semaphore=network.PW_S,
log=log, log=log,
) )

View file

@ -1,89 +1,20 @@
## Base Log @ 2026-04-22 15:48 UTC ## Base Log @ 2026-04-21 16:32 UTC
### ✅ Working Streams: 81<br>❌ Dead Streams: 80 ### ✅ Working Streams: 150<br>❌ Dead Streams: 11
| Channel | Error (Code) | Link | | Channel | Error (Code) | Link |
| ------- | ------------ | ---- | | ------- | ------------ | ---- |
| Aspire | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/150605.ts` |
| C-SPAN | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/14804.ts` |
| CBS Sports Network | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/267357.ts` |
| CNBC | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/2295.ts` |
| CW | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/171820.ts` |
| Cleo TV | HTTP Error (401) | `http://supersonictv.live:8080/317136/Kennzack1218/86101` | | Cleo TV | HTTP Error (401) | `http://supersonictv.live:8080/317136/Kennzack1218/86101` |
| Comedy TV | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/199482.ts` | | FDSN North | HTTP Error (444) | `http://mytvstream.net:8080/live/5AGbfz/324331/20928.m3u8` |
| Cozi TV | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/8392.ts` |
| Discovery Family Channel | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/2300.ts` |
| Discovery Life | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/201208.ts` |
| Discovery Science | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/2301.ts` |
| Disney | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/2303.ts` |
| ESPN News | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/2312.ts` |
| ESPN2 | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/41918.ts` |
| FDSN Detroit | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/6463.ts` |
| FDSN Florida | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/296662.ts` |
| FDSN Midwest | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/58557.ts` |
| FDSN Ohio | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/296675.ts` |
| FDSN Oklahoma | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/6452.ts` |
| FDSN SoCal | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/296681.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 Southeast | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/82301.ts` | | FDSN Southeast | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/82301.ts` |
| FDSN Southwest | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/296685.ts` | | FDSN Southwest | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/296685.ts` |
| FDSN Sun | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/2325.ts` |
| FDSN West | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/3367.ts` | | FDSN West | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/3367.ts` |
| FDSN Wisconsin | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/295668.ts` | | FDSN Wisconsin | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/295668.ts` |
| FX Movie Channel | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/39873.ts` | | FX Movie Channel | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/39873.ts` |
| FYI TV | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/9234.ts` |
| Fox News | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/1611.ts` |
| Fox Sports 1 | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/756.ts` |
| Fox Sports 2 | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/757.ts` |
| Freeform TV | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/2329.ts` |
| Game Show Network | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/466.ts` |
| Golf Channel | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/5845.ts` |
| Grit TV | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/15086.ts` |
| HBO Family | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/760.ts` |
| Hallmark Family | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/304609.ts` |
| Hallmark Mystery | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/3388.ts` |
| History Channel | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/761.ts` |
| INSP | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/30900.ts` |
| ION TV | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/147661.ts` |
| Investigation Discovery | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/8557.ts` |
| Lifetime | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/4667.ts` |
| MLB Network | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/2342.ts` |
| MSNBC | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/406.ts` |
| Marquee Sports Network | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/150609.ts` |
| MotorTrend TV | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/272264.ts` |
| NBA TV | HTTP Error (404) | `http://212.102.60.231/NBA_TV/index.m3u8` |
| NBC Sports Bay Area | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/45785.ts` |
| NBC Sports Boston | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/35132.ts` |
| NBC Sports California | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/16116.ts` |
| NBC Sports Philadelphia | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/35472.ts` |
| NFL RedZone | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/2369.ts` |
| NHL Network | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/2348.ts` |
| National Geographic | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/749.ts` |
| NewsNation | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/6296.ts` |
| Nick Jr | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/14835.ts` |
| Ovation | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/194336.ts` |
| Oxygen | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/6378.ts` |
| Pop TV | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/305494.ts` |
| Premier Sports 1 | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/1097.ts` |
| Premier Sports 2 | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/4723.ts` |
| Showtime Extreme | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/12036.ts` |
| Sky Sports News | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/304775.ts` |
| Smithsonian Channel | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/8585.ts` |
| Sony Movie Channel | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/5831.ts` |
| SportsNet New York | HTTP Error (404) | `http://212.102.60.231/SNY/index.m3u8` |
| SportsNet Pittsburgh | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/59945.ts` |
| Sportsnet 360 | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/3377.ts` |
| Sportsnet East | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/1720.ts` |
| Sportsnet One | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/3378.ts` |
| TLC | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/2362.ts` |
| TSN1 | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/770.ts` |
| TSN2 | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/771.ts` |
| TV Land | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/2364.ts` |
| TV One | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/13010.ts` |
| The Weather Channel | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/2361.ts` |
| USA East | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/3390.ts` |
| Willow Cricket | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/296763.ts` |
| getTV | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/194187.ts` | | getTV | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/194187.ts` |
| NBC Sports Bay Area | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/45785.ts` |
| Smithsonian Channel | HTTP Error (404) | `http://iptvtree.net:8080/live/7e4b0dbd/1dd755dc3f/8585.ts` |
--- ---
#### Base Channels URL #### Base Channels URL
``` ```