diff --git a/README.md b/README.md index e958613..5a566ae 100644 --- a/README.md +++ b/README.md @@ -22,14 +22,26 @@ Extracts original m3u8 links from multiple IPTV networks. ## Use -To extract the link for a _channel_ and put it in `stdout`: +To list the supported _sources_: ```sh -./iptv_network_extract.py -c channelName +./iptv_network_extract.py -l ``` -You can also pipe the link to a player like `mpv`: +To list the supported _channels_ of a _source_: ```sh -./iptv_network_extract.py -c channelName | mpv --playlist=- +./iptv_network_extract.py -s SOURCE -l +``` + +To extract the _link_ for a _channel_: + +```sh +./iptv_network_extract.py -s SOURCE -c CHANNEL +``` + +You can also pipe the link directly to a player like `mpv`: + +```sh +./iptv_network_extract.py -s SOURCE -c CHANNEL | mpv --playlist=- ``` diff --git a/channels.json b/channels.json index 5dd89f5..a827e9d 100644 --- a/channels.json +++ b/channels.json @@ -1,68 +1,70 @@ { - "realtime":{ - "url":"https://it.dplay.com/ajax/playbackjson/channel/2", - "data":{ - "referer":"https://it.dplay.com/realtime/" - } - }, - "nove":{ - "url":"https://it.dplay.com/ajax/playbackjson/channel/3", - "data":{ - "referer":"https://it.dplay.com/nove/" - } - }, - "dmax":{ - "url":"https://it.dplay.com/ajax/playbackjson/channel/5", - "data":{ - "referer":"https://it.dplay.com/dmax/" - } - }, - "giallo":{ - "url":"https://it.dplay.com/ajax/playbackjson/channel/6", - "data":{ - "referer":"https://it.dplay.com/giallo/" - } - }, - "k-2":{ - "url":"https://it.dplay.com/ajax/playbackjson/channel/7", - "data":{ - "referer":"https://it.dplay.com/k-2/" - } - }, - "frisbee":{ - "url":"https://it.dplay.com/ajax/playbackjson/channel/8", - "data":{ - "referer":"https://it.dplay.com/frisbee/" - } - }, - "motor-trend":{ - "url":"https://it.dplay.com/ajax/playbackjson/channel/19", - "data":{ - "referer":"https://it.dplay.com/motor-trend/" - } - }, - "food-network":{ - "url":"https://it.dplay.com/ajax/playbackjson/channel/21", - "data":{ - "referer":"https://it.dplay.com/food-network/" - } - }, - "home-and-garden-tv":{ - "url":"https://it.dplay.com/ajax/playbackjson/channel/36", - "data":{ - "referer":"https://it.dplay.com/home-and-garden-tv/" - } - }, - "eurosport1":{ - "url":"https://it.dplay.com/ajax/playbackjson/channel/49", - "data":{ - "referer":"https://it.dplay.com/eurosport1/" - } - }, - "eurosport2":{ - "url":"https://it.dplay.com/ajax/playbackjson/channel/50", - "data":{ - "referer":"https://it.dplay.com/eurosport2/" + "dplay-it": { + "realtime":{ + "url":"https://it.dplay.com/ajax/playbackjson/channel/2", + "data":{ + "referer":"https://it.dplay.com/realtime/" + } + }, + "nove":{ + "url":"https://it.dplay.com/ajax/playbackjson/channel/3", + "data":{ + "referer":"https://it.dplay.com/nove/" + } + }, + "dmax":{ + "url":"https://it.dplay.com/ajax/playbackjson/channel/5", + "data":{ + "referer":"https://it.dplay.com/dmax/" + } + }, + "giallo":{ + "url":"https://it.dplay.com/ajax/playbackjson/channel/6", + "data":{ + "referer":"https://it.dplay.com/giallo/" + } + }, + "k-2":{ + "url":"https://it.dplay.com/ajax/playbackjson/channel/7", + "data":{ + "referer":"https://it.dplay.com/k-2/" + } + }, + "frisbee":{ + "url":"https://it.dplay.com/ajax/playbackjson/channel/8", + "data":{ + "referer":"https://it.dplay.com/frisbee/" + } + }, + "motor-trend":{ + "url":"https://it.dplay.com/ajax/playbackjson/channel/19", + "data":{ + "referer":"https://it.dplay.com/motor-trend/" + } + }, + "food-network":{ + "url":"https://it.dplay.com/ajax/playbackjson/channel/21", + "data":{ + "referer":"https://it.dplay.com/food-network/" + } + }, + "home-and-garden-tv":{ + "url":"https://it.dplay.com/ajax/playbackjson/channel/36", + "data":{ + "referer":"https://it.dplay.com/home-and-garden-tv/" + } + }, + "eurosport1":{ + "url":"https://it.dplay.com/ajax/playbackjson/channel/49", + "data":{ + "referer":"https://it.dplay.com/eurosport1/" + } + }, + "eurosport2":{ + "url":"https://it.dplay.com/ajax/playbackjson/channel/50", + "data":{ + "referer":"https://it.dplay.com/eurosport2/" + } } } } diff --git a/iptv_network_extract.py b/iptv_network_extract.py index 4c57d14..71f11c8 100755 --- a/iptv_network_extract.py +++ b/iptv_network_extract.py @@ -5,76 +5,142 @@ import json import argparse import requests +__author__ = "Meliurwen" +__version__ = "0.1.1" +__license__ = "GPL3" -with open('channels.json', 'r') as myfile: - channelsDict = json.loads(myfile.read()) - -parser = argparse.ArgumentParser( - prog='IPTV Network Extract', - description='Extracts original m3u8 links from multiple IPTV networks.' -) -parser.add_argument( - "-c", "--channel", - nargs=1, - choices=list(channelsDict.keys()), - required=True, - help="TV channel name" -) -parser.add_argument( - "--version", - action="version", - version="%(prog)s 0.1" -) -args = parser.parse_args() - -channel = args.channel[0] - -HEADERS: dict = { - "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(channelsDict[channel]["data"]["referer"]), - "cb-enabled": "enabled", - "DNT": "1", - "Pragma": "no-cache", - "Cache-Control": "no-cache" -} - -content = requests.get( - channelsDict[channel]["url"], - headers=HEADERS, - timeout=(3, 300) -) - -content = { - "status_code": content.status_code, - "content": content.content, - "encoding": content.encoding, - "cookies": content.cookies, - "request": content.request, - "url": content.url, - "reason": content.reason, - "headers": content.headers, - "elapsed": content.elapsed, - "history": content.history -} - -if content["status_code"] == 200: - uglyPayload = json.loads(content["content"].decode('unicode_escape')[1:-1]) - print( - uglyPayload["data"]["attributes"]["streaming"]["hls"]["url"], - sep='', - end='', - file=sys.stdout, - flush=False - ) -else: - print( - "[ERROR] Http status code: " + str(content["status_code"]), - sep='', - end='\n', - file=sys.stderr, +def get_link_m3u(source, channel, chan_dict): + + 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: + ugly_payload = json.loads(content["content"].decode('unicode_escape')[1:-1]) + print( + ugly_payload["data"]["attributes"]["streaming"]["hls"]["url"], + sep='', + end='\n', + file=sys.stdout, + flush=False + ) + sys.exit(0) + else: + print( + "[ERROR] Http status code: {stat_code}".format(stat_code=content["status_code"]), + file=sys.stderr + ) + sys.exit(1) + +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 + ) + sys.exit(1) + +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 + ) + sys.exit(1) + +def get_list(source, chan_dict): + + if source: + is_supp_source(chan_dict, source) + print(*chan_dict[source].keys(), sep = "\n") + else: + print(*chan_dict.keys(), sep = "\n") + sys.exit(1) + +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 + + with open('channels.json', 'r') as myfile: + chan_dict = json.loads(myfile.read()) + + if source and channel: + is_supp_source(chan_dict, source) + is_supp_channel(chan_dict, source, channel) + get_link_m3u(source, channel, chan_dict) + + if is_list: + get_list(source, chan_dict) + +if __name__ == "__main__": + + parser = argparse.ArgumentParser( + prog='IPTV Network Extract', + usage='%(prog)s [-h] [[-s SOURCE -c CHANNEL] | [-s SOURCE -l]] [-l] [--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="TV channel name" + ) + parser.add_argument( + "-l", "--list", + required=False, + action='store_true', + help="List supported networks and channels" + ) + 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) == 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 CHANNEL or --list") + + if args.list and args.channel: + parser.error("--list should either be used singularly or with --source") + + main(args)