用于处理 IPTV 的 M3U 播放列表的库(AKA m3u_plus)
项目描述
IPyTV
用于解析 M3U Plus 格式的 IPTV 播放列表的 python3 库。
M3U Plus 和 IPTV
M3U Plus 格式是在 Internet 上分发 IPTV 播放列表的事实标准。
IPTV 播放列表和M3U Plus 播放列表这两个术语通常可以互换使用,但在此存储库中,M3U Plus指的是数据格式,而IPTV 播放列表指的是 M3U Plus 格式的播放列表。
M3U Plus 源于extended M3U8
格式,它只支持 2 个标签(#EXTM3U和#EXTINF)。
#EXTM3U和标签的语法#EXTINF已被修改以包含额外的属性(例如,徽标、组、语言)。不幸的是,这破坏了与原始 M3U8 标准的向后兼容性(如这里详细解释的)。
该库是从头开始创建的,仅用于解析和处理 M3U Plus 格式。它不完全支持常规 M3U8 播放列表(仅解析基本频道属性)。
支持的标签
仅支持#EXTM3U,#EXTINF和纯 url 行(即它们被解析并且它们的值作为IPTVChannel对象的属性可用)。
#EXTINF在行及其相关的 url 行之间找到的所有标签都作为extras通道添加,但不执行任何解析(即,它们被视为纯字符串)。
在下面的示例中,该#EXTVLCOPT行没有被解析,而是按原样复制:
#EXTINF:-1 tvg-id="" tvg-name="hello" tvg-country="IT" tvg-url="" group-title="Greetings",Hello!
#EXTVLCOPT:http-user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0
https://my-website.com/hello
安装
这个库需要 Python 3(和相关的pip安装程序)。
请注意:该库使用了在使用IDLE环境multiprocessing.Pool时需要小心的类
。
要在系统范围内安装库,请运行:
pip install m3u-ipytv
要在虚拟环境中安装它,请运行:
python -m venv .venv
源.venv/bin/activate
pip install m3u-ipytv
用法
模块
该库包括几个模块,每个模块都有一个特定的能力领域:
- 渠道
- 与播放列表中的频道处理相关的所有内容。
- 医生
- 修复 M3U 文件中常见错误的函数集合。
- 例外
- 库抛出的所有异常。
- m3u
- 与 M3U 文件相关的常量和函数。
- 播放列表
- 与加载和处理 M3U 播放列表相关的所有内容。
加载 IPTV 播放列表
从一个文件
使用playlist.loadf(file)功能:
from ipytv import playlist
file = "~/Documents/my_playlist.m3u"
pl = playlist.loadf(file)
print(pl.length())
从一个 URL
使用playlist.loadu(url)功能:
from ipytv import playlist
url = "https://iptv-org.github.io/iptv/categories/classic.m3u"
pl = playlist.loadu(url)
print(pl.length())
从一个字符串
使用playlist.loads(string)功能:
from ipytv import playlist
string = """#EXTM3U
#EXTINF:-1 tvg-id="Rai 1" tvg-name="Rai 1" group-title="RAI",Rai 1
http://myown.link:80/luke/210274/78482"""
pl = playlist.loads(string)
print(pl.length())
从一个数组(即一个列表)
使用playlist.loada(array)功能:
from ipytv import playlist
array = [
'#EXTM3U',
'#EXTINF:-1 tvg-id="Rai 1" tvg-name="Rai 1" group-title="RAI",Rai 1',
'http://myown.link:80/luke/210274/78482'
]
pl = playlist.loada(array)
print(pl.length())
M3UPlaylist 类
上面的每个加载函数都返回一个M3UPlaylist类的对象。
此类模拟播放列表(基本上是频道列表)的概念,并提供与播放列表本身及其频道进行交互的方法。
播放列表中有两个主要属性,它们是:
- 属性
- 频道
这些属性是什么以及如何访问它们将在接下来的段落中描述。
访问播放列表的属性
行中指定的键值对#EXTM3U被视为播放列表范围的属性(即它们适用于播放列表本身或播放列表中的每个频道)。
例如x-tvg-url下面的部分:
#EXTM3U x-tvg-url="http://myown.link:80/luke/220311/22311"
这些属性,以字典的形式,可以通过以下
get_attributes()方法访问:
from ipytv import playlist
url = "https://iptv-org.github.io/iptv/categories/kids.m3u"
pl = playlist.loadu(url)
attributes = pl.get_attributes()
for k, v in attributes.items():
print(f'"{k}": "{v}"')
或者,当事先知道属性的名称时,可以通过以下方式检索其值:
from ipytv import playlist
url = "https://iptv-org.github.io/iptv/categories/kids.m3u"
pl = playlist.loadu(url)
attributes = pl.get_attributes()
tvg_url = pl.get_attribute("x-tvg-url")
print(f"x-tvg-url: {tvg_url}")
也可以使用以下方法添加、修改和删除属性:
from ipytv.playlist import M3UPlaylist
pl = M3UPlaylist()
attribute_name = 'tvg-shift'
# Add the 'tvg-shift' attribute and set it to 1
pl.add_attribute(attribute_name, "1")
# Update the 'tvg-shift' attribute to -2
pl.update_attribute(attribute_name, "-2")
# Completely remove the 'tvg-shift' attribute
value_before_deletion = pl.remove_attribute(attribute_name)
还有一种方法允许以字典的形式一次添加多个属性(而不是单个属性):
pl.add_attributes({})
访问播放列表的频道
该类M3UPlaylist基本上是具有一些商品功能的频道列表。可以使用以下方法之一访问播放列表中的频道。
个别
通过使用get_channel(index)方法:
from ipytv import playlist
url = "https://iptv-org.github.io/iptv/categories/classic.m3u"
pl = playlist.loadu(url)
# Let's retrieve the first channel in the list
channel = pl.get_channel(0)
print(f'channel \"{channel.name}\": {channel.url}')
# The next line will throw IndexOutOfBoundsException
channel = pl.get_channel(-1)
迭代地
通过循环M3UPlaylist对象中的通道:
from ipytv import playlist
url = "https://iptv-org.github.io/iptv/categories/classic.m3u"
pl = playlist.loadu(url)
for channel in pl:
print(f'channel \"{channel.name}\": {channel.url}')
低级
在所有前两种访问方法都不够用的情况下,可以通过以下get_channels()方法访问内部频道列表:
from ipytv import playlist
url = "https://iptv-org.github.io/iptv/categories/classic.m3u"
pl = playlist.loadu(url)
chan_list = pl.get_channels()
ten_channels = chan_list[:10]
也可以使用以下方法添加、修改和删除通道:
from ipytv.playlist import M3UPlaylist
from ipytv.channel import IPTVChannel
pl = M3UPlaylist()
channel = IPTVChannel()
# Add a channel to the end of the list (last index)
pl.append_channel(channel)
# Insert a channel in the specified position (all succeeding channels are
# shifted right by 1 position)
pl.insert_channel(0, channel)
new_channel = IPTVChannel()
# Replace the second channel of the playlist with a new channel
pl.update_channel(1, new_channel)
# Remove the channel at the specified position (all succeeding channels are
# shifted left by 1 position)
old_channel = pl.remove_channel(0)
还有两种方法可以添加频道列表(而不是单个频道):
pl.append_channels([])
pl.insert_channels([])
访问通道的属性
M3UPlaylist 对象的get_channels()方法返回一个对象列表
IPTVChannel。
一个IPTVChannel对象有 3 个基本属性(url和name)
duration和两个可选字段:(attributes字典)和extras
(列表)。
例如:
from ipytv.channel import IPTVAttr, IPTVChannel
channel = IPTVChannel(
url="http://myown.link:80/luke/210274/78482",
name="Rai 1",
duration="-1",
attributes={
IPTVAttr.TVG_ID.value: "Rai 1",
IPTVAttr.TVG_NAME.value: "Rai 1",
IPTVAttr.TVG_LOGO.value: "https://static.epg.best/it/RaiUno.it.png",
IPTVAttr.GROUP_TITLE.value: "RAI"
},
extras=['#EXTVLCOPT:http-user-agent=Lavf53.32.100']
)
print(channel.name)
print(channel.attributes[IPTVAttr.GROUP_TITLE.value])
print(channel.extras[0])
IPTVAttr枚举类包含 IPTV 播放列表中常见的属性名称。
doctor模块_
来自 Internet 的 IPTV 播放列表通常包含许多格式错误。这个模块想要解决一些常见的错误。
该模块包含三个类,每个类都有自己的范围:
M3UDoctor- 它包含修复 m3u 文件中的错误的方法(即无法将 m3u 文件加载为播放列表的错误)。
IPTVChannelDoctor
- 它包含修复通道中的错误(即#EXTINF 行的属性中的错误)的方法。
M3UPlaylistDoctor
- 它将修复应用于
IPTVChannelDoctor播放列表中的所有频道。
上述所有类都提供了一种名为的公共静态方法sanitize(),该方法负责应用所有不同的修复程序。它可以按如下方式使用:
from ipytv.doctor import M3UDoctor, M3UPlaylistDoctor
from ipytv import playlist
with open('my-broken-playlist.m3u', encoding='utf-8') as in_file:
content = in_file.readlines()
fixed_content = M3UDoctor.sanitize(content)
pl = playlist.loada(fixed_content)
fixed_pl = M3UPlaylistDoctor.sanitize(pl)
with open('my-fixed-playlist.m3u', 'w', encoding='utf-8') as out_file:
content = fixed_pl.to_m3u_plus_playlist()
out_file.write(content)
日志记录
IPyTV 支持 python 的标准日志系统。
要启用 IPyTV 的日志记录,请将日志记录配置添加到您的应用程序:
import logging
from ipytv import playlist
logging.basicConfig(level=logging.INFO)
pl = playlist.loadu("https://iptv-org.github.io/iptv/categories/classic.m3u")
格式注意事项
#EXTM3UM3U Plus 格式引入的和标签扩展#EXTINF破坏了与 M3U8 格式的兼容性。
这是标准#EXTINF行的样子:
#EXTINF:-1,Rai 1
格式非常简单:
#EXTINF:<duration>,[<title>]
让我们分解一下:
#EXTINF:标签_- 内容的持续时间(整数或浮点数,有符号或无符号)
- 逗号字符
- 标题
这是#EXTINFM3U Plus 格式中的一行的样子:
#EXTINF:-1 tvg-id="Rai 1" tvg-name="Rai 1" tvg-logo="https://static.epg.best/it/RaiUno.it.png" group-title="RAI",Rai 1
如果我们将其分解,我们会看到第 3 点和第 4 点已被添加(并且它们破坏了之前对#EXTINF标记的定义):
#EXTINF:标签_- 内容的持续时间(整数或浮点数,有符号或无符号)
- 空间
- 可变长度、空格分隔的属性列表
- 逗号字符
- 标题
第 4 点中的属性采用attribute="value"格式,其中value
也可能包含非转义逗号(这确实使解析逻辑复杂化)。
值得注意的是,M3U8 RFC 文档指定了 属性列表的格式,但 M3U Plus 的实现不符合标准。
总而言之,M3U Plus 格式及其怪癖和特质很难被人类阅读,也很难被计算机解析。这是一种丑陋的格式,但它太普遍而无法被忽略,并且 Python 缺乏解析库。
有趣的是,VLC 程序员是这样命名 M3U Plus 格式的 IPTV 播放列表的解析函数的:
static void parseEXTINFIptvDiots(...)
只是说... :sweat_smile:
执照
该项目根据 MIT 许可条款获得许可。
有关详细信息,请参阅LICENSE.txt。