op351 发表于 2023-12-5 17:46

江苏电信IPTV直播源(RTSP认证源)在tvbox上播放(技术分析)

本帖最后由 op351 于 2023-12-5 17:46 编辑

接前两篇帖子
江苏电信IPTV回看地址探究 (技术分析)
江苏电信IPTV回看源在tvbox上进行播放 (技术分析)
在回看源搞定之后 我尝试将江苏电信的组播源也搬到tvbox来播放(原来在diyp播放)
但是搬过来以后发现换台速度极慢 起码要5秒
所以放弃udpxy转换的模式 转向直接使用江苏电信IPTV的RTSP源进行播放
经过验证 换台流畅度和原配机顶盒达到了差不多的程度 可以接受


本篇 我们就来看看如何使用tvbox来进行江苏电信IPTV RTSP源的抓取,存储和播放
顺带实现江苏电信IPTV原生EPG的抓取,存储和使用
tvbox配置文件存储,获取,全流程本地化也在本篇一并分析


tvbox版本:https://github.com/takagen99/Box takagen99版
路由器:斐讯K3
openwrt版本:23.05


接上回

我们打开jar爬虫项目
在iptvAuth方法的frameset_builder获取到返回报文后
使用正则剥离频道id和rstp源
<font size="3">String tsurl = new String(response.body().bytes(), "UTF-8");
      Pattern TimeShiftURLPattern = Pattern.compile("ChannelID=\"(.*)\",ChannelName=\".*\",UserChannelID=\"\\d*\",ChannelURL=\".*\",TimeShift=\"\\d*\",ChannelSDP=\".*\",TimeShiftURL=\"(.*)\",ChannelLogURL=");
      Matcher TimeShiftURLMatch = TimeShiftURLPattern.matcher(tsurl);
      try {
            JSONArray timeShiftJsonTemp = new JSONArray();
            while (TimeShiftURLMatch.find()) {
                JSONObject timet = new JSONObject();
                timet.put("chid", TimeShiftURLMatch.group(1));
                timet.put("rtspurl", TimeShiftURLMatch.group(2));
//                tvlist += TimeShiftURLMatch.group(1) + ",";
//                tvlist += TimeShiftURLMatch.group(2) + "\r\n";
                timeShiftJsonTemp.put(timet);
            }
            timeShiftJson = timeShiftJsonTemp;
      } catch (Exception e) {

      }</font>保存到timeShiftJson中备用


本地EPG实现:

新建方法setepg
1.获取/iptvepg/frame1194/play_channel.jsp的返回报文 (请在第一篇江苏电信IPTV回看地址探究中保存的wireshark文件中自行查找)
2.利用正则剥离江苏电信原生频道排序信息
String chsl = new String(response.body().bytes(), "GB2312");
                Pattern channelListPattern = Pattern.compile("channelAllList\":(.*),\"mixnoAllList\":");
                Matcher channelListMatch = channelListPattern.matcher(chsl);
                channelListMatch.find();
                JSONArray channelListJson = new JSONArray(channelListMatch.group(1));3.在openwrt上新建文件/cgi-bin/epgcheck

#!/usr/bin/lua
local EpgCheck = require 'EpgCheck'
EpgCheck.Run()
4.在openwrt上新建lua模块EpgCheck
local EpgCheck = {}

function EpgCheck.Run()
    io.write("Content-type: text/html\nProgma: no-cache\n\n")
    local time1f = io.popen("ls -l --full-time /www/CCTV1-HD.json")
    local time1 = time1f:read()
    if (time1 == nil)
    then
      print("false")
    else
      local time1date = string.match(time1, '%d+-%d+-%d+')
      local time2f = io.popen("date +\"%Y-%m-%d\"")
      local time2 = time2f:read()
      if (time1date == time2)
      then
            print("true")
      else
            print("false")
      end
    end
end

return EpgCheck
这个cgi的作用是减少epg的获取频次,仅在每天第一次启动tvbox时获取所有频道的EPG信息
5.建立频道分类数组
Integer[] CCTV = new Integer[] {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,140,152};
            List cctv = Arrays.asList(CCTV);
            Integer[] JSTV = new Integer[] {17,18,19,20,21,22,23,24,25,26,83,107,108,109,110,111,113,115};
            List jstv = Arrays.asList(JSTV);
            Integer[] DFTV = new Integer[] {17,32,33,34,35,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,57,58,59,60,61,62,63,64,65,77,85,86,87,107,116,117,118,119,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,137,138,139,141,142};
            List dftv = Arrays.asList(DFTV);
            Integer[] GQTV = new Integer[] {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,25,26,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,52,54,57,58,59,66,67,68,69,70,71,72,73,74,75,76,77,79,80,81,82,105,106,120,140,142,143,155,156,157};
            List gqtv = Arrays.asList(GQTV);6.遍历之前存储的channelListJson
