纯前端IPTV播放器,源码分享,直播源分享

直播源:https://raw.githubusercontent.com/hujingguang/ChinaIPTV/main/cnTV_AutoUpdate.m3u8

网盘:https://www.yeahhe.online/OneDriveShare/小虎会享

下载源码:https://www.yeahhe.online/d/Tencent%20Cloud/IPTV.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>智能IPTV播放器</title>
    <link href="https://vjs.zencdn.net/7.20.3/video-js.min.css" rel="stylesheet">
    <script src="https://vjs.zencdn.net/7.20.3/video.min.js"></script>
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;700&display=swap" rel="stylesheet">
    <style>
        :root {
            --primary-color: #2c3e50;
            --secondary-color: #34495e;
            --accent-color: #3498db;
            --background-color: #ecf0f1;
            --text-color: #2c3e50;
            --hover-color: #e74c3c;
        }

        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        body {
            font-family: 'Noto Sans SC', sans-serif;
            background-color: var(--background-color);
            color: var(--text-color);
            line-height: 1.6;
        }

        .container {
            max-width: 1400px;
            margin: 0 auto;
            padding: 20px;
        }

        header {
            background-color: var(--primary-color);
            color: white;
            text-align: center;
            padding: 20px 0;
            margin-bottom: 30px;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
        }

        h1 {
            font-size: 2.5em;
            font-weight: 700;
            letter-spacing: 2px;
        }

        .content {
            display: flex;
            gap: 30px;
            background-color: white;
            border-radius: 15px;
            overflow: hidden;
            box-shadow: 0 10px 20px rgba(0,0,0,0.1);
        }

        #channel-list {
            flex: 0 0 300px;
            height: calc(100vh - 200px);
            overflow-y: auto;
            background-color: var(--secondary-color);
            padding: 20px;
            scrollbar-width: thin;
            scrollbar-color: var(--accent-color) var(--secondary-color);
        }

        #channel-list::-webkit-scrollbar {
            width: 8px;
        }

        #channel-list::-webkit-scrollbar-track {
            background: var(--secondary-color);
        }

        #channel-list::-webkit-scrollbar-thumb {
            background-color: var(--accent-color);
            border-radius: 20px;
        }

        #channel-list button {
            display: block;
            width: 100%;
            padding: 15px;
            border: none;
            background-color: transparent;
            color: white;
            text-align: left;
            cursor: pointer;
            transition: all 0.3s ease;
            border-radius: 8px;
            margin-bottom: 10px;
        }

        #channel-list button:hover, #channel-list button.active {
            background-color: var(--accent-color);
            transform: translateX(5px);
        }

        #player-container {
            flex: 1;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            padding: 20px;
        }

        #video-wrapper {
            width: 100%;
            max-width: 1080px;
            position: relative;
            padding-top: 56.25%; /* 16:9 Aspect Ratio */
        }

        .video-js {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            border-radius: 8px;
            overflow: hidden;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
        }

        .channel-item {
            display: flex;
            align-items: center;
        }

        .channel-icon {
            width: 40px;
            height: 40px;
            margin-right: 15px;
            object-fit: contain;
            border-radius: 50%;
            background-color: white;
            padding: 5px;
            transition: transform 0.3s ease;
        }

        #channel-list button:hover .channel-icon {
            transform: scale(1.1);
        }

        .channel-info {
            display: flex;
            flex-direction: column;
            flex-grow: 1;
            overflow: hidden;
        }

        .channel-name {
            font-weight: bold;
            margin-bottom: 5px;
            font-size: 1em;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        .channel-description {
            font-size: 0.8em;
            opacity: 0.8;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        @media (max-width: 968px) {
            .content {
                flex-direction: column;
            }

            #channel-list, #player-container {
                width: 100%;
            }

            #channel-list {
                height: 300px;
                flex: none;
            }
        }
    </style>
