近期在整理家里的iptv的节目表,iptv提供的epg不完整,更新不及时,准备手动整理一个epg数据,当然,已经有比较知名的免费服务了http://epg.51zmt.top:8000/,之所以还是要自己搞,主要还是因为51zmt的频道信息匹配在我家的xteve里面每次都要收工匹配,爱折腾的人经常变换播放源数据,手动匹配epg数据实在是太痛苦,毕竟我的播放源数据也不少.

xteve

通过Chrome模拟手机页面,打开某个频道的节目表,这里以CCTV1的节目单为例,打开后能看到上午的节目排期表,要通过查看更多来加载剩下的节目单,这里也是本次的关键,涉及到一部分加密的逻辑.

https://m.tvmao.com/api/pg?p=

chrome-01

通过查看页面源码,可以找到发送请求的处理逻辑,这里请求核心的p参数的获取来自于var d = A.d("a", val);,所以重点就是看这部分的实现.
chrome-source-js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
var A = {
_keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
_keyStr2: "KQMFS=DVGO",
J: function(a) {
var b = "";
var c, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
a = A._C(a);
while (i < a.length) {
c = a.charCodeAt(i++);
chr2 = a.charCodeAt(i++);
chr3 = a.charCodeAt(i++);
enc1 = c >> 2;
enc2 = ((c & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64
} else if (isNaN(chr3)) {
enc4 = 64
}
b = b + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4)
}
return b
},
H: function(a) {
a = a.toString();
var b = '';
for (var i = 0; i < a.length; i++) {
b += this._keyStr2[a.charAt(i)]
}
for (var i = 0; i < a.length; i++) {
b += this._keyStr[a.charAt(i)]
}
return b
},
_C: function(a) {
a = a.replace(/\r\n/g, "\n");
var b = "";
for (var n = 0; n < a.length; n++) {
var c = a.charCodeAt(n);
if (c < 128) {
b += String.fromCharCode(c)
} else if ((c > 127) && (c < 2048)) {
b += String.fromCharCode((c >> 6) | 192);
b += String.fromCharCode((c & 63) | 128)
} else {
b += String.fromCharCode((c >> 12) | 224);
b += String.fromCharCode(((c >> 6) & 63) | 128);
b += String.fromCharCode((c & 63) | 128)
}
}
return b
},
E: function(a) {
$(':input[name="ed"]', a).val(A.J('l' + $(".ed", a).val() + 'o'))
},
B: function(a) {
var b = (new Date()).getTime();
if (a != undefined)
return A.J(a + '|' + b);
else
return A.J('' + b)
},
e: function(u) {
var x = 1;
var f = $('form').first();
var a = f.find("input[class='baidu']");
if (a != undefined) {
x = 2
} else if (u != undefined) {
x = u
}
if (f == undefined)
return x;
return f.attr('a')
},
c: function(e) {
var v;
var f = $('form').first();
if (f == undefined)
return "";
var s = f.find("*[type='submit']");
if (s == undefined) {
v = f.find("input[class='qq']");
if (v == undefined)
return "";
v = e
}
v = s.attr('id');
return v
},
d: function(p, h) {
var v = A.w(h);
var a = $("div.fix");
var x = a || p;
if (a != undefined) {
x = h || $("s.fix1")
}
x = A.c();
var b = new Date();
var c = b.getUTCDate();
var d = b.getDay();
var i = d == 0 ? 7 : d;
i = i * i;
var F = this._keyStr.charAt(i);
return F + A.J(x + "|" + A.e(p)) + v
},
w: function(v) {
var t = $("head");
var a = "|";
if (t == undefined) {
tl = "/"
} else {
tl = v
}
var r = A.J(a + k(tl));
return r
},
s: function(a, b) {
var c = this._keyStr.charAt(37);
return A.J(c + a)
}
};
var k = function(a) {
var f = $('form').first();
if (f == undefined)
return "";
var b = f.attr('id');
if (b == undefined)
f.attr('id', a);
return f.attr('q')
};
$(function() {
var b = $('<input type="hidden" name="ek"/>');
b.val(A.B());
$('form[name="frmlogin"]').append(b);
$('a[class^="by"]').each(function() {
var a = $(this).attr("href") + "&ek=" + encodeURIComponent(A.B());
$(this).attr("href", a)
})
});

找到了这部分的代码实现,我们可以模拟调用一下方法,看看获取到的数据是什么样的.

chrome-console-01
通过调用js的方法,可以看到接口需要的参数能够正常生成:
chrome-console-02

追踪方法的实现,过滤掉一些方法的实现,实际上生成参数过程中只要三个参数,而这三个参数来源于页面里面的form表单的几个数据:

chrome-source-form

精简掉多余的逻辑后,只要实现js中的三个方法即可(因版权原因,这里仅提供分析过程,Golang的代码就不给出了):
golang-funcs