6.1进行频道分类,并存储到变量tvlist中
String channelidTemp = channelListJson.getJSONObject(i).get("channelcode").toString();
                  String channelname = channelListJson.getJSONObject(i).get("channelname").toString();
                  if (!channelname.contains("HD") || channelname.contains("购物")) {
                        continue;
                  }
                  String columncode = channelListJson.getJSONObject(i).get("columncode").toString();
                  String mixno = channelListJson.getJSONObject(i).get("mixno").toString();
                  String realmixno = channelListJson.getJSONObject(i).get("realmixno").toString();
                  for (int j = 0; j < timeShiftJson.length(); j++) {
                        if (channelidTemp.equals(timeShiftJson.getJSONObject(j).get("chid"))) {
                            if (cctv.contains(i)) {
                              tvlist += "CCTV,#genre#" + "\r\n";
                            }else if (jstv.contains(i)) {
                              tvlist += "江苏,#genre#" + "\r\n";
                            }else if (dftv.contains(i)) {
                              tvlist += "卫视,#genre#" + "\r\n";
                            }else if (gqtv.contains(i)) {
                              tvlist += "高清,#genre#" + "\r\n";
                            } else {
                              tvlist += "其他,#genre#" + "\r\n";
                            }
                            tvlist += channelname + ",";
                            tvlist += timeShiftJson.getJSONObject(j).get("rtspurl") + "\r\n";
                        }
                  }6.2在进入epg爬取之前,判断路由器上是否已有epg信息,有则跳过下面的代码
client = new OkHttpClient().newBuilder()
                  .build();
            request = new Request.Builder()
                  .url("http://192.168.8.1/cgi-bin/epgcheck")
                  .get()
                  .build();
            response = client.newCall(request).execute();
            String egresult = response.body().string().trim();
if (egresult.equals("true")) {
                        continue;
                  }6.3epg爬取
获取/iptvepg/frame1194/CHANNEL_PLAYER_UTILS/datas/prevue_list.jsp报文(请在第一篇江苏电信IPTV回看地址探究中保存的wireshark文件中自行查找)
通过正则剥离原始epg信息,并转化为tvbox可用格式
String epgjson = new String(response.body().bytes(), "GB2312");
                  JSONObject epg = new JSONObject(epgjson);
                  String epglist = epg.get("channelPrevue").toString();
                  JSONArray epgJsonBefore = new JSONArray(epglist);
                  JSONArray epgJsonAfter = new JSONArray();
                  for (int k = 0; k < epgJsonBefore.length(); k++) {
                        JSONObject epgitem = new JSONObject();
                        String title = epgJsonBefore.getJSONObject(k).get("prevuename").toString();
                        String begintimet = epgJsonBefore.getJSONObject(k).get("begintime").toString();
                        Pattern timePattern = Pattern.compile("\\d{4}.\\d{2}.\\d{2} (\\d{2}:\\d{2}):\\d{2}");
                        Matcher btimeMatch = timePattern.matcher(begintimet);
                        btimeMatch.find();
                        String begintime = btimeMatch.group(1);
                        String endtimet = epgJsonBefore.getJSONObject(k).get("endtime").toString();
                        Matcher endtimeMatch = timePattern.matcher(endtimet);
                        endtimeMatch.find();
                        String endtime = endtimeMatch.group(1);
                        epgitem.put("start", begintime);
                        epgitem.put("desc", "");
                        epgitem.put("end", endtime);
                        epgitem.put("title", title);
                        epgJsonAfter.put(epgitem);
                  }
                  Date now = new Date();
                  SimpleDateFormat ST = new SimpleDateFormat("yyyy-MM-dd");
                  String nowdate = ST.format(now);
                  JSONObject epgFinal = new JSONObject();
                  epgFinal.put("date", nowdate);
                  epgFinal.put("channel_name", channelname);
                  epgFinal.put("url", "");
                  epgFinal.put("epg_data", epgJsonAfter);
                  String epgFinalstring = epgFinal.toString();6.4上传epg信息到路由器
