Package: proxytv
proxytvRobot forhttps://proxytv.ru/(ProxyBot)pip installproxytvpython -m proxytv --helpusage: proxytv [-h] [-f] [-t] [-q QUERY] [-cd COOLDOWN] [-x PROXY] [-chf CHANNEL_FILTERS] [-pl [PL ...]] [-o OUTPUT]

options:
  -h, --help            show this help message and exit
  -f, --forever         Run the script again every cooldown seconds.
  -t, --threading       Send and parse the request for each playlist in a separate thread. (Default: True)
  -q QUERY, --query QUERY
                        Send only one request with the specified text. For example: "ch: HD" or "pl: aist"
  -cd COOLDOWN, --cooldown COOLDOWN
                        Cooldown in seconds. (Default: 1)
  -x PROXY, --proxy PROXY
                        Proxy <protocol>://<ip>
  -chf CHANNEL_FILTERS, --channel-filters CHANNEL_FILTERS
                        Filters for received channels
  -pl [PL ...]          Playlist name(s)
  -o OUTPUT, --output OUTPUT
                        Output .m3u(8) file. (Default: <stdout>)Если запустить без доп. аргументов, будут получены все каналы, всех плейлистов (plist)
И записаны в файл output, по умолчание выведет результат в формате m3u в консоль.Аругмент -pl: список плейлистов по которым будет происходить поиск, по умолчанию все доступные.Кодfrom.extinfimport*from.searchimport*from.staticimport*classRobot:"""A class describing a parser robot. Designed for inheritance."""__slots__=('end_extinf','plist','PLIST_LEN','search_engine','pl_number','output')sort_key=None# sort channelsdef__init__(self,forever:bool=True,cooldown:SupportsFloat=0.,search:Srch=None,output=None,except_types=(Exception,)):"""Runs the order of actions, if forever is true then it does it forever."""...defpost_init(self):"""Execute after initialization."""passdefrun(self,except_types:tuple[BaseException]=None,*,cooldown:int|float=None,forever:bool=None):"""Run self.loop()Skip exceptions from `except_types`repeat if `forever` every `cooldown` seconds"""ifexcept_typesisNone:except_types=self.except_typesifcooldownisNone:cooldown=self.cooldownifforeverisNone:forever=self.forever...defloop(self):self.on_start()self.during()self.on_end()defon_start(self):"""Initial actions."""self.plist=self.search_engine.plist()self.PLIST_LEN=len(self.plist)self.end_extinf=Extinf()self.pl_number=1defsearch_pl(self,pl_name:str):pl=self.search_engine.pl(pl_name)print(f'Pl:{pl_name}({self.pl_number}/{self.PLIST_LEN}) Channels:{len(pl)}')self.end_extinf+=plself.pl_number+=1defduring(self):"""Actions in the middle of the process."""forpl_nameinself.plist:self.search_pl(pl_name)defon_end(self):"""Sort by self.sort_key function and save"""self.end_extinf.data.sort(key=self.sort_key)ch_count=len(self.end_extinf)print(f"Saving{ch_count}channel{''ifch_count==1else's'}")self.upload(save_extinf(self.end_extinf,self.output))defupload(self,fn:str):"""Used to upload files to a remote host, used in on_end."""passclassRobotThreading(Robot):"""An example of implementing your own robot, with multithreading"""__slots__=('end_extinf','pl_number')defduring(self):"""Creates threads to search for each playlist."""withThreadPool()aspool:pool.map(self.search_pl,self.plist)Классы для наследования, при написании более сложных роботов.ФильтрыАргумент--channel-filtersпринимает файл с фильтрами в формате регулярного выражения,
применяемого к выходному m3u форматуСуществуют некоторое упрощения для более быстрого составления файла фильтров.
Напомню, выходной m3u формат выглядит примерно так:#EXTINF:-1 tvch-id="40025" group-title="ДЕТСКИЕ",DISNEY CHANNEL-40025
http://93.158.224.2:4022/udp/239.3.100.85:4321Каждая строка в файле фильтров представляет собой фильтр в особом формате.Чтобы канал прошёл фильтрацию, необходимо чтобы как минимум один фильтр к нему подошёл.\в начале строки всегда обрезаетсяЧтобы фильтровать IP адреса (вторую строку), фильтр должен начинаться со знака:Чтобы проверить на присутствие подстроки в строке, фильтр должен начинаться с#Или любое регулярное выражение (при использовании#регулярное выражение не применяется, строка
экранируется), если строка начинается с:, регулярное выражение по-прежнему проверяется, но уже на IPВсе филтры не чувствительны к регистру (re.IGNORECASE)Было:\:-1Стало::-1:.+:4022.+Найти все IP с портом 1234http://93.158.224.2:4022/udp/239.3.100.85:4321:#:4321:.*:4321.*http://93.158.224.2:4022/udp/239.3.100.85:4321#group-title="ДЕТСКИЕ".*group-title="ДЕТСКИЕ".*Найти все каналы в группе детских#EXTINF:-1 tvch-id="40025"group-title="ДЕТСКИЕ",DISNEY CHANNEL-40025#HD.*HD.*Тоже что и -q "ch:HD" аргумет#EXTINF:-1 tvch-id="46046" group-title="ИНФ/РАЗВЛЕКАТЕЛЬНЫЕ",РОССИЯ 1HD-46046Пример составления нужного плейлиста:filters.txt#NICKELODEON HD
#CARTOON NETWORK
#Рен ТВ
#ТНТ HD
#Пятница
#Россия 24python -m proxytv -chf filters.txt -f -cd 120 -o tv.m3u8Каждые 2 минуты будет обновляться файл tv.m3u8 с перечисленными выше каналами.Вопросы задавайте на GitHub, на почту или загляните в исходный код, там всё прозрачно.