懒人必备:一键部署Cloudflare Worker 电视台直播播放器

作者:Q师傅 发布时间: 2026-02-25 阅读量:16 评论数:0
// Cloudflare Worker 直播播放器

// 部署:复制全部代码到 Worker 编辑器中保存即可

// 您提供的直播源列表 - 带中文频道名称

const CHANNELS = [

    ["CCTV1 综合", "https://zb.fxynet.cn/ysp.php?pd=cctv1"],

    ["CCTV2 财经", "https://zb.fxynet.cn/ysp.php?pd=cctv2"],

    ["CCTV3 综艺", "https://zb.fxynet.cn/ysp.php?pd=cctv3"],

    ["CCTV4 中文国际", "https://zb.fxynet.cn/ysp.php?pd=cctv4"],

    ["CCTV5 体育", "https://zb.fxynet.cn/ysp.php?pd=cctv5"],

    ["CCTV5+ 体育赛事", "https://zb.fxynet.cn/ysp.php?pd=cctv5p"],

    ["CCTV6 电影", "https://zb.fxynet.cn/ysp.php?pd=cctv6"],

    ["CCTV7 国防军事", "https://zb.fxynet.cn/ysp.php?pd=cctv7"],

    ["CCTV8 电视剧", "https://zb.fxynet.cn/ysp.php?pd=cctv8"],

    ["CCTV9 纪录", "https://zb.fxynet.cn/ysp.php?pd=cctv9"],

    ["CCTV10 科教", "https://zb.fxynet.cn/ysp.php?pd=cctv10"],

    ["CCTV11 戏曲", "https://zb.fxynet.cn/ysp.php?pd=cctv11"],

    ["CCTV12 社会与法", "https://zb.fxynet.cn/ysp.php?pd=cctv12"],

    ["CCTV13 新闻", "https://zb.fxynet.cn/ysp.php?pd=cctv13"],

    ["CCTV14 少儿", "https://zb.fxynet.cn/ysp.php?pd=cctv14"],

    ["CCTV15 音乐", "https://zb.fxynet.cn/ysp.php?pd=cctv15"],

    ["CCTV16 奥林匹克", "https://zb.fxynet.cn/ysp.php?pd=cctv16"],

    ["CCTV16 4K 奥林匹克", "https://zb.fxynet.cn/ysp.php?pd=cctv164k"],

    ["CCTV17 农业农村", "https://zb.fxynet.cn/ysp.php?pd=cctv17"],

    ["CCTV4K 超高清", "https://zb.fxynet.cn/ysp.php?pd=cctv4k"],

    ["CCTV8K 超高清", "https://zb.fxynet.cn/ysp.php?pd=cctv8k"],

    ["CGTN 新闻", "https://zb.fxynet.cn/ysp.php?pd=cgtn"],

    ["CGTN 法语", "https://zb.fxynet.cn/ysp.php?pd=cgtnfayu"],

    ["CGTN 俄语", "https://zb.fxynet.cn/ysp.php?pd=cgtneyu"],

    ["CGTN 阿拉伯语", "https://zb.fxynet.cn/ysp.php?pd=cgtnalaboyu"],

    ["CGTN 西班牙语", "https://zb.fxynet.cn/ysp.php?pd=cgtnxibanyayu"],

    ["CGTN 外语纪录", "https://zb.fxynet.cn/ysp.php?pd=cgtnwaiyujilu"],

    ["CETV1 中国教育", "https://zb.fxynet.cn/ysp.php?pd=cetv1"],

    ["国学频道", "https://zb.fxynet.cn/ysp.php?pd=guoxuepindao"],

    ["北京卫视", "https://zb.fxynet.cn/ysp.php?pd=beijingweishi"],

    ["东方卫视", "https://zb.fxynet.cn/ysp.php?pd=dongfangweishi"],

    ["江苏卫视", "https://zb.fxynet.cn/ysp.php?pd=jiangsuweishi"],

    ["浙江卫视", "https://zb.fxynet.cn/ysp.php?pd=zhejiangweishi"],

    ["湖南卫视", "https://zb.fxynet.cn/ysp.php?pd=hunanweishi"],

    ["湖北卫视", "https://zb.fxynet.cn/ysp.php?pd=hubeiweishi"],

    ["广东卫视", "https://zb.fxynet.cn/ysp.php?pd=guangdongweishi"],

    ["广西卫视", "https://zb.fxynet.cn/ysp.php?pd=guangxiweishi"],

    ["黑龙江卫视", "https://zb.fxynet.cn/ysp.php?pd=heilongjiangweishi"],

    ["海南卫视", "https://zb.fxynet.cn/ysp.php?pd=hainanweishi"],

    ["重庆卫视", "https://zb.fxynet.cn/ysp.php?pd=chongqingweishi"],

    ["深圳卫视", "https://zb.fxynet.cn/ysp.php?pd=shenzhenweishi"],

    ["四川卫视", "https://zb.fxynet.cn/ysp.php?pd=sichuanweishi"],

    ["河南卫视", "https://zb.fxynet.cn/ysp.php?pd=henanweishi"],

    ["东南卫视", "https://zb.fxynet.cn/ysp.php?pd=dongnanweishi"],

    ["贵州卫视", "https://zb.fxynet.cn/ysp.php?pd=guizhouweishi"],

    ["江西卫视", "https://zb.fxynet.cn/ysp.php?pd=jiangxiweishi"],

    ["辽宁卫视", "https://zb.fxynet.cn/ysp.php?pd=liaoningweishi"],

    ["安徽卫视", "https://zb.fxynet.cn/ysp.php?pd=anhuiweishi"],

    ["河北卫视", "https://zb.fxynet.cn/ysp.php?pd=hebeiweishi"],

    ["山东卫视", "https://zb.fxynet.cn/ysp.php?pd=shandongweishi"],

    ["天津卫视", "https://zb.fxynet.cn/ysp.php?pd=tianjinweishi"],

    ["陕西卫视", "https://zb.fxynet.cn/ysp.php?pd=shaanxiweishi"],

    ["内蒙古卫视", "https://zb.fxynet.cn/ysp.php?pd=neimengguweishi"],

    ["甘肃卫视", "https://zb.fxynet.cn/ysp.php?pd=gansuweishi"],

    ["宁夏卫视", "https://zb.fxynet.cn/ysp.php?pd=ningxiaweishi"],

    ["山西卫视", "https://zb.fxynet.cn/ysp.php?pd=shanxiweishi"],

    ["云南卫视", "https://zb.fxynet.cn/ysp.php?pd=yunnanweishi"],

    ["吉林卫视", "https://zb.fxynet.cn/ysp.php?pd=jilinweishi"],

    ["青海卫视", "https://zb.fxynet.cn/ysp.php?pd=qinghaiweishi"],

    ["西藏卫视", "https://zb.fxynet.cn/ysp.php?pd=xizangweishi"],

    ["新疆卫视", "https://zb.fxynet.cn/ysp.php?pd=xinjiangweishi"],

    ["兵团卫视", "https://zb.fxynet.cn/ysp.php?pd=bingtuanweishi"]

];

