4gtv 在台湾地区也属于比较流行的流媒体平台,它的频道数量是非常多的,正常情况下,普通用户只能观看免费频道以及 720P 的直播,但实际上它的系统代码是比较简单的,下面的方法可以帮助你解析出 1080P 的直播地址,以及付费频道的直播地址。
首先按照我的理解,我将 4gtv 的频道分为三类:
了解 Livednow 的朋友应该知道,之前我们提供港澳台的频道直播,但是后面因为一些原因暂停了,现在重新整理代码的时候发现之前使用的方法已经失效,所以本贴先暂时分享其中一种有效的方法,等我后续再发布其他方式。
本方法无需绕过 Cloudflare WAF 验证,但是有些代理 IP 无法解锁,具体需要自己测试
这类频道的 ID 类似 民視第一台 对应的 1,以下是代码:
import json
from urllib.parse import urlparse, parse_qs
from utils import my_requester, encrypt_data, decrypt_data
def get_channel(channel_id):
channel_info = {
'id': channel_id,
}
get_channel_api = f'https://api2.4gtv.tv/Channel/GetChannel/{channel_id}'
headers = {
'User-Agent': 'okhttp/3.12.11'
}
response = my_requester('GET', url=get_channel_api, headers=headers)
if not response:
print('Failed to get channel info')
return channel_info
response_dict = response.json()
channel_info['name'] = response_dict['Data']['fsNAME']
request_dict = {
'fnCHANNEL_ID': response_dict['Data']['fnID'],
'fsASSET_ID': response_dict['Data']['fs4GTV_ID'],
'fsDEVICE_TYPE': 'mobile',
'clsIDENTITY_VALIDATE_ARUS': {'fsVALUE': ''}
}
encrypt = encrypt_data(json.dumps(request_dict))
request_data = {'value': encrypt.decode()}
headers['Content-Type'] = 'application/x-www-form-urlencoded'
response = my_requester('POST',
url='https://api2.4gtv.tv/Channel/GetChannelUrl3',
data=request_data, headers=headers)
if not response:
print('Failed to get channel play info')
return channel_info
response_dict = response.json()
if 'Data' not in response_dict:
print('Failed to get channel play info')
return channel_info
decrypt = decrypt_data(response_dict['Data'])
playlist = json.loads(decrypt)
play_url = playlist['flstURLs'][0]
if play_url.startswith('https://4gtvfree-cds.cdn.hinet.net'):
play_url = play_url.replace('/index.m3u8?', '/1080.m3u8?')
else:
play_url = get_play_raw(play_url, return_type='url')
channel_info['play_url'] = play_url
return channel_info
def get_play_raw(url, return_type='raw'):
headers = {
'User-Agent': 'okhttp/3.12.11'
}
response = my_requester('GET', url=url, headers=headers)
parsed_url = urlparse(url)
if not response:
return None
resp_text = response.text.strip()
latest_line = resp_text.split('\n')[-1]
url_path = '/'.join(parsed_url.path.split('/')[0:-1])
new_url = f'{parsed_url.scheme}://{parsed_url.netloc}{url_path}/{latest_line}'
if return_type == 'url':
return new_url
if '.ts' in latest_line:
return resp_text
else:
return get_play_raw(new_url, return_type)
def play_4gtv(play_url):
m3u8_raw = get_play_raw(play_url)
if not m3u8_raw:
print('Failed to get play url')
if play_url.startswith('https://4gtvfree-cds.cdn.hinet.net'):
# live 格式的处理
base_url = play_url.split('?')[0].rsplit('/', 1)[0]
lines = []
for line in m3u8_raw.split('\n'):
if line.startswith('#EX') or not line.strip():
lines.append(line)
elif '.ts' in line:
ts_file = line.split('?')[0]
parsed_url = urlparse(line)
params = parse_qs(parsed_url.query)
params_simplified = {k: v[0] for k, v in params.items()}
ts_url = (f'{base_url}/{ts_file}?'
f'token1={params_simplified["token1"]}'
f'&expires1={params_simplified["expires1"]}')
lines.append(ts_url)
else:
lines.append(line)
m3u8_raw = '\n'.join(lines)
else:
channel = play_url.split('?')[0].split('/')[-3]
lines = []
prex = f'https://litvpc-hichannel.cdn.hinet.net/live/pool/{channel}/litv-pc/'
for line in m3u8_raw.split('\n'):
if line.startswith('#EXT') or not line.strip():
lines.append(line)
else:
ts_file = line.split('?')[0]
ts_file = ts_file.replace('video=2000000', 'video=6000000')
ts_file = ts_file.replace('video=2936000', 'video=5936000')
ts_file = ts_file.replace('video=3000000', 'video=6000000')
ts_file = ts_file.replace('avc1_2000000=3', 'avc1_6000000=1')
ts_file = ts_file.replace('avc1_2000000=6', 'avc1_6000000=1')
ts_file = ts_file.replace('avc1_2936000=4', 'avc1_6000000=5')
ts_file = ts_file.replace('avc1_3000000=3', 'avc1_6000000=1')
ts_url = f'{prex}{ts_file}'
lines.append(ts_url)
m3u8_raw = '\n'.join(lines)
print(m3u8_raw)
if __name__ == '__main__':
info = get_channel('1')
print(info)
if 'play_url' in info:
play_4gtv(info['play_url'])
以上代码仅提供一些思路和测试方法,如果要在播放器中播放,可以使用我已经构建好的 Docker 镜像 pixman/4gtv:
# run
docker run --name=4gtv -d -p 5000:5000 pixman/4gtv
# 使用代理 (192.168.1.1:7890 替换成你的代理地址)
docker run --name=4gtv -d -p 5000:5000 -e HTTP_PROXY=http://192.168.1.1:7890 -e HTTPS_PROXY=http://192.168.1.1:7890 pixman/4gtv
# test
curl http://localhost:5000/4gtv/1 --location
在播放器中使用 http://localhost:5000/4gtv/{ID}
即可播放。
关于代理,只需将以下域名添加到你的规则中即可:
如果你在请求时,报错 ERROR in utils: Request failed: 403 Client Error: Forbidden for url: https://api2.4gtv.tv/Channel/GetChannelUrl3
,说明你的 IP 无法解锁,可以尝试使用其他代理。
import base64
import time
import logging
import requests
import urllib3
from Cryptodome.Cipher import AES
from Cryptodome.Util.Padding import pad, unpad
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
KEY = 'ilyB29ZdruuQjC45JhBBR7o2Z8WJ26Vg'.encode()
IV = 'JUMxvVMmszqUTeKn'.encode()
def my_requester(method, **kwargs):
retry_times = 3
timeout = 5
if 'timeout' in kwargs:
timeout = kwargs.pop('timeout', 5)
verify = False
if 'verify' in kwargs:
verify = kwargs.pop('verify', False)
while retry_times > 0:
try:
resp = requests.request(method, timeout=timeout, verify=verify, **kwargs)
resp.raise_for_status()
return resp
except Exception as e:
retry_times -= 1
time.sleep(0.5)
logging.error(f'Request failed: {e}, retrying...')
logging.error('Request failed after retrying')
return None
def encrypt_data(data):
cipher = AES.new(KEY, AES.MODE_CBC, IV)
padded_data = pad(data.encode(), AES.block_size)
return base64.b64encode(cipher.encrypt(padded_data))
def decrypt_data(encrypted_data):
decipher = AES.new(KEY, AES.MODE_CBC, IV)
decrypted_data = unpad(decipher.decrypt(base64.b64decode(encrypted_data)), AES.block_size)
return decrypted_data.decode()
id | name |
---|---|
1 | 民視第一台 |
2 | 民視台灣台 |
3 | 民視 |
4 | 中視 |
6 | 華視 |
7 | 公視戲劇 |
8 | 好消息 2 台 |
9 | 好消息 |
11 | 達文西頻道 |
15 | 靖天卡通台 |
16 | 民視綜藝台 |
19 | 愛爾達娛樂台 |
21 | 靖天綜合台 |
22 | 靖天日本台 |
23 | 龍華戲劇台 |
24 | 民視影劇台 |
25 | 采昌影劇台 |
28 | 龍華日韓台 |
30 | 中天新聞台 |
31 | 民視新聞台 |
33 | 中視新聞 |
34 | 華視新聞 |
36 | 寰宇新聞台 |
38 | 視納華仁紀實頻道 |
39 | amc 電影台 |
40 | 影迷數位電影台 |
42 | 靖天映畫 |
48 | 博斯高球台 |
50 | 博斯運動一台 |
51 | 博斯無限台 |
52 | 博斯網球台 |
57 | TRACE Sport Stars |
58 | 智林體育台 |
59 | 靖洋卡通 Nice Bingo |
60 | i-Fun 動漫台 |
61 | 民視旅遊台 |
69 | 時尚運動 X |
78 | TRACE Urban |
79 | Mezzo Live HD |
80 | CLASSICA 古典樂 |
82 | 靖天電影台 |
83 | 靖天育樂台 |
84 | KLT-靖天國際台 |
85 | 半島國際新聞台 |
86 | VOA 美國之音 |
88 | 靖天資訊台 |
93 | 鳳梨直擊台 |
94 | 香蕉直擊台 |
106 | ELTV 生活英語台 |
107 | 客家電視台 |
113 | 豬哥亮歌廳秀 |
114 | 八大綜藝台 |
116 | 靖天戲劇台 |
118 | 靖洋戲劇台 |
119 | Nice TV 靖天歡樂台 |
121 | 龍華電影台 |
123 | 中視菁采台 |
124 | 中視經典台 |
139 | Love Nature |
160 | 車迷 TV |
168 | 幸福空間居家台 |
169 | 三立綜合台 |
170 | 國會頻道 1 |
171 | 國會頻道 2 |
172 | 八大精彩台 |
173 | 東森購物一台 |
174 | 東森購物二台 |
175 | LUXE TV Channel |
176 | My Cinema Europe HD 我的歐洲電影 |
178 | TV5MONDE STYLE HD 生活時尚 |
179 | GINX Esports TV |
180 | ROCK Action |
181 | TechStorm |
182 | Pet Club TV |
183 | TVBS |
184 | TVBS 歡樂台 |
185 | 尼克兒童頻道 |
186 | HITS 頻道 |
188 | LiveABC 互動英語頻道 |
189 | ARIRANG 阿里郎頻道 |
201 | 經典電影台 |
202 | 經典卡通台 |
204 | 精選動漫台 |
209 | 大愛電視 |
210 | 人間衛視 |
212 | 影迷數位紀實台 |
213 | MTV Live HD 音樂頻道 |
214 | History 歷史頻道 |
215 | CI 罪案偵查頻道 |
217 | Lifetime 娛樂頻道 |
218 | 電影原聲台 CMusic |
219 | Nick Jr. 兒童頻道 |
223 | XTR 亞太台 |
224 | SBN 全球財經台 |
225 | CinemaWorld |
226 | DW 德國之聲 |
227 | TVBS 精采台 |
229 | 三立新聞 iNEWS |
230 | 大愛二台 |
231 | MOMO 親子台 |
235 | CNBC Asia 財經台 |
236 | 金光布袋戲 |
237 | 愛爾達生活旅遊台 |
244 | 戲劇免費看 2 台 |
245 | 電影免費看 2 台 |
246 | 民視 |
249 | 滾動力 rollor |
250 | 亞洲旅遊台 |
252 | Global Trekker |
254 | 民視第一台 |
255 | 民視台灣台 |
256 | 華視新聞 |
257 | 民視新聞台 |
258 | 中視新聞 |
260 | 中天新聞台 |
261 | 亞洲旅遊台 |
268 | 鏡電視新聞台 |
269 | 兒童卡通台 |
270 | 戲劇免費看 1 台 |
273 | 原住民族電視台 |
274 | fun 探索娛樂台 |
275 | ROCK Entertainment |
277 | 時尚運動 X |
278 | 超人力霸王整套看 |
279 | 花系列 經典劇場 |
280 | 寰宇新聞台灣台 |
281 | 寰宇財經台 |
282 | DreamWorks 夢工廠動畫 |
283 | Bloomberg TV |
284 | TVBS 綜藝台 |
285 | TVBS 台劇台 |
286 | 東森購物四台 |
287 | 東森購物三台 |
288 | Globetrotter 環遊旅行家 |
289 | 番茄直擊台 |
290 | 芭樂直擊台 |
291 | TVBS 新聞 |
292 | 東森新聞台 |
293 | 東森財經新聞台 |