现在有很多线上 IPTV 服务,限制只能在浏览器的播放器观看,可以通过 PHP 爬取对应的流媒体源,来实现在其他应用或电视上观看。

IPTV 源分类

不限制 Referer

这类源最简单,直接 PHP 发送请求获取到流媒体的链接。

iptv-sample-a.php 代码片段

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<?php
    function get($url){
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
        $result = curl_exec($ch);
        curl_close($ch);
        return $result;
    }

严格限制 Referer

这类源的服务器会检查所有访问请求的 HTTP Header,如果 Referer 不匹配当前提供服务的网站域名,就拒绝返回正确的流媒体数据。现在大部分的源服务器都会检查 Header。

一般的解决方法是用浏览器正常打开源服务器的页面,通过 Chrome/Edge 的开发者工具获取到正确的 HTTP Header,再使用 PHP 发送相同的 Header 来模拟浏览器访问。

iptv-sample-b.php 代码片段

 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
<?php
    $id = isset($_GET['id']) ? $_GET['id'] : '-1';

    if($id !== '-1'){
        $baseurl = 'https://iptv.stream/';

        $header = array(
                'Referer: https://iptv.stream/'
            );
        
        // 获取 ts
        // 流媒体的链接为 https://iptv.stream/100/index.php
        $url = $baseurl.$id.'/index.php';
        $res = get($url, $header);
        $reg = '/(.*?.ts)/';
        preg_match($reg, $res, $arr);
        $ts = $arr[1];

        // 如果 ts 为相对路径,则在前面加上 $baseurl 后返回;如果为绝对路径,直接返回。
        ......

        // 构建m3u8内容
        $m3u8_content = "#EXTM3U\n";
        $m3u8_content .= "#EXT-X-STREAM-INF:PROGRAM-ID=1,   BANDWIDTH=2000000\n";
        $m3u8_content .= "$ts\n";

        // 输出m3u8内容
        echo $m3u8_content;
    }

    function get($url, $header){
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        $result = curl_exec($ch);
        curl_close($ch);
        return $result;
    }

ts 文件也被检查

如果源服务器连 ts 文件的请求也要检查 Header,需要将 m3u8 内的 ts 链接修改为 php 所在的服务器,并在收到 ts 请求后,附加正确的 header,发往源服务器,最后将获取到的二进制数据返回给客户端。缺点是 php 所在的服务器需要消耗所有的上下行流媒体流量。

iptv-sample-c.php 代码片段

 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
<?php
    $id = isset($_GET['id']) ? $_GET['id'] : '-1';
    $ts = isset($_GET['ts']) ? $_GET['ts'] : '-1';

    if($id !== '-1'){
        $baseurl = 'https://iptv.stream/';

        $header = array(
                'Referer: https://iptv.stream/'
            );
        
        // 获取 m3u8
        // 流媒体的链接为 https://iptv.stream/100/index.php
        $url = $baseurl.$id.'/index.php';
        $res = get($url, $header, $e_url);
        $reg = '/(.*?.m3u8)/';
        preg_match($reg, $res, $arr);
        $str = $arr[1];

        // 获取 ts
        $baseurl = dirname($e_url);
        $str = $baseurl.'/'.$str;
        $res = get($str, $header, $e_url);
        $reg = '/(.*\.ts)/i';
        preg_match_all($reg, $res, $arr);
        foreach ($arr[1] as $key => $value){
            $res = str_replace($value, 'iptv-sample-c.php?ts='.strtr(base64_encode("$baseurl/$value"), '+/', '-_'), $res);
        }
        echo($res);
    }else if($ts !== '-1'){
        $header = array(
            'Referer: https://iptv.stream/'
        );
        $decoded_ts = base64_decode(strtr($ts, '-_', '+/'));
        $res = ts($decoded_ts, $header);
    }

    function get($url, $header, &$e_url){
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        $result = curl_exec($ch);
        $e_url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
        curl_close($ch);
        return $result;
    }

    function ts($url, $header){
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        $result = curl_exec($ch);
        curl_close($ch);
    }

源服务器使用 flv

如果源服务器返回的是 flv 文件,可以直接将请求重定向到这个链接。

iptv-sample-d.php 代码片段

 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
<?php
    $id = isset($_GET['id']) ? $_GET['id'] : '-1';
    
    if($id !== '-1'){
        $baseurl = 'https://iptv.stream/';

        $header = array(
                'Referer: https://iptv.stream/'
            );
        
        // 获取 m3u8
        // 流媒体的链接为 https://iptv.stream/100/index.php
        $url = $baseurl.$id.'/index.php';
        $res = get($url, $header, $e_url);
        $reg = '/(.*?.flv)/';
        preg_match($reg, $res, $arr);
        $flv = $arr[1];
        
        // 重定向链接到 flv 文件
        header('location:'.$flv);
    }

    function get($url, $header, &$e_url){
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        $result = curl_exec($ch);
        $e_url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
        curl_close($ch);
        return $result;
    }