VNEWS / vtv_scraper.py
bep40's picture
Upload vtv_scraper.py
137a671 verified
Raw
History Blame
5.39 kB
"""
VTV Channels Scraper
Fetches stream URLs from hd.xemtv.net PHP endpoints for VTV1-VTV10 + VTV Cần Thơ
"""
import requests, re, time, threading
UA = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
"Accept-Language": "vi-VN,vi;q=0.9",
"Referer": "https://hd.xemtv.net/",
}
XEMTV_PHP_ENDPOINTS = {
"vtv1": "https://hd.xemtv.net/kenh/vtv1.php",
"vtv2": "https://hd.xemtv.net/kenh/vtv2.php",
"vtv3": "https://hd.xemtv.net/kenh/vtv3.php",
"vtv4": "https://hd.xemtv.net/kenh/vtv4.php",
"vtv5": "https://hd.xemtv.net/kenh/vtv5.php",
"vtv6": "https://hd.xemtv.net/kenh/vtv6.php",
"vtv7": "https://hd.xemtv.net/kenh/vtv7.php",
"vtv8": "https://hd.xemtv.net/kenh/vtv8.php",
"vtv9": "https://hd.xemtv.net/kenh/vtv9.php",
"vtv10": "https://hd.xemtv.net/kenh/vtv10.php",
}
CHANNEL_NAMES = {
"vtv1": "VTV1", "vtv2": "VTV2", "vtv3": "VTV3", "vtv4": "VTV4",
"vtv5": "VTV5", "vtv6": "VTV6", "vtv7": "VTV7", "vtv8": "VTV8",
"vtv9": "VTV9", "vtv10": "VTV Cần Thơ",
}
CDN_FALLBACK = {
"vtv1": "https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv1-manifest.m3u8",
"vtv2": "https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv2-manifest.m3u8",
"vtv3": "https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv3-manifest.m3u8",
"vtv4": "https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv4-manifest.m3u8",
"vtv5": "https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv5-manifest.m3u8",
"vtv6": "https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv6-manifest.m3u8",
"vtv7": "https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv7-manifest.m3u8",
"vtv8": "https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv8-manifest.m3u8",
"vtv9": "https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv9-manifest.m3u8",
"vtv10": "https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv10-manifest.m3u8",
"_fpt_vtv1": "https://live.fptplay53.net/fnxch2/vtv1hd_abr.smil/chunklist.m3u8",
"_fpt_vtv2": "https://live.fptplay53.net/fnxch2/vtv2hd_abr.smil/chunklist.m3u8",
"_fpt_vtv3": "https://live.fptplay53.net/fnxch2/vtv3hd_abr.smil/chunklist.m3u8",
"_fpt_vtv4": "https://live.fptplay53.net/fnxch2/vtv4hd_abr.smil/chunklist.m3u8",
"_fpt_vtv5": "https://live-a.fptplay53.net/live/media/VTV5HD/live_hls_avc/index.m3u8",
"_fpt_vtv6": "https://live.fptplay53.net/fnxch2/vtv6hd_abr.smil/chunklist.m3u8",
"_fpt_vtv7": "https://live.fptplay53.net/fnxhd1/vtv7hd_vhls.smil/chunklist_b5000000.m3u8",
"_fpt_vtv8": "https://live.fptplay53.net/epzhd1/vtv8hd_vhls.smil/chunklist.m3u8",
"_fpt_vtv9": "https://live.fptplay53.net/fnxhd1/vtv9hd_vhls.smil/chunklist.m3u8",
"_fpt_vtv10": "https://live.fptplay53.net/fnxch2/vtvcantho_abr.smil/chunklist.m3u8",
}
_vtv_cache = {}
_vtv_lock = threading.Lock()
_CACHE_TTL = 180
def _cached(key):
with _vtv_lock:
if key in _vtv_cache and time.time() - _vtv_cache[key]['t'] < _CACHE_TTL:
return _vtv_cache[key]['d']
return None
def _set_cache(key, data):
with _vtv_lock:
_vtv_cache[key] = {'t': time.time(), 'd': data}
def extract_m3u8_from_html(html):
if not html:
return None
m = re.search(r"file\s*:\s*['\"]([^'\"]*\.m3u8[^'\"]*)['\"]", html, re.IGNORECASE)
if m:
url = m.group(1).strip()
if len(url) > 20:
return url
m = re.search(r"(https?://[^\s\"'<>\\]+\.m3u8[^\s\"'<>\\]*)", html, re.IGNORECASE)
if m:
url = m.group(1).strip()
if len(url) > 20:
return url
return None
def fetch_vtv_stream(channel_id):
channel_id = channel_id.lower().strip()
name_map = {
'vtvct': 'vtv10', 'vtv-can-tho': 'vtv10', 'vtv can tho': 'vtv10',
'vtv_can_tho': 'vtv10', 'cantho': 'vtv10', 'cần thơ': 'vtv10',
'vietnam_vtv1': 'vtv1', 'vietnam_vtv2': 'vtv2', 'vietnam_vtv3': 'vtv3',
'vietnam_vtv4': 'vtv4', 'vietnam_vtv5': 'vtv5', 'vietnam_vtv6': 'vtv6',
'vietnam_vtv7': 'vtv7', 'vietnam_vtv8': 'vtv8', 'vietnam_vtv9': 'vtv9',
}
channel_id = name_map.get(channel_id, channel_id)
cached = _cached(channel_id)
if cached:
return cached
vtvgourl = CDN_FALLBACK.get(channel_id)
if vtvgourl:
_set_cache(channel_id, vtvgourl)
return vtvgourl
php_url = XEMTV_PHP_ENDPOINTS.get(channel_id)
if php_url:
try:
r = requests.get(php_url, headers=UA, timeout=15, allow_redirects=True, verify=False)
if r.status_code == 200:
m3u8 = extract_m3u8_from_html(r.text)
if m3u8:
_set_cache(channel_id, m3u8)
return m3u8
except:
pass
fpt_key = f"_fpt_{channel_id}"
fpt_url = CDN_FALLBACK.get(fpt_key)
if fpt_url:
_set_cache(channel_id, fpt_url)
return fpt_url
return None
def get_all_vtv_streams():
channels = []
for ch_id, php_url in XEMTV_PHP_ENDPOINTS.items():
stream_url = fetch_vtv_stream(ch_id)
channels.append({
'id': ch_id,
'name': CHANNEL_NAMES.get(ch_id, ch_id.upper()),
'stream_url': stream_url,
})
return channels
XEMTV_CHANNELS = {v: k for k, v in CHANNEL_NAMES.items()}
CDN_STREAMS = {v: k for k, v in CDN_FALLBACK.items()}