#!/usr/bin/env python3 import sys import json import argparse import re import requests __author__ = "Meliurwen" __version__ = "0.1.1" __license__ = "GPL3" def gen_link_m3u(chan_dict, source, channel): headers = { "User-Agent": ("Mozilla/5.0 (Windows NT 10.0; rv:75.0) " "Gecko/20100101 Firefox/75.0"), "Accept-Language": "en-US;q=0.9,en;q=0.8,en-GB;q=0.7", "X-Requested-With": "XMLHttpRequest", "Connection": "keep-alive", "Referer": str(chan_dict[source][channel]["data"]["referer"]), "cb-enabled": "enabled", "DNT": "1", "Pragma": "no-cache", "Cache-Control": "no-cache" } content = requests.get( chan_dict[source][channel]["url"], headers=headers, timeout=(3, 300) ) content = { "status_code": content.status_code, "content": content.content, "url": content.url } if content["status_code"] != 200: print( "[ERROR] Http status code {stat_code} " "for {source}:{channel} at {url}".format( stat_code=content["status_code"], source=source, channel=channel, url=content["url"]), file=sys.stderr ) return None ugly_payload = json.loads(content["content"].decode('unicode_escape')[1:-1]) return ugly_payload["data"]["attributes"]["streaming"]["hls"]["url"] def is_supp_source(chan_dict, source): if source not in chan_dict.keys(): print( "[ERROR] Source not supported: {source}".format(source=source), file=sys.stderr ) return False return True def is_supp_channel(chan_dict, source, channel): if channel not in chan_dict[source].keys(): print( "[ERROR] Channel not supported: {channel}".format(channel=channel), file=sys.stderr ) return False return True def gen_list(source, chan_dict): is_success = False if source: if not is_supp_source(chan_dict, source): return False for channel in chan_dict[source].keys(): print("{source}:{channel}".format(source=source, channel=channel)) is_success = True else: for src in chan_dict.keys(): for channel in chan_dict[src].keys(): print("{source}:{channel}".format(source=src, channel=channel)) is_success = True return is_success def gen_playlist(chan_dict): is_success = False re_list = re.compile('^([a-z0-9-_]{1,32}):([a-z0-9-_]{1,32})\n?$', re.IGNORECASE) is_first_line = True for line in sys.stdin: stream = re_list.findall(line) if stream: if is_first_line: print("#EXTM3U") is_first_line = False source, channel = stream[0][0], stream[0][1] if is_supp_source(chan_dict, source) and is_supp_channel(chan_dict, source, channel): link = gen_link_m3u(chan_dict, source, channel) if link: print("#EXTINF:-1" "group-title=\"{source}\",{chanName}".format( source=source, chanName=chan_dict[source][channel]["name"]) ) print(link) is_success = True if not is_success: print("[ERROR] No playlist created because empty", file=sys.stderr) return False return True def main(arguments): source = arguments.source[0] if args.source else None channel = arguments.channel[0] if args.channel else None is_list = arguments.list if args.list else False is_playlist = arguments.playlist if args.playlist else False with open('channels.json', 'r') as myfile: chan_dict = json.loads(myfile.read()) if source and channel: if not (is_supp_source(chan_dict, source) or is_supp_channel(chan_dict, source, channel)): sys.exit(1) link = gen_link_m3u(chan_dict, source, channel) if link: print( link, sep='', end='\n', file=sys.stdout, flush=False ) else: sys.exit(1) if is_list: if not gen_list(source, chan_dict): sys.exit(1) if is_playlist: if not gen_playlist(chan_dict): sys.exit(1) if __name__ == "__main__": parser = argparse.ArgumentParser( prog='IPTV Network Extract', usage='%(prog)s [-h] [-s SOURCE [-c CHANNEL | -l]] [-l] [-p] [--version]', description='Extracts original m3u8 links from multiple IPTV networks.' ) parser.add_argument( "-s", "--source", nargs=1, required=False, help="source where getting the channel" ) parser.add_argument( "-c", "--channel", nargs=1, required=False, help="channel name" ) parser.add_argument( "-l", "--list", required=False, action='store_true', help="list supported networks and channels" ) parser.add_argument( "-p", "--playlist", required=False, action='store_true', help="generate a m3u8 playlist from stdin" ) parser.add_argument( "--version", action="version", version="%(prog)s (version {version})".format(version=__version__) ) args = parser.parse_args() if len(sys.argv) <= 1: parser.error("at least 1 argument is required") if len(sys.argv) > 2 and args.playlist: parser.error("--playlist must be a single parameter") if len(sys.argv) == 3 and args.channel: parser.error("--source is required for this parameter") if args.source and not bool(args.channel) ^ bool(args.list): parser.error("--source requires either --channel or --list") if args.list and args.channel: parser.error("--list should either be used singularly or with --source") main(args)