Alyssa Smith revised this gist . Go to revision
1 file changed, 75 insertions
nbl_dl.py(file created)
| @@ -0,0 +1,75 @@ | |||
| 1 | + | #!/usr/bin/env python3 | |
| 2 | + | from requests import get, post | |
| 3 | + | from subprocess import Popen | |
| 4 | + | from os import remove | |
| 5 | + | from datetime import datetime | |
| 6 | + | ||
| 7 | + | class Category: | |
| 8 | + | HIGHLIGHTS = 71361 | |
| 9 | + | CONDENSED = 71377 | |
| 10 | + | FULL = 71385 | |
| 11 | + | ||
| 12 | + | def get_vods(category = None, filter_name = None, filter_date_after = None): | |
| 13 | + | if not category: | |
| 14 | + | category = Category.HIGHLIGHTS | |
| 15 | + | data = get("https://apicdn.nbl.com.au/nbl/custom/api/sportradar", params={ | |
| 16 | + | "route": "content", | |
| 17 | + | "status_id": "2,3", | |
| 18 | + | "cat3_id": "11191", | |
| 19 | + | "limit": "200", | |
| 20 | + | "page": "1", | |
| 21 | + | "content_type_id": category, | |
| 22 | + | "sort_direction": "desc", | |
| 23 | + | "content_source_id": "81387", | |
| 24 | + | "filter[tenant]": "nbl" | |
| 25 | + | }).json()["data"] | |
| 26 | + | matches = data | |
| 27 | + | if filter_name: | |
| 28 | + | matches = [match for match in matches if filter_name.lower() in match["editorial"]["translations"]["en"]["title"].lower()] | |
| 29 | + | if filter_date_after: | |
| 30 | + | matches = [match for match in matches if datetime.strptime(filter_date_after, "%Y-%m-%d") < datetime.strptime(match["startTime"].split("T",1)[0], "%Y-%m-%d")] | |
| 31 | + | return matches | |
| 32 | + | ||
| 33 | + | def get_vod(vod_id, resolution=None): | |
| 34 | + | stream_url = post(f"https://ott.nbl.com.au/api/v2/content/{vod_id}/access/hls", headers={ | |
| 35 | + | "Referer": f"https://ott.nbl.com.au/en-int/embed/{vod_id}", | |
| 36 | + | "Origin": "https://ott.nbl.com.au", | |
| 37 | + | "Content-Length": "0" | |
| 38 | + | }).json()["data"]["stream"] | |
| 39 | + | if not resolution: | |
| 40 | + | return stream_url | |
| 41 | + | m3u8 = get(stream_url).text.split("\n") | |
| 42 | + | for n,line in enumerate(m3u8): | |
| 43 | + | if line[0] == "#" and f",RESOLUTION={resolution}" in line: | |
| 44 | + | return m3u8[n+1] | |
| 45 | + | raise Exception("No stream urls matching resolution found") | |
| 46 | + | ||
| 47 | + | def main(): | |
| 48 | + | from argparse import ArgumentParser | |
| 49 | + | parser = ArgumentParser() | |
| 50 | + | parser.add_argument("--name", default="perth wildcats") | |
| 51 | + | parser.add_argument("--category", default="highlights") | |
| 52 | + | parser.add_argument("--resolution", default="1280x720") | |
| 53 | + | parser.add_argument("--date-after", default="2021-07-01") | |
| 54 | + | args = parser.parse_args() | |
| 55 | + | category = getattr(Category, args.category.upper()) if args.category else None | |
| 56 | + | matches = get_vods(category=category, filter_name=args.name, filter_date_after=args.date_after) | |
| 57 | + | for match in matches: | |
| 58 | + | fn = match['startTime'] + " - " + match['editorial']['translations']['en']['title'] + (f" ({args.resolution.split('x')[1]}p)" if args.resolution else "") + f".{match['id']}.mp4" | |
| 59 | + | print(fn) | |
| 60 | + | try: | |
| 61 | + | # skip existing files | |
| 62 | + | with open(fn) as f: | |
| 63 | + | continue | |
| 64 | + | except FileNotFoundError: | |
| 65 | + | pass | |
| 66 | + | proc = Popen(["ffmpeg", "-v", "quiet", "-stats", "-n", "-i", get_vod(match['id'], resolution=args.resolution), "-c", "copy", "file:" + fn]) | |
| 67 | + | try: | |
| 68 | + | proc.communicate() | |
| 69 | + | finally: | |
| 70 | + | # delete incomplete files | |
| 71 | + | if proc.returncode != 0: | |
| 72 | + | remove(fn) | |
| 73 | + | ||
| 74 | + | if __name__ == "__main__": | |
| 75 | + | main() | |
Newer
Older