去广告技术文档
1. 系统概述
本项目设计为M3U8播放地址处理工具,核心功能在于自动检测并移除嵌入在M3U8播放列表内的广告片段,同时兼容处理嵌套的M3U8链接。利用axios
进行网络通信以检索M3U8文件,提供无广告的纯净播放体验。
去除网络插播广告虽满足部分用户需求,但需谨慎行事,考虑到可能引发的法律、技术及道德后果。用户在寻求改善在线体验的同时,应选择合法、安全的方式,并尊重内容创作者和平台的权益
2. 关键模块说明
2.1. Array.prototype.toReversed Polyfill
- 目标: 确保在所有环境下均可使用数组的倒序功能。
- 价值: 提高代码的跨环境兼容性。
2.2. URL解析与拼接 (resolve
, urljoin
)
- 功能: 实现URL的正确解析与片段的拼接,特别是在处理M3U8内相对路径时。
- 重要性: 确保资源引用的准确无误。
2.3. 广告检测与过滤核心逻辑 (fixAdM3u8Ai
)
- 职责:
- 智能识别广告: 通过比对播放片段特征识别广告内容。
- 嵌套处理: 处理M3U8列表中嵌套的其他M3U8链接。
- URL修正: 转换所有播放片段URL为绝对路径。
- 技术亮点:
- 异步数据获取。
- 精准的广告识别算法。
- 详细的日志记录。
- 工作流程:
- 输入: 接收用户提供的M3U8播放地址。
- 获取M3U8内容: 使用
axios
执行网络请求。
- 获取M3U8内容: 使用
- 基础处理: 清理文本,转换URL为绝对形式。
- 广告识别与剔除:
- 4.1. 分析首尾播放片段,计算相似度。
- 4.2. 依据阈值移除疑似广告URL。
- 递归处理嵌套M3U8(如存在)。
- 输出: 返回处理后的纯净M3U8列表。
- 日志记录: 全程记录关键步骤和性能指标。
2.4. 遗留问题
- 匹配精度
- 同域名广告识别
3. 示例代码
代码由
@hpindigo
提供
点我查看示例代码
ts
import axios from 'axios';
if (typeof Array.prototype.toReversed !== 'function') {
Object.defineProperty(Array.prototype, 'toReversed', {
value: function () {
const clonedList = this.slice();
// 倒序新数组
const reversedList = clonedList.reverse();
return reversedList;
},
enumerable: false,
});
}
const resolve = (from, to) => {
const resolvedUrl = new URL(to, new URL(from, 'resolve://'));
if (resolvedUrl.protocol === 'resolve:') {
const { pathname, search, hash } = resolvedUrl;
return pathname + search + hash;
}
return resolvedUrl.href;
};
/**
* url拼接
* @param fromPath 初始当前页面url
* @param nowPath 相对当前页面url
* @returns {*}
*/
const urljoin = (fromPath, nowPath) => {
fromPath = fromPath || '';
nowPath = nowPath || '';
return resolve(fromPath, nowPath);
};
/**
* 智能对比去除广告。支持嵌套m3u8。只需要传入播放地址
* @param m3u8_url m3u8播放地址
* @param headers 自定义访问m3u8的请求头,可以不传
* @returns {string}
*/
const fixAdM3u8Ai = async (m3u8_url: string, headers: object = {}) => {
let ts = new Date().getTime();
let option = headers;
// 字符串比较
function b(s1, s2) {
let i = 0;
while (i < s1.length) {
if (s1[i] !== s2[i]) {
break;
}
i++;
}
return i;
}
function reverseString(str) {
return str.split('').reverse().join('');
}
let m3u8 = await axios({ url: m3u8_url, method: 'get', ...option });
m3u8 = m3u8
.trim()
.split('\n')
.map((it) => (it.startsWith('#') ? it : urljoin(m3u8_url, it)))
.join('\n');
m3u8 = m3u8.replace(/\n\n/gi, '\n');
let last_url = m3u8.split('\n').slice(-1)[0];
if (last_url.length < 5) {
last_url = m3u8.split('\n').slice(-2)[0];
}
if (last_url.includes('.m3u8') && last_url !== m3u8_url) {
m3u8_url = urljoin(m3u8_url, last_url);
console.log('嵌套的m3u8_url:' + m3u8_url);
m3u8 = await axios({ url: m3u8_url, method: 'get', ...option });
}
let s = m3u8
.trim()
.split('\n')
.filter((it) => it.trim())
.join('\n');
let ss = s.split('\n');
let firststr = '';
let maxl = 0;
let kk = 0;
let kkk1 = 1;
let kkk2 = 0;
let secondstr = '';
for (let i = 0; i < ss.length; i++) {
let s = ss[i];
if (!s.startsWith('#')) {
if (kk == 0) firststr = s;
if (kk > 0) {
if (maxl > b(firststr, s) + 1) {
if (secondstr.length < 5) secondstr = s;
kkk2++;
} else {
maxl = b(firststr, s);
kkk1++;
}
}
kk++;
if (kk >= 30) break;
}
}
if (kkk2 > kkk1) firststr = secondstr;
let firststrlen = firststr.length;
let ml = Math.round(ss.length / 2).toString().length;
let maxc = 0;
let laststr = ss.toReversed().find((x) => {
if (!x.startsWith('#')) {
let k = b(reverseString(firststr), reverseString(x));
maxl = b(firststr, x);
maxc++;
if (firststrlen - maxl <= ml + k || maxc > 10) {
return true;
}
}
return false;
});
console.log('最后一条切片:' + laststr);
let ad_urls: string[] = [];
for (let i = 0; i < ss.length; i++) {
let s = ss[i];
if (!s.startsWith('#')) {
if (b(firststr, s) < maxl) {
ad_urls.push(s);
ss.splice(i - 1, 2);
i = i - 2;
} else {
ss[i] = urljoin(m3u8_url, s);
}
} else {
ss[i] = s.replace(/URI=\"(.*)\"/, 'URI="' + urljoin(m3u8_url, '$1') + '"');
}
}
console.log('处理的m3u8地址:' + m3u8_url);
console.log('----广告地址----');
console.log(ad_urls);
m3u8 = ss.join('\n');
console.log('处理耗时:' + (new Date().getTime() - ts).toString());
return m3u8;
};
export { fixAdM3u8Ai };
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
143
144
145
146
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
143
144
145
146
4. 结论
算法核心十分重要,微小的逻辑错误,可能引发连锁反应,导致误删非广告部分正常内容的关键数据。因此,在面临模棱两可的决策时,应更加谨慎细致,力求在保留数据的完整性与准确性的同时,实现体验的升级。