天津移动 IPTV 的播放列表,好久以前整理过一次,最近发现好多频道失效了,重新整理并记录下来。希望能帮到有需要的人。
扫描获取组播源 这种方法适合没有 IPTV 盒子或没开通 IPTV 业务但知道组播 IP 段的情况。我这边不管开没开通 IPTV,IPTV 的网络配置都会下发到光猫,在路由器上设置好组播代理,路由器下的设备就可以看组播了。 用组播还可以看 IPTV 盒子内的付费频道,没有时间限制。
扫描 抓到的列表不是很好用,有些频道名字和实际播放的频道对不上。所以还是通过这个列表获取到移动的组播IP段,然后扫一下全部的频道。 用的是 iptv_channel_scanner_windows ,有 Windows 版和 Linux 版。
iptvscanner 225.2.1.1 225.2.1.254 iptvscanner 239.0.2.1 239.0.2.254
扫完之后会把扫描结果保存到当前目录的 m3u 文件。
节目信息和排序 这是最枯燥无聊的步骤了,要一个一个观看列表中的频道并加上正确的频道名再按自己的喜好进行分类和排序。这里用到的工具是 PlaylistEditorTV 和 Notepad3 。
EPG 用的是 https://epg.112114.xyz/ 和 https://epg.erw.cc/。
播放列表 移动IPTV2025-11-09.m3u 组播带回看 (需设置路由)单播带回看 (需设置路由) 台标文件:tvlogos.7z
另附电信的组播列表:电信IPTV2023-10-18.m3u 电信IPTV2023-10-18.txt
单播源,时移源及 EPG 获取 扫出来的都是组播源,如果还想要时移和官方的 EPG 就要对 IPTV 盒子下手了。(关于移动 IPTV 盒子的更多研究还可以看这个 。)
准备 路由器下虽然可以看组播,但是不能直接访问 IPTV 的内网,需要做些设置。分以下几种情况:
光猫是路由模式
要在光猫上设置静态路由才可以让光猫下的其他设备也能连接到 IPTV 内网。
或者用命令行添加路由: ip route add default via 10.166.0.1 dev nbif1 table table.csp_main
OTHER_R_VID_xx 是IPTV 的接口。10.166.0.1 是 OTHER_R_VID_xx 连接的网关,nbif1 是所在的网卡名。但是这个命令会在光猫重启后失效,每次都要去执行一次。
光猫设置端口转发: iptables -t nat -I PREROUTING -s 192.168.1.111 -p tcp -j DNAT --to-destination 192.168.1.2:8888
这样就可以在路由器上用 iptables 把所有来自盒子(192.168.1.111)的 tcp 连接都转到电脑上的 Fiddler(192.168.1.2:8888) 。这样就可以抓包了。
上网是桥接,IPTV是路由模式
这种也要在光猫上设置静态路由才能连接到 IPTV 内网,设置方法和上面一样:
接口
目标
网关
wan
10.206.0.0/16
192.168.1.1
wan
10.128.0.0/24
192.168.1.1
接口是路由器可以访问到光猫的接口,目标地址是抓盒子发的包发现的,网关就是光猫的 IP。
上网和IPTV都是桥接模式
这种方法是软VLAN,不太稳定,还是推荐第二种方法。
我这里上网和 IPTV 的业务模式为 INTERNET OTHER。把这两个都设置为桥接,改前记得记下 VLAN ID(30) 组播VLAN(511) 和拨号的用户名密码。
设置完桥接后在光猫上设置好 VLAN 绑定。
OpenWrt 上设置好 internet 和 IPTV 接口:
名称
协议
设备
internet
PPPoE
wan.10
IPTV
DHCP客户端
wan.30
wan
DHCP客户端
wan
OpenWrt 使用 VLAN 的方式是在网卡名后加上 .VLANID,如:wan.10,wan.30。所以新建接口的设备那里也要这样填。
接口
目标
网关
IPTV
10.206.0.0/16
10.166.0.1
IPTV
10.128.0.0/24
10.166.0.1
目标地址是抓盒子的包发现的,网关就是 IPTV 接口的网关。
抓包 在路由器上把盒子发出的 tcp 请求转到电脑上的 Fiddler ,这个也可以用来劫持盒子应用商店的下载链接来装第三方的 apk 。
#!/usr/sbin/nft -f table inet nat { chain prerouting { type nat hook prerouting priority -30; meta nfproto ipv4 ether saddr "盒子的mac" tcp dport 0-65535 counter dnat ip to 10.0.0.2:8888 meta nfproto ipv6 ether saddr "盒子的mac" tcp dport 0-65535 counter dnat ip6 to [fe80::2]:8888 } chain postrouting { type nat hook postrouting priority -30; meta nfproto ipv4 ether saddr "盒子的mac" ip daddr 10.0.0.2 counter masquerade meta nfproto ipv6 ether saddr "盒子的mac" ip6 daddr [fe80::2] counter masquerade } }
10.0.0.2 [fe80::2] 8888 就是电脑的 IPv4地址 IPv6地址 和 Fiddler 的端口。
节目源 重启盒子,开机后可以看到有一条去/EPG/jsp/getchannellistHWCTC.jsp的连接,点开后就是播放地址了。 每行一个频道,单行格式化后是这个格式:
ChannelID="20000179" , ChannelName="中央-3高清" , UserChannelID="315" , ChannelURL="igmp://225.2.1.158:5000" , TimeShift="1" , TimeShiftLength="7200" , ChannelSDP="igmp://225.2.1.158:5000|rtsp://10.206.255.108/PLTV/88888888/224/3221225755/10000100000000060000000000320797_0.smil?rrsip=10.206.255.108,rrsip=10.206.123.4&zoneoffset=480&icpid=1008601&limitflux=-1&limitdur=-1&tenantId=8601&accountinfo=xxxx&GuardEncType=2" , TimeShiftURL="rtsp://10.206.255.108/PLTV/88888888/224/3221225755/10000100000000060000000000320797_0.smil?rrsip=10.206.255.108,rrsip=10.206.123.4&zoneoffset=480&icpid=1008601&limitflux=-1&limitdur=-1&tenantId=8601&GuardEncType=2&accountinfo=xxxx" , ChannelType="1" , IsHDChannel="1" , PreviewEnable="0" , ChannelPurchased="1" , ChannelLocked="0" , ChannelLogURL="" , PositionX="" , PositionY="" , BeginTime="0" , Interval="" , Lasting="" , ActionType="1" , FCCEnable="1" , ChannelFCCIP="10.206.255.108" , ChannelFCCPort="8027" , ChannelFECPort="0"
上面的igmp://225.2.1.158:5000就是组播地址了,改成rtp://225.2.1.158:5000就可以在 VLC 里面播放。单播地址是rtsp://10.206.255.108/PLTV/88888888/224/3221225755/10000100000000060000000000320797_0.smil,试了下去掉鉴权信息好像可以直接看。还可以看到TimeShift="1"和FCCEnable="1",说明支持回看和FCC。 组播转单播可以用rtp2httpd,内置网页播放器。
回看 播放回看源的方法是在回看链接后面加上playseek参数后面接时间,例如 playseek=20251001000000-20251001010000。指的就是回看2025年10月01日00时00分00秒到2025年10月01日01时00分00秒的内容。比如上面的组合起来就是这样rtsp://10.206.255.108/PLTV/88888888/224/3221225755/10000100000000060000000000320797_0.smil?rrsip=10.206.255.108,rrsip=10.206.123.4&zoneoffset=480&icpid=1008601&limitflux=-1&limitdur=-1&tenantId=8601&GuardEncType=2&accountinfo=xxxx&playseek=20251001000000-20251001010000。放到 PotPlayer 里面就可以看了。
IPTV播放器回看的方法是把playseek的参数部分根据格式替换成时间,比如酷9的是playseek=${(b)yyyyMMddHHmmss}-${(e)yyyyMMddHHmmss},那他的回放地址就是这样:rtsp://10.206.255.108/PLTV/88888888/224/3221225755/10000100000000060000000000320797_0.smil?rrsip=10.206.255.108,rrsip=10.206.123.4&zoneoffset=480&icpid=1008601&limitflux=-1&limitdur=-1&tenantId=8601&GuardEncType=2&accountinfo=xxxx&playseek=${(b)yyyyMMddHHmmss}-${(e)yyyyMMddHHmmss}。
像上面的例子,他就会把${(b)yyyyMMddHHmmss}和 ${(e)yyyyMMddHHmmss}替换成20251001000000和20251001010000。不同播放器支持替换的格式不一样,比如 kodi 的IPTV Simple PVR就是{utc:YmdHMS} {utcend:YmdHMS}还有这些格式 ,这点还是要注意一下的。还有有些地区可能不用playseek参数来回看,也有tvdr=xxx之类的。
FCC rtp2httpd 要用 FCC 需要在链接后面加上fcc=${ChannelFCCIP}:${ChannelFCCPort}这样的参数,像上面就是 rtp://225.2.1.158:5000?fcc=10.206.255.108:8027。兼容 udpxy 格式的组播转单播则是http://10.0.0.1:4022/rtp/225.2.1.158:5000?fcc=10.206.255.108:8027。他转换的单播列表会根据 m3u 内容在播放时自动使用FCC。
不过我这里FCC播放报错 Server response error code: 2,可能还是不支持吧。
自动抓取 之后就可以按照抓包信息,写一个模拟盒子自动登录获取频道列表和 EPG 的脚本,然后生成标准列表供 kodi emby 之类的软件订阅。
可是其中有 Authenticator 和 tempKey 参数不知道是怎么生成的。左搜搜又搜搜,搜到恩山上的帖子 和这篇文章 。
Authenticator 是用盒子设置里的登录密码使用 3DES ECB PKCS7 加密生成的,加密前为 随便8位数字+$+TOKEN+$+USERID+$+STBID+$+IP地址+$+MAC地址+$$+CTC 。
TOKEN可以在访问 /EPG/jsp/authLoginHWCTC.jsp 时得到。
STBID 和 mac 可以从运营商机顶盒下面的贴纸中找到。
USERID 是盒子设置里的登录帐号,登录帐号和密码一般和宽带拨号帐号密码一样,如果不同可以用 ZTE_STB_Tools 连上盒子,然后导出配置查看。
CTC 应该是运营商吧,但我的是移动,抓包得到的值却是CU。
测试后 ip 和 mac 为空也可以获取SessionID,抓到频道列表。
tempKey 就没有办法了,只知道是根据 SessionID 算出来的,把服务器返回的 SessionID 修改成 11111111111111111111111111111111 , tempKey 会变成 BC5F5F820458CFBA981CF0C90CA313F9 ,去 cmd5 查了下结果为 781943957。实在看不出来这些有什么关系……但不传 tempKey 也可以获取到频道列表。
抓取脚本 获取频道列表的脚本,需要把下面的信息改成自己的,抓完会存在当前目录的 iptvlist.txt 里。
getchannellist.sh #!/bin/bash userid= passwd= stbid= ctc=CU STBType=B860AV2.1-A STBVersion=V81011354.5017 ua='Mozilla/5.0 (Linux; Android 4.4.2; B860AV2.1-A Build/KOT49H) (ztebw,1.0.1,ZTE,blink,7105)AppleWebKit/537.36 (KHTML, like Gecko) Chrome Safari/537.36' header="-H User-Agent: $ua " curl -Ss "$header " -D headers.txt "http://10.206.249.8:8082/EDS/jsp/AuthenticationURL?UserID=$userid &Action=Login" newurl=$(sed -En 's/Location: (http.+Login).+/\1/p' headers.txt) EPGIP_PORT=$(grep -oP '(?<=EPGIP_PORT=")(.+?)(?=";)' headers.txt) token=$(curl -Ss "$header " -X POST \ -d "UserID=$userid &VIP=" http://$EPGIP_PORT /EPG/jsp/authLoginHWCTC.jsp | \ grep -Po '(?<=var EncryptToken = ")(.+?)(?=";)' ) text='12345678$' $token '$' $userid '$' $stbid '$' $ip '$' $mac '$$' $ctc key=$passwd key_hex=$(echo -n $key |xxd -p -c0) auth=$(echo -n $text | openssl enc -e -des-ede3-ecb -K $key_hex 2>/dev/null | xxd -p -u -c0) authpage=$(curl -Ss -X POST "$header " -D headers.txt \ -d "UserID=$userid &Lang=0&SupportHD=1&NetUserID=&Authenticator=$auth &STBType=$STBType &STBVersion=$STBVersion &conntype=dhcp&STBID=$stbid &templateName=&areaId=&userToken=$token &userGroupId=&productPackageId=-1&mac=$mac &UserField=0&SoftwareVersion=$STBVersion &IsSmartStb=0&desktopId=&stbmaker=&XMPPCapability=&ChipID=&VIP=" \ http://$EPGIP_PORT /EPG/jsp/ValidAuthenticationHWCTC.jsp) cookie=$(grep 'Cookie: ' headers.txt|sed 's/Set-//' ) UserToken=$(echo "$authpage " | grep -oP '(?<=name="UserToken" value=")(.+?)(?=">)' ) listpage=$(curl -Ss -X POST "$header " \ -d "conntype=dhcp&UserToken=$UserToken &SupportHD=1&Lang=1" \ -H "$cookie " \ http://$EPGIP_PORT /EPG/jsp/getchannellistHWCTC.jsp) channel=$(echo "$listpage " | grep "Authentication.CTCSetConfig('Channel','" |grep -Ev '购|聚鲨环球|食全食美|天视家居|天视优选|天视商城|天视好物|津彩生活|津彩潮流' ) echo "$channel " > getchannellist.txtecho "$channel " |sed -En 's|^.+,ChannelName="(.+)",.+,ChannelURL="igmp://(.+:[0-9]+)",TimeShift.*$|\1,rtp://\2|p' > iptvlist.txt
添加回看地址和FCC的脚本 这个脚本会根据上面脚本抓取到的getchannellist.txt修改上面分享的 m3u 文件,添加时移源及FCC。
#!/usr/bin/env bash channellist=getchannellist.txt m3u=IPTV.m3u m3u_udpxy=IPTV_udpxy.m3u m3u_rtsp=IPTV_rtsp.m3u cp $m3u $m3u .bakrm /tmp/newch.txtsed -i "s| iRet = Authentication.CTCSetConfig('Channel','||g;s|');||g" $channellist sed -i -E 's#catchup="default" catchup-source=".*" tvg-logo=#tvg-logo=#g; s#[\?|\&]fcc=.*$##g' $m3u PLAYLIST="$(cat $m3u) " RTSPLIST="$PLAYLIST " for i in `cat $channellist `; do ChannelURL="$( echo "$i " | sed -E 's#.+ChannelURL="(.+)" ,TimeShift=.+#\1#' | sed 's|igmp://|rtp://|' | sed 's/&/\\&/g' ) " ChannelURL2="$( echo "$ChannelURL " | sed 's|.smil.*||' ) " urlno="$(echo "$PLAYLIST " | grep -n "$ChannelURL2 " $m3u | awk -F: '{print $1}' | head -n1 ) " no="$((urlno-1) )" if [[ "$urlno " == "" ]]; then ChannelName="$( echo "$i " | sed -E 's#.+ChannelName="(.+)" ,UserChannelID=.+#\1#' ) " echo "有新频道:$ChannelName , $ChannelURL " echo "$ChannelName , $ChannelURL " >> /tmp/newch.txt continue fi RTPSURL="$( echo "$i " |sed -E 's#.+ChannelSDP="(igmp.+|)?(rtsp.+)" ,TimeShiftURL.+#\2#' ) " RTPSURL="$( echo "$RTPSURL " | sed -E 's#smil\?.+#smil#' ) " RTSPLIST="$(echo "$RTSPLIST " | sed -E ${urlno}s#^rtp.+\$#"$RTPSURL " # ) " echo "$i " | grep -q 'TimeShift="1"' if [ $? -eq 0 ]; then TimeShiftURL="$( echo "$i " | sed -E 's#.+TimeShiftURL="(.+)" ,ChannelType=.+#\1#' ) &playseek=\${(b)yyyyMMddHHmmss}-\${(e)yyyyMMddHHmmss}" TimeShiftURL="$( echo "$TimeShiftURL " | sed -E 's|smil\?.+&playseek|smil?playseek|g' ) " TimeShiftURL="$( echo "$TimeShiftURL " | sed 's/&/\\&/g' ) " a='tvg-logo=' b='catchup="default" catchup-source="' "$TimeShiftURL " '" tvg-logo=' c='catchup="append" catchup-source="?playseek=${(b)yyyyMMddHHmmss}-${(e)yyyyMMddHHmmss}" tvg-logo=' PLAYLIST="$( echo "$PLAYLIST " | sed ${no}s#"$a " #"$b " #g ) " RTSPLIST="$( echo "$RTSPLIST " | sed ${no}s#"$a " #"$c " #g ) " fi echo "$i " | grep -q 'FCCEnable="1"' if [ $? -eq 0 ]; then ChannelFCCIP="$( echo "$i " | sed -E 's#.+ChannelFCCIP="(([0-9]{1,3}\.){3}[0-9]{1,3})" .+#\1#' ) " ChannelFCCPort="$( echo "$i " | sed -E 's#.+ChannelFCCPort="([0-9]{1,5})" .+#\1#' ) " echo $ChannelURL | grep -q '?' if [ $? -ne 0 ]; then fcca="\?fcc=$ChannelFCCIP :$ChannelFCCPort " PLAYLIST="$(echo "$PLAYLIST " | sed -e ${urlno}s#"$ChannelURL$" #"$ChannelURL$fcca " #g ) " fi fi done echo "$PLAYLIST " > $m3u echo "$RTSPLIST " > $m3u_rtsp sed 's|rtp://|http://10.0.0.1:4022/rtp/|g; s|rtsp://|http://10.0.0.1:4022/rtsp/|g' $m3u > $m3u_udpxy
EPG 节目单 换台时可以看到一条去 http://10.206.254.12:4000/pub/json/2023-12-31/30001110000000000000000000001343_calibrated.js?_t=1703995562326 的连接
2023-12-31是 EPG 的日期,最多可以获取到前面三年到后面七天的节目信息。1703995562326则是当前的 13位Unix时间戳,没有这个也会返回结果。
那30001110000000000000000000001343从哪里来呢?
通过搜索可以找到 http://10.128.0.9:8087/IPTV-Interfaces/tianjinmobile/tv/queryChannels?extra={"spCode":"gd"}&devicestbid=9XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX4&requestUuid=1XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX5 点开是 JSON 格式,是里面的channelId,里面还可以看到频道名称。
这两个链接都没有鉴权验证,组装一下就可以爬下来了。不过运营商的EPG还要转格式很麻烦,直接用网上的EPG会更省事一些。
{ "code" : "0000" , "desc" : "成功" , "data" : [ { "channelNo" : "1" , "channelId" : "30001110000000000000000000000102" , "channelName" : "天津卫视高清" , "channelCode" : "" } , { ... ... ... } , { "channelNo" : "902" , "channelId" : "30001110000000000000000000001042" , "channelName" : "902频道" , "channelCode" : "" } ] }