跳到主要内容

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

  • 新帖
  • 热门
  • 精选

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

coding 2y ago in IPTV · 0 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 #31 2024年05月13日
对 bzkwbeek8 #27 回复

可能是 oc 没有代理本机,试试下面的方法

# 使用代理 (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
回复此楼 修改
smartdriver #32 2024年05月14日

分享才是快乐

回复此楼 修改
bobotoy #33 2024年05月14日
对 coding #31 回复

ERROR in utils: Request failed: Missing dependencies for SOCKS support., retrying...

回复此楼 修改
await #34 2024年05月14日

群主一键部署命令后面加个参数,要不重启服务器不会自动启动,(--restart)。或者部署完了的用这个命令 执行一遍(docker container update --restart=always 容器名字)

回复此楼 修改
35 楼 已删除
linx #36 2024年05月14日

{"error":"Failed to get play url"} 获取播放网址失败

回复此楼 修改
coding #37 2024年05月14日
对 await #34 回复

可以的,根据你需要来

回复此楼 修改
coding #38 2024年05月14日
对 linx #36 回复

可能是 ip 不太行,换个区域试试

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

容器部署在vpa,那就不能用代理了吧。

回复此楼 修改
coding #40 2024年05月14日
对 ohjust #39 回复

不需要

回复此楼 修改
jason #41 2024年05月15日
对 bzkwbeek8 #27 回复

请问解决了openclash不能播放的问题吗?我用openclash连4gtv的网站都打不开,ShadowSocksR Plus+就可以正常运行docker。

回复此楼 修改
bzkwbeek8 #42 2024年05月15日
对 jason #41 回复

我用ShadowSocksR Plus+ ,网页可以播放,docker还是不成功。还是提示"error":"Failed to get channel info"}

回复此楼 修改
bzkwbeek8 #43 2024年05月15日
对 coding #31 回复

试过代理,还是提示"error":"Failed to get channel info",不知道什么原因。 stderr: [2024-05-15 01:01:19,816] ERROR in utils: Request failed: HTTPSConnectionPool(host='api2.4gtv.tv', port=443): Max retries exceeded with url: /Channel/GetChannel/19 (Caused by ProxyError('Unable to connect to proxy', NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))), retrying...

回复此楼 修改
44 楼 已删除
lingch #45 2024年05月15日
对 bzkwbeek8 #27 回复

我和你一样,同样环境,也是打不开。

回复此楼 修改
BreadnButter #46 2024年05月15日

真心不知怎樣可以在aptv,Tivimate或Ott app上應用?😅

回复此楼 修改
coding 在 四季線上 4GTV 无需解锁代理播放 第二弹 提及了此话题。 2024年05月15日
coding #48 2024年05月15日
对 linx #36 回复

新版本: https://pixman.io/topics/12

回复此楼 修改
coding #49 2024年05月15日
对 BreadnButter #46 回复

等我出个 m3u 文件

回复此楼 修改
1 个赞
coding #50 2024年05月15日
对 bzkwbeek8 #43 回复

试试新版本 https://pixman.io/topics/12

回复此楼 修改
coding 在 Docker 镜像 pixman/pixman 使用说明 提及了此话题。 2024年05月21日
zzq12345678 #52 2024年06月27日

这个这个算法失效了,不知道4gtv-auth那个怎么算法?猜测时间函数年月日加其他

回复此楼 修改
coding #53 2024年06月27日
对 zzq12345678 #52 回复

不需要关心,用最新镜像即可

回复此楼 修改
  • 上一页
  • 1
  • 2
收到新回复,点击立即加载
需要 登录 后方可回复, 如果你还没有账号请 注册新账号
Coding
@coding
有事请发帖 @coding 或 联系 [email protected]
统计信息
会员
1,786
话题
259
回帖
2,955
活跃会员 查看排行
Devin
Koguan
nt01xp
jmk92
859876741
kewycjy
cnvc
kkk00011
0xyyyy
wayne630
zf7222749
zmy123
Unlovely
bos5201314
scjxx
once
wykds
LeoXD
a15ban
gally

A digital lifestyle community co-maintained by Pixer

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