// 找到浙江卫视的索引(默认选中)

const DEFAULT_CHANNEL_INDEX = CHANNELS.findIndex(ch => ch[0].includes("浙江卫视"));

// HTML 页面模板 - 大窗口版本,无提示文字

const HTML_TEMPLATE = `<!DOCTYPE html>

<html lang="zh">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>电视直播</title>

    <style>

        * {

            box-sizing: border-box;

            font-family: 'Segoe UI', Roboto, system-ui, sans-serif;

            margin: 0;

            padding: 0;

        }

        body {

            background: #0b1120;

            min-height: 100vh;

            display: flex;

            align-items: center;

            justify-content: center;

            padding: 16px;

        }

        .player-container {

            max-width: 1400px;

            width: 100%;

            background: #0f172a;

            border-radius: 2rem;

            box-shadow: 0 25px 50px -12px rgba(0,0,0,0.8), 0 0 0 1px rgba(56, 189, 248, 0.2);

            overflow: hidden;

            backdrop-filter: blur(4px);

            border: 1px solid #1e293b;

        }

        .video-wrapper {

            background: #030712;

            position: relative;

            aspect-ratio: 16 / 9;

        }

        #live-video {

            width: 100%;

            height: 100%;

            display: block;

            background: #000;

            outline: none;

        }

        .video-control-bar {

            display: flex;

            align-items: center;

            gap: 1rem;

            padding: 0.75rem 1.5rem;

            background: #0f172a;

            flex-wrap: wrap;

        }

        .channel-info {

            display: flex;

            align-items: center;

            gap: 0.5rem;

            font-weight: 600;

            background: #1e293b;

            padding: 0.4rem 1rem;

            border-radius: 40px;

            color: #b9d3f0;

            font-size: 0.95rem;

            border: 1px solid #334155;

        }

        .channel-info i {

            color: #38bdf8;

        }

        .control-btn {

            background: #1e293b;

            border: none;

            color: #e2e8f0;

            width: 40px;

            height: 40px;

            border-radius: 40px;

            display: inline-flex;

            align-items: center;

            justify-content: center;

            cursor: pointer;

            transition: 0.2s;

            font-size: 1.3rem;

            border: 1px solid #334155;

        }

        .control-btn:hover {

            background: #2d3b52;

            color: #90d2ff;

            border-color: #38bdf8;

        }

        .control-btn.primary {

            background: #2563eb;

            border-color: #3b82f6;

            color: white;

        }

        .control-btn.primary:hover {

            background: #3b82f6;

        }

        .channel-selector {

            flex: 2;

            min-width: 280px;

        }

        .channel-selector select {

            width: 100%;

            padding: 0.65rem 1rem;

            background: #1e293b;

            border: 1px solid #334155;

            border-radius: 40px;

            color: #f1f5f9;

            font-size: 0.95rem;

            cursor: pointer;

            appearance: none;

            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");

            background-repeat: no-repeat;

            background-position: right 1rem center;

            background-size: 16px;

        }

        .channel-selector select option {

            background: #0f172a;

            color: #e2e8f0;

            padding: 4px;

        }

        .status-badge {

            color: #94a3b8;

            font-size: 0.8rem;

            background: #1e293b;

            padding: 4px 12px;

            border-radius: 24px;

            display: inline-block;

            border: 1px solid #334155;

            margin-left: auto;

        }

    </style>

    <script src="https://cdn.jsdelivr.net/npm/hls.js@1.5.7/dist/hls.min.js"></script>

</head>

<body>

<div class="player-container" id="app">

    <div class="video-wrapper">

        <video id="live-video" controls autoplay playsinline></video>

    </div>

    <div class="video-control-bar">

        <div class="channel-info">

            <i>📺</i> <span id="current-channel-name">浙江卫视</span>

        </div>

        <button class="control-btn" id="reload-btn" title="重新加载">↻</button>

        <button class="control-btn primary" id="fullscreen-btn" title="全屏">⛶</button>

        <div class="channel-selector">

            <select id="channel-select-dropdown"></select>

        </div>

        <span class="status-badge" id="channel-count"></span>

    </div>

</div>

<script>

    (function() {

        const CHANNELS = ${JSON.stringify(CHANNELS)};

        const DEFAULT_INDEX = ${DEFAULT_CHANNEL_INDEX};

        const video = document.getElementById('live-video');

        const currentNameSpan = document.getElementById('current-channel-name');

        const dropdown = document.getElementById('channel-select-dropdown');

        const reloadBtn = document.getElementById('reload-btn');

        const fullscreenBtn = document.getElementById('fullscreen-btn');

        const channelCountSpan = document.getElementById('channel-count');

        let currentIndex = DEFAULT_INDEX;

        let hlsInstance = null;

        channelCountSpan.innerText = CHANNELS.length + '个频道';

        function destroyHls() {

            if (hlsInstance) {

                try { hlsInstance.destroy(); } catch (e) {}

                hlsInstance = null;

            }

        }

        function playChannelByIndex(index) {

            if (index < 0 || index >= CHANNELS.length) return;

            const [name, url] = CHANNELS[index];

            currentNameSpan.innerText = name;

            

            if (dropdown) dropdown.value = index;

            destroyHls();

            video.pause();

            video.removeAttribute('src');

            video.load();

            // 智能播放

            if (url.includes('.m3u8') || url.includes('ysp.php')) {

                if (video.canPlayType('application/vnd.apple.mpegurl')) {

                    video.src = url;

                } else if (Hls.isSupported()) {

                    const hls = new Hls({ enableWorker: true, lowLatencyMode: true });

                    hlsInstance = hls;

                    hls.loadSource(url);

                    hls.attachMedia(video);

                    hls.on(Hls.Events.ERROR, function (event, data) {

                        if (data.fatal) {

                            destroyHls();

                            video.src = url;

                        }

                    });

                } else {

                    video.src = url;

                }

            } else {

                video.src = url;

            }

            video.play().catch(e => console.log('自动播放被阻止', e));

        }

        // 渲染下拉框

        let dropdownHtml = '';

        CHANNELS.forEach((ch, idx) => {

            const [name] = ch;

            dropdownHtml += <option value="\${idx}" \${idx === DEFAULT_INDEX ? 'selected' : ''}>\${name}</option>\;

        });

        dropdown.innerHTML = dropdownHtml;

        dropdown.addEventListener('change', (e) => {

            const idx = parseInt(e.target.value, 10);

            if (!isNaN(idx)) {

                currentIndex = idx;

                playChannelByIndex(currentIndex);

            }

        });

        reloadBtn.addEventListener('click', () => playChannelByIndex(currentIndex));

        

        fullscreenBtn.addEventListener('click', () => {

            if (video.requestFullscreen) video.requestFullscreen();

            else if (video.webkitRequestFullscreen) video.webkitRequestFullscreen();

            else if (video.msRequestFullscreen) video.msRequestFullscreen();

        });

        document.addEventListener('keydown', (e) => {

            if (e.code === 'Space' && document.activeElement !== dropdown) {

                e.preventDefault();

                if (video.paused) video.play(); else video.pause();

            }

        });

        // 默认播放浙江卫视

        playChannelByIndex(DEFAULT_INDEX);

    })();

</script>

</body>

</html>`;

// Worker 主入口

export default {

    async fetch(request, env, ctx) {

        const url = new URL(request.url);

        

        // 如果是请求根路径,返回HTML页面

        if (url.pathname === "/" || url.pathname === "") {

            return new Response(HTML_TEMPLATE, {

                headers: {

                    "Content-Type": "text/html;charset=UTF-8",

                    "Cache-Control": "public, max-age=3600",

                },

            });

        }

        // 其他路径返回404

        return new Response("Not Found", { status: 404 });

    },

};

分享:

评论