</head>
<body>
    <header>
        <h1>智能IPTV播放器</h1>
    </header>
    <div class="container">
        <div class="content">
            <div id="channel-list"></div>
            <div id="player-container">
                <div id="video-wrapper">
                    <video-js id="my-video" class="video-js vjs-big-play-centered" controls preload="auto" data-setup='{"techOrder": ["html5"]}'>
                        <p class="vjs-no-js">
                            To view this video please enable JavaScript, and consider upgrading to a web browser that
                            <a href="https://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a>
                        </p>
                    </video-js>
                </div>
            </div>
        </div>
    </div>

    <script>
        const playlistUrl = 'https://raw.githubusercontent.com/hujingguang/ChinaIPTV/main/cnTV_AutoUpdate.m3u8';
        const channelList = document.getElementById('channel-list');
        const player = videojs('my-video', {
            html5: {
                hls: {
                    overrideNative: true
                }
            }
        });

        let currentChannelIndex = -1;
        let channels = [];

        function isValidIpv4Url(string) {
            try {
                const url = new URL(string);
                return !string.includes('[') && !string.includes(']');
            } catch (_) {
                return false;
            }
        }

        function parseM3ULine(line) {
            if (line.startsWith('http://') || line.startsWith('https://')) {
                return line.trim();
            }
            return null;
        }

        function extractTvgInfo(extinf) {
            const logoMatch = extinf.match(/tvg-logo="([^"]+)"/);
            const nameMatch = extinf.match(/tvg-name="([^"]+)"/);
            return {
                logo: logoMatch ? logoMatch[1] : null,
                name: nameMatch ? nameMatch[1] : null
            };
        }

        function playChannel(index) {
            if (index >= 0 && index < channels.length) {
                const channel = channels[index];
                player.src({
                    type: 'application/x-mpegURL',
                    src: channel.url
                });
                player.play();
                channelList.querySelectorAll('button').forEach(btn => btn.classList.remove('active'));
                channelList.children[index].classList.add('active');
                currentChannelIndex = index;
                channelList.children[index].scrollIntoView({ behavior: 'smooth', block: 'nearest' });
            }
        }

        fetch(playlistUrl)
            .then(response => response.text())
            .then(data => {
                const lines = data.split('\n');
                for (let i = 0; i < lines.length; i++) {
                    if (lines[i].startsWith('#EXTINF')) {
                        const tvgInfo = extractTvgInfo(lines[i]);
                        const title = lines[i].split(',')[1];
                        const url = parseM3ULine(lines[i+1]);
                        if (url && isValidIpv4Url(url)) {
                            channels.push({ title, url, tvgInfo });
                            const button = document.createElement('button');
                            const channelItem = document.createElement('div');
                            channelItem.className = 'channel-item';

                            if (tvgInfo.logo) {
                                const img = document.createElement('img');
                                img.src = tvgInfo.logo;
                                img.className = 'channel-icon';
                                img.onerror = function() { this.style.display = 'none'; };
                                channelItem.appendChild(img);
                            }

                            const infoDiv = document.createElement('div');
                            infoDiv.className = 'channel-info';

                            const nameSpan = document.createElement('span');
                            nameSpan.className = 'channel-name';
                            nameSpan.textContent = tvgInfo.name || title;
                            infoDiv.appendChild(nameSpan);

                            if (tvgInfo.name && tvgInfo.name !== title) {
                                const descSpan = document.createElement('span');
                                descSpan.className = 'channel-description';
                                descSpan.textContent = title;
                                infoDiv.appendChild(descSpan);
                            }

                            channelItem.appendChild(infoDiv);
                            button.appendChild(channelItem);
                            channelList.appendChild(button);
                        }
                    }
                }
                if (channels.length > 0) {
                    playChannel(0);
                }
                // 设置频道点击事件
                channelList.querySelectorAll('button').forEach((button, index) => {
                    button.onclick = function() {
                        playChannel(index);
                    };
                });
            })
            .catch(error => console.error('Error:', error));
    </script>
</body>
</html>```
12 个赞

感谢

感谢

1 个赞

感谢分享

很好,刚好论坛的直播系统可以用这个(

1 个赞

老哥自己写的吗

欢迎改进,目前不支持IPv6和H265

对的,加上Claude辅助

谢谢分享

加一个高清直播源,送给大家

https://gitee.com/jonesheye/main/raw/master/HIGH.m3u


更加高清

感谢分享

m3u.zip (7.8 KB)
我也来分享一套,从github上收集的,都是ipv4的

感谢,我把你的文件做成链接了https://gitee.com/jonesheye/main/raw/master/new.m3u

网页有个问题,就是如果源不支持cors则直接无法播放

3 个赞

这个合集有点狠啊,太多了

1 个赞

感谢分享

感谢,打卡

1 个赞

不错

感谢大佬