AI 生成: 途播播放器专用直播源,每天自动更新央视、卫视、地方台等频道,生成可访问的 M3U 格式文件。
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
__pycache__/
|
||||
*.pyc
|
||||
output/*.m3u
|
||||
.venv/
|
||||
venv/
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 安装 cron
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends cron \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
# 配置定时任务:每天凌晨 3 点更新
|
||||
RUN echo "0 3 * * * cd /app && /usr/local/bin/python /app/updater.py >> /var/log/updater.log 2>&1" > /etc/cron.d/tubo \
|
||||
&& chmod 0644 /etc/cron.d/tubo \
|
||||
&& crontab /etc/cron.d/tubo
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
# 启动 cron 和 flask
|
||||
CMD service cron start && python app.py
|
||||
@@ -0,0 +1,65 @@
|
||||
# 途播播放器专用直播源
|
||||
|
||||
每天自动更新央视、卫视、地方台等频道的直播源,生成标准 M3U 格式文件,可直接被途播播放器订阅使用。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- 自动采集央视、卫视、地方台直播源
|
||||
- 每天定时自动更新(cron)
|
||||
- 生成标准 M3U 格式文件
|
||||
- 通过 HTTP 服务对外提供访问
|
||||
- 支持 Docker 部署
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
tubo-live-source/
|
||||
├── app.py # Flask Web 服务,对外提供 m3u 文件
|
||||
├── updater.py # 直播源采集与更新脚本
|
||||
├── sources.py # 频道源配置
|
||||
├── requirements.txt # Python 依赖
|
||||
├── Dockerfile # Docker 构建文件
|
||||
├── crontab # 定时任务配置
|
||||
├── output/
|
||||
│ └── tubo.m3u # 生成的 M3U 文件(自动生成)
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 本地运行
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
python updater.py # 生成一次 m3u
|
||||
python app.py # 启动 web 服务
|
||||
```
|
||||
|
||||
访问:http://localhost:8000/tubo.m3u
|
||||
|
||||
### Docker 运行
|
||||
|
||||
```bash
|
||||
docker build -t tubo-live-source .
|
||||
docker run -d -p 8000:8000 --name tubo tubo-live-source
|
||||
```
|
||||
|
||||
### 定时更新
|
||||
|
||||
容器内已配置 cron,每天凌晨 3 点自动更新。
|
||||
|
||||
## 途播播放器订阅地址
|
||||
|
||||
```
|
||||
http://<你的服务器IP>:8000/tubo.m3u
|
||||
```
|
||||
|
||||
在途播播放器中添加上述地址即可自动同步频道。
|
||||
|
||||
## 自定义频道
|
||||
|
||||
编辑 `sources.py` 中的 `CHANNELS` 列表即可增删频道。
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
@@ -0,0 +1,49 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Flask Web 服务
|
||||
对外提供 m3u 文件访问,并支持手动触发更新
|
||||
"""
|
||||
|
||||
import os
|
||||
from flask import Flask, send_file, jsonify
|
||||
from updater import update, OUTPUT_FILE
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
"""首页,返回简单说明"""
|
||||
return jsonify({
|
||||
"name": "tubo-live-source",
|
||||
"description": "途播播放器专用直播源",
|
||||
"m3u": "/tubo.m3u",
|
||||
"update": "/update",
|
||||
})
|
||||
|
||||
|
||||
@app.route("/tubo.m3u")
|
||||
def m3u():
|
||||
"""返回 m3u 文件;若不存在则先更新一次"""
|
||||
if not os.path.exists(OUTPUT_FILE):
|
||||
update()
|
||||
return send_file(
|
||||
OUTPUT_FILE,
|
||||
mimetype="audio/x-mpegurl",
|
||||
as_attachment=False,
|
||||
download_name="tubo.m3u",
|
||||
)
|
||||
|
||||
|
||||
@app.route("/update")
|
||||
def manual_update():
|
||||
"""手动触发更新"""
|
||||
path = update()
|
||||
return jsonify({"status": "ok", "file": path})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 启动前确保 m3u 存在
|
||||
if not os.path.exists(OUTPUT_FILE):
|
||||
update()
|
||||
app.run(host="0.0.0.0", port=8000)
|
||||
@@ -0,0 +1,2 @@
|
||||
# 每天凌晨 3 点自动更新直播源
|
||||
0 3 * * * cd /app && /usr/local/bin/python /app/updater.py >> /var/log/updater.log 2>&1
|
||||
@@ -0,0 +1,2 @@
|
||||
Flask==3.0.3
|
||||
requests==2.32.3
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
频道源配置
|
||||
每个频道包含:名称、分组、URL
|
||||
URL 支持直接 m3u8 地址或第三方订阅源
|
||||
"""
|
||||
|
||||
# 央视频道
|
||||
CCTV = [
|
||||
{"name": "CCTV-1 综合", "group": "央视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225710/1.m3u8"},
|
||||
{"name": "CCTV-2 财经", "group": "央视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225711/1.m3u8"},
|
||||
{"name": "CCTV-3 综艺", "group": "央视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225712/1.m3u8"},
|
||||
{"name": "CCTV-4 中文国际", "group": "央视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225713/1.m3u8"},
|
||||
{"name": "CCTV-5 体育", "group": "央视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225714/1.m3u8"},
|
||||
{"name": "CCTV-6 电影", "group": "央视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225715/1.m3u8"},
|
||||
{"name": "CCTV-7 国防军事", "group": "央视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225716/1.m3u8"},
|
||||
{"name": "CCTV-8 电视剧", "group": "央视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225717/1.m3u8"},
|
||||
{"name": "CCTV-9 纪录", "group": "央视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225718/1.m3u8"},
|
||||
{"name": "CCTV-10 科教", "group": "央视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225719/1.m3u8"},
|
||||
{"name": "CCTV-11 戏曲", "group": "央视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225720/1.m3u8"},
|
||||
{"name": "CCTV-12 社会与法", "group": "央视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225721/1.m3u8"},
|
||||
{"name": "CCTV-13 新闻", "group": "央视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225722/1.m3u8"},
|
||||
{"name": "CCTV-14 少儿", "group": "央视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225723/1.m3u8"},
|
||||
{"name": "CCTV-15 音乐", "group": "央视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225724/1.m3u8"},
|
||||
{"name": "CCTV-17 农业农村", "group": "央视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225725/1.m3u8"},
|
||||
]
|
||||
|
||||
# 卫视频道
|
||||
SATELLITE = [
|
||||
{"name": "湖南卫视", "group": "卫视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225730/1.m3u8"},
|
||||
{"name": "浙江卫视", "group": "卫视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225731/1.m3u8"},
|
||||
{"name": "江苏卫视", "group": "卫视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225732/1.m3u8"},
|
||||
{"name": "东方卫视", "group": "卫视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225733/1.m3u8"},
|
||||
{"name": "北京卫视", "group": "卫视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225734/1.m3u8"},
|
||||
{"name": "安徽卫视", "group": "卫视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225735/1.m3u8"},
|
||||
{"name": "山东卫视", "group": "卫视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225736/1.m3u8"},
|
||||
{"name": "广东卫视", "group": "卫视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225737/1.m3u8"},
|
||||
{"name": "深圳卫视", "group": "卫视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225738/1.m3u8"},
|
||||
{"name": "天津卫视", "group": "卫视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225739/1.m3u8"},
|
||||
{"name": "湖北卫视", "group": "卫视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225740/1.m3u8"},
|
||||
{"name": "四川卫视", "group": "卫视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225741/1.m3u8"},
|
||||
{"name": "河南卫视", "group": "卫视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225742/1.m3u8"},
|
||||
{"name": "黑龙江卫视", "group": "卫视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225743/1.m3u8"},
|
||||
{"name": "辽宁卫视", "group": "卫视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225744/1.m3u8"},
|
||||
{"name": "江西卫视", "group": "卫视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225745/1.m3u8"},
|
||||
{"name": "河北卫视", "group": "卫视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225746/1.m3u8"},
|
||||
{"name": "重庆卫视", "group": "卫视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225747/1.m3u8"},
|
||||
{"name": "贵州卫视", "group": "卫视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225748/1.m3u8"},
|
||||
{"name": "云南卫视", "group": "卫视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225749/1.m3u8"},
|
||||
{"name": "陕西卫视", "group": "卫视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225750/1.m3u8"},
|
||||
{"name": "广西卫视", "group": "卫视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225751/1.m3u8"},
|
||||
{"name": "吉林卫视", "group": "卫视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225752/1.m3u8"},
|
||||
{"name": "山西卫视", "group": "卫视", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225753/1.m3u8"},
|
||||
]
|
||||
|
||||
# 地方台
|
||||
LOCAL = [
|
||||
{"name": "北京新闻", "group": "地方", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225760/1.m3u8"},
|
||||
{"name": "上海新闻综合", "group": "地方", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225761/1.m3u8"},
|
||||
{"name": "广州综合", "group": "地方", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225762/1.m3u8"},
|
||||
{"name": "深圳都市", "group": "地方", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225763/1.m3u8"},
|
||||
{"name": "杭州综合", "group": "地方", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225764/1.m3u8"},
|
||||
{"name": "南京新闻", "group": "地方", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225765/1.m3u8"},
|
||||
{"name": "成都新闻", "group": "地方", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225766/1.m3u8"},
|
||||
{"name": "武汉新闻", "group": "地方", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225767/1.m3u8"},
|
||||
{"name": "西安新闻", "group": "地方", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225768/1.m3u8"},
|
||||
{"name": "重庆新闻", "group": "地方", "url": "http://111.40.196.9:8001/PLTV/88888888/224/3221225769/1.m3u8"},
|
||||
]
|
||||
|
||||
# 全部频道
|
||||
CHANNELS = CCTV + SATELLITE + LOCAL
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
直播源采集与更新脚本
|
||||
生成标准 M3U 格式文件到 output/tubo.m3u
|
||||
"""
|
||||
|
||||
import os
|
||||
import time
|
||||
import requests
|
||||
from datetime import datetime
|
||||
from sources import CHANNELS
|
||||
|
||||
OUTPUT_DIR = "output"
|
||||
OUTPUT_FILE = os.path.join(OUTPUT_DIR, "tubo.m3u")
|
||||
|
||||
# 请求头,模拟常见播放器
|
||||
HEADERS = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
|
||||
"(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
||||
"Accept": "*/*",
|
||||
}
|
||||
|
||||
|
||||
def check_url(url, timeout=5):
|
||||
"""检测直播源是否可用"""
|
||||
try:
|
||||
resp = requests.get(url, headers=HEADERS, timeout=timeout, stream=True)
|
||||
return resp.status_code == 200
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def build_m3u(channels):
|
||||
"""根据频道列表构建 m3u 内容"""
|
||||
lines = ["#EXTM3U"]
|
||||
lines.append(f"# 途播播放器直播源 - 更新时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
lines.append(f"# 频道总数: {len(channels)}")
|
||||
lines.append("")
|
||||
|
||||
for ch in channels:
|
||||
name = ch["name"]
|
||||
group = ch.get("group", "其他")
|
||||
url = ch["url"]
|
||||
# tvg-name / group-title 便于播放器分组
|
||||
lines.append(f'#EXTINF:-1 tvg-name="{name}" group-title="{group}",{name}')
|
||||
lines.append(url)
|
||||
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def update():
|
||||
"""执行一次更新"""
|
||||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||||
|
||||
print(f"[{datetime.now()}] 开始更新直播源...")
|
||||
valid_channels = []
|
||||
for ch in CHANNELS:
|
||||
ok = check_url(ch["url"])
|
||||
status = "✓" if ok else "✗"
|
||||
print(f" {status} {ch['name']}")
|
||||
if ok:
|
||||
valid_channels.append(ch)
|
||||
time.sleep(0.1) # 避免请求过频
|
||||
|
||||
# 如果全部失效,则保留全部频道(避免空文件)
|
||||
if not valid_channels:
|
||||
print(" 警告:所有源检测失败,保留全部频道")
|
||||
valid_channels = CHANNELS
|
||||
|
||||
content = build_m3u(valid_channels)
|
||||
with open(OUTPUT_FILE, "w", encoding="utf-8") as f:
|
||||
f.write(content)
|
||||
|
||||
print(f"[{datetime.now()}] 更新完成,共 {len(valid_channels)} 个频道 -> {OUTPUT_FILE}")
|
||||
return OUTPUT_FILE
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
update()
|
||||
Reference in New Issue
Block a user