跳到主要内容

四季線上 4GTV 解析播放代码 第一弹

  • 新帖
  • 热门
  • 精选

四季線上 4GTV 解析播放代码 第一弹

coding 2y ago in IPTV · 10 views

4gtv 在台湾地区也属于比较流行的流媒体平台,它的频道数量是非常多的,正常情况下,普通用户只能观看免费频道以及 720P 的直播,但实际上它的系统代码是比较简单的,下面的方法可以帮助你解析出 1080P 的直播地址,以及付费频道的直播地址。

首先按照我的理解,我将 4gtv 的频道分为三类:

  1. 使用数字 ID
  2. 使用字符 ID

了解 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 运行

以上代码仅提供一些思路和测试方法,如果要在播放器中播放,可以使用我已经构建好的 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} 即可播放。

注意事项

关于代理,只需将以下域名添加到你的规则中即可:

  • api2.4gtv.tv
  • 4gtvfreemobile-cds.cdn.hinet.net
  • 4gtvfree-cds.cdn.hinet.net

如果你在请求时,报错 ERROR in utils: Request failed: 403 Client Error: Forbidden for url: https://api2.4gtv.tv/Channel/GetChannelUrl3,说明你的 IP 无法解锁,可以尝试使用其他代理。

附录

utils.py

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 列表

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 東森財經新聞台
  • 引用文章
  • 被文章引用
四季線上 4GTV 无需解锁代理播放 第二弹
四季線上 4GTV 无需解锁代理播放 第二弹 Docker 镜像 pixman/pixman 使用说明
coding 将本帖设为了精华贴。 2024年05月13日
stronghu #2 2024年05月13日

哈哈哈哈

回复此楼 修改
Williams #3 2024年05月13日

老规矩,一键三连

回复此楼 修改
coding #4 2024年05月13日
对 Williams #3 回复

😎

回复此楼 修改
88 #5 2024年05月13日

一键三连😈

回复此楼 修改
Jordankobe #6 2024年05月13日

挺不错的,可惜要台湾原生ip,学习学习

回复此楼 修改
coding #7 2024年05月13日
对 Jordankobe #6 回复

大部分机场其实都行的

回复此楼 修改
lew8o9w8 #8 2024年05月13日

一键三连

回复此楼 修改
coding #9 2024年05月13日

回复此楼 修改
lj98 #10 2024年05月13日

好详细,先收慢慢消化

回复此楼 修改
cooleyes #11 2024年05月13日

一键三连

回复此楼 修改
smallen #12 2024年05月13日

感谢楼主分享精品,可以看

回复此楼 修改
huanxian #13 2024年05月13日

一键三连

回复此楼 修改
aiwei #14 2024年05月13日

一键三连

回复此楼 修改
daneilzhou #15 2024年05月13日

支持 Coding

回复此楼 修改
YuanKong #16 2024年05月13日

太强了,回帖膜拜!

回复此楼 修改
hw8111 #17 2024年05月13日

小白也可以看4Gtv,感视大神!!

回复此楼 修改
qinsmoon #18 2024年05月13日

感谢分享

回复此楼 修改
daisy_rain #19 2024年05月13日

收藏收藏

回复此楼 修改
zbzmzj #20 2024年05月13日

太棒了,手工点赞!

回复此楼 修改
rogers #21 2024年05月13日

收藏

回复此楼 修改
bobotoy #22 2024年05月13日

楼主,希望docker可以支援socks代理

回复此楼 修改
coding #23 2024年05月13日
对 bobotoy #22 回复

可以的,你启动的时候设置

docker run --name=4gtv -d -p 5000:5000 -e HTTP_PROXY=socks5://192.168.1.1:7891 -e HTTPS_PROXY=socks5://192.168.1.1:7891 pixman/4gtv
回复此楼 修改
tiao #24 2024年05月13日

🎉

回复此楼 修改
ohjust #25 2024年05月13日

这个容器怎么安装使用呢?自己有谷歌云的vps,可以安装使用吗?

回复此楼 修改
13866622029 #26 2024年05月13日

谢谢分享谢谢分享谢谢分享

回复此楼 修改
bzkwbeek8 #27 2024年05月13日

还是有门槛的,openwrt,使用openclash代理的,使用docker搭建了,打不开,提示{"error":"Failed to get channel info"}。看不到容器log

回复此楼 修改
ohjust #28 2024年05月13日

容器搭建很方便,一次过,第三步test(curl http://localhost:5000/4gtv/1 --location),提示{"error":"Failed to get channel info"}。是节点的问题吗?

回复此楼 修改
coding #29 2024年05月13日
对 ohjust #25 回复

可以的,但是我不确定谷歌 vps 能不能解锁

回复此楼 修改
coding #30 2024年05月13日
对 ohjust #28 回复

是的,换个 ip 试试

回复此楼 修改
  • 1
  • 2
  • 下一页
收到新回复,点击立即加载
需要 登录 后方可回复, 如果你还没有账号请 注册新账号
Coding
@coding
有事请发帖 @coding 或 联系 [email protected]
统计信息
会员
1,797
话题
260
回帖
2,980
活跃会员 查看排行
stronghu
Devin
mengzehe
Brian9394
CharmingCheung
kast
ABC
88
ionull
sarsmark
daisy_rain
john
rudy
winship
xiasufern
ccf
tvbjack
Koguan
zhctor
iamwind

A digital lifestyle community co-maintained by Pixer

本网站服务器由 提供,CDN 由 提供,基于 Homeland 构建,采用 Docker 部署。