mirror of
https://github.com/abc1763613206/myiptv.git
synced 2025-12-17 15:24:59 +08:00
Initial Commit
This commit is contained in:
24
README.md
24
README.md
@@ -1 +1,25 @@
|
|||||||
# myiptv
|
# myiptv
|
||||||
|
自用直播源集合,附带检测与分类功能。
|
||||||
|
|
||||||
|
### 为啥搞
|
||||||
|
TLDR: 太闲了。
|
||||||
|
|
||||||
|
- 自己有收集直播源的爱好,和录制直播源的需求。
|
||||||
|
- 一些软件自带的直播源太过难用。
|
||||||
|
- 网上现有的直播源太杂,且缺乏检测。
|
||||||
|
- 一些大源缺乏持续更新,如 `iptv-org`。
|
||||||
|
|
||||||
|
### 使用指南与 TODO
|
||||||
|
|
||||||
|
- 由于目前主要还是自用,故打算每次手动生成。
|
||||||
|
- `merged` 为全部直播源,`groups` 文件夹里为各个直播源的分类。
|
||||||
|
- 带 `simple` 的为纯净格式**(不带来源与分辨率)**
|
||||||
|
- 目前只输出了 `txt` 格式,急着用的可以自行用 [Telelist](https://guihet.com/tvlive-telelist.html) 进行转换,其他格式慢慢写。
|
||||||
|
- 关于分类: `difang` 分类为地方频道与 IPTV 特色频道,`cctv` 为 CCTV 下属频道(含部分特色频道与 CGTN),`weishi` 为地方卫视(含凤凰卫视)
|
||||||
|
- 关于来源与命名:频道命名尽量遵循[ EPG 频道列表](http://epg.51zmt.top:8000/) 中的格式进行命名统一以确保正确匹配;来源命名多使用 IPIP 进行 IP 段标注,`difang` 分类为目测(小地方台基本上只有官方供源,要是挨个查 IP 的话会出事)
|
||||||
|
- 现阶段可以下载直播源上传至 [ EPG 频道列表](http://epg.51zmt.top:8000/)自动匹配节目表。
|
||||||
|
- 脚本所做的工作:通过 `requests` 和 `ffprobe` 检测直播源,后进行分类,最后用有效信息输出新的 `csv`。
|
||||||
|
- 比较重要的一点:我主要使用家机(当前是北方移动)进行检测,直播源可用性对地区依赖较大,有必要时请自行跑脚本检测。
|
||||||
|
- 脚本会去除仅音频/视频的直播源,所以该源显然不含音频广播/无声直播。
|
||||||
|
- 部分直播源限制连接数,如有需要请自行设置延时。
|
||||||
|
- TODO:添加新格式、新直播源,优化脚本。
|
||||||
|
|||||||
0
groups/.gitkeep
Normal file
0
groups/.gitkeep
Normal file
103
main.py
Normal file
103
main.py
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import csv
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import traceback
|
||||||
|
import requests
|
||||||
|
import subprocess
|
||||||
|
from ffmpy import FFprobe
|
||||||
|
from subprocess import PIPE
|
||||||
|
from sys import stdout
|
||||||
|
from termcolor import colored, RESET
|
||||||
|
from datetime import datetime
|
||||||
|
dt=datetime.now()
|
||||||
|
# Channel Group Source Link
|
||||||
|
|
||||||
|
SKIP_FFPROBE_MESSAGES = [re.compile(pattern) for pattern in (
|
||||||
|
'Last message repeated',
|
||||||
|
'mmco: unref short failure',
|
||||||
|
'number of reference frames .+ exceeds max',
|
||||||
|
)]
|
||||||
|
|
||||||
|
def check_channel(clist,num):
|
||||||
|
# clist 为一行 csv
|
||||||
|
uri = clist[3]
|
||||||
|
try:
|
||||||
|
r = requests.get(clist[3], timeout=2) # 先测能不能正常访问
|
||||||
|
if(r.status_code == requests.codes.ok):
|
||||||
|
#ffprobe = FFprobe(inputs={uri: '-v warning'})
|
||||||
|
#errors = tuple(filter(
|
||||||
|
# lambda line: not (line in ('', RESET) or any(regex.search(line) for regex in SKIP_FFPROBE_MESSAGES)),
|
||||||
|
# ffprobe.run(stderr=PIPE)[1].decode('utf-8').split('\n')
|
||||||
|
#))
|
||||||
|
#if errors: # https://github.com/Jamim/iptv-checker/blob/master/iptv-checker.py#L26
|
||||||
|
# print('[{}] {}({}) Error:{}'.format(str(num), clist[0], clist[2], str(errors)))
|
||||||
|
# return False
|
||||||
|
#else: # 查视频信息
|
||||||
|
ffprobe = FFprobe(inputs={uri: '-v error -show_format -show_streams -print_format json'})
|
||||||
|
cdata = json.loads(ffprobe.run(stdout=PIPE,stderr=PIPE)[0].decode('utf-8'))
|
||||||
|
flagAudio = 0
|
||||||
|
flagVideo = 0
|
||||||
|
vwidth = 0
|
||||||
|
vheight = 0
|
||||||
|
for i in cdata['streams']:
|
||||||
|
if i['codec_type'] == 'video':
|
||||||
|
flagVideo = 1
|
||||||
|
if vwidth <= i['coded_width']: # 取最高分辨率
|
||||||
|
vwidth = i['coded_width']
|
||||||
|
vheight = i['coded_height']
|
||||||
|
elif i['codec_type'] == 'audio':
|
||||||
|
flagAudio = 1
|
||||||
|
if flagAudio == 0:
|
||||||
|
print('[{}] {}({}) Error: Video Only!'.format(str(num), clist[0], clist[2]))
|
||||||
|
return False
|
||||||
|
if flagVideo == 0:
|
||||||
|
print('[{}] {}({}) Error: Audio Only!'.format(str(num), clist[0], clist[2]))
|
||||||
|
return False
|
||||||
|
print('[{}] {}({}) PASS: {}*{}'.format(str(num), clist[0], clist[2], vwidth, vheight))
|
||||||
|
return [vwidth,vheight]
|
||||||
|
else:
|
||||||
|
print('[{}] {}({}) Error:{}'.format(str(num), clist[0], clist[2], str(r.status_code)))
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
#traceback.print_exc()
|
||||||
|
print('[{}] {}({}) Error:{}'.format(str(num), clist[0], clist[2], str(e)))
|
||||||
|
return False
|
||||||
|
|
||||||
|
def print_info():
|
||||||
|
print('Time: {}-{}-{} {}:{}'.format(dt.year,dt.month,dt.day,dt.hour,dt.minute))
|
||||||
|
#subprocess.run(['ffprobe'])
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print_info()
|
||||||
|
Total = 0
|
||||||
|
fulltimes = '-{}{}{}{}{}'.format(dt.year,dt.month,dt.day,dt.hour,dt.minute) # 时间后缀
|
||||||
|
# times = fulltimes # 有时间后缀
|
||||||
|
times = '' # 无时间后缀
|
||||||
|
with open('data.csv') as f:
|
||||||
|
f_csv = csv.reader(f)
|
||||||
|
headers = next(f_csv)
|
||||||
|
num = 1
|
||||||
|
with open('data{}.csv'.format(fulltimes), 'a+') as f0: # 写入检测后新data
|
||||||
|
# print('Channel,Group,Source,Link', file=f0)
|
||||||
|
for row in f_csv:
|
||||||
|
ret = check_channel(row,num)
|
||||||
|
if(ret): # 通过,写入
|
||||||
|
with open('groups/{}{}.txt'.format(row[1],times), 'a+') as f1:
|
||||||
|
print('{}({}-{}*{}),{}'.format(row[0],row[2],ret[0],ret[1],row[3]), file=f1)
|
||||||
|
with open('groups/{}-simple{}.txt'.format(row[1],times), 'a+') as f1:
|
||||||
|
print('{},{}'.format(row[0],row[3]), file=f1)
|
||||||
|
with open('merged{}.txt'.format(times),'a+') as f1:
|
||||||
|
print('{}({}-{}*{}),{}'.format(row[0],row[2],ret[0],ret[1],row[3]), file=f1)
|
||||||
|
with open('merged-simple{}.txt'.format(times),'a+') as f1:
|
||||||
|
print('{},{}'.format(row[0],row[3]), file=f1)
|
||||||
|
print('{},{},{},{}'.format(row[0],row[1],row[2],row[3]), file=f0)
|
||||||
|
Total = Total + 1
|
||||||
|
num = num + 1
|
||||||
|
print('Total: {}'.format(Total))
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user