MediaType mediaType = MediaType.parse("text/plain");
                  RequestBody body = RequestBody.create(mediaType, epgFinalstring);
                  request = new Request.Builder()
                            .url("http://192.168.8.1/cgi-bin/uploadepg")
                            .method("POST", body)
                            .addHeader("User-Agent", URLEncoder.encode(channelname))
                            .addHeader("Content-Type", "text/plain")
                            .build();
                  client.newCall(request).execute();6.5 /cgi-bin/uploadepg内容
#!/usr/bin/lua
local EpgJson = require 'EpgJson'
EpgJson.Run()
6.6lua模块EpgJson内容
local EpgJson = {}
require "nixio"

function EpgJson.Run()
   io.write("Content-type: text/html\nProgma: no-cache\n\n")
   local epgjsonbody = io.read("*all")
      http_headers = nixio.getenv()
      local channame = http_headers['HTTP_USER_AGENT']
      file = io.open(channame .. ".json", "w")
   file:write(epgjsonbody)
   io.close(file)
end

return EpgJson
通过使用user-agent来传输频道名,以简化lua程序
json放置在body中,以原始text传送
最终文件生成为/www/xxx.json


在huikan方法中调用setepg
在homeContent方法中上传分类好的频道列表
OkHttpClient client = new OkHttpClient().newBuilder()
                  .build();
            MediaType mediaType = MediaType.parse("text/plain");
            RequestBody body = RequestBody.create(mediaType, tvlist);
            Request request = new Request.Builder()
                  .url("http://192.168.8.1/cgi-bin/iptvfile")
                  .method("POST", body)
                  .addHeader("Content-Type", "text/plain")
                  .build();
            client.newCall(request).execute();/cgi-bin/iptvfile内容
#!/usr/bin/lua
local WebService = require 'WebService'
WebService.Run()lua模块WebService内容
local WebService = {}

function WebService.Run()
   io.write("Content-type: text/html\nProgma: no-cache\n\n")
   local tvlist = io.read("*all")
   print(tvlist)
   file = io.open("tv.txt", "w")
   file:write(tvlist)
   io.close(file)
end

return WebService
最终频道分类表储存在/www/tv.txt中

接下来我们把源配置文件iptv.json和jar放置在路由器的/www目录下

在iptv.json中我们修改直播源url为本地的tv.txt
epg修改为本地cgi-bin接口getepg

/cgi-bin/getepg内容
#!/usr/bin/lua
local GetEpg = require 'GetEpg'
GetEpg.Run()
lua模块GetEpg内容
local GetEpg = {}
require "nixio"

function GetEpg.Run()
   io.write("Content-type: application/json\nProgma: no-cache\n\n")
   http_headers = nixio.getenv()
   local qs = http_headers['QUERY_STRING']
      local channame = string.match(qs, 'ch=(.*)&')
       file = io.open(channame .. ".json", "r")
       local content = file:read "*a"
       print(content)
       file:close()
end

return GetEpg


接下来我们看看实际效果




gdqpwl 发表于 2023-12-5 18:00

?b,太厉害了。

zsz520 发表于 2023-12-5 18:05

不明觉厉

ldd1231 发表于 2023-12-5 18:25

感谢分享,一看这行文就知道什么叫专业~:lol

0223 发表于 2023-12-5 18:58

厉害

joysboss 发表于 2023-12-5 20:08

插眼,表示好多看不懂https://www.right.com.cn/forum//mobcent//app/data/phiz/default/13.png

moon186 发表于 2023-12-5 20:32

太深澳了,最好写一个转换软件,把m3u和txt直接转换成tvbox节目列表文件

dywly 发表于 2023-12-5 21:22

很好,就是有点看不懂

hai886 发表于 2023-12-6 08:11

厉害,就是看不懂:lol

tzxinqing 发表于 2023-12-6 16:02

大佬真厉害

luohuiyong 发表于 2023-12-6 16:59

就是看不懂

hohaiuhsx 发表于 2023-12-7 10:49

mark一下

mooncake5874 发表于 2023-12-8 09:34

小白只有干瞪眼的份了 期待有一天能有插件

didoo 发表于 2023-12-8 10:14

需要好好思考一下

theo01 发表于 2023-12-8 11:12

:'( 太专业了,要是一键复制黏贴就好了
页: [1] 2
查看完整版本: 江苏电信IPTV直播源(RTSP认证源)在tvbox上播放(技术分析)