nbl_dl.py
· 2.9 KiB · Python
Raw
#!/usr/bin/env python3
from requests import get, post
from subprocess import Popen
from os import remove
from datetime import datetime
class Category:
HIGHLIGHTS = 71361
CONDENSED = 71377
FULL = 71385
def get_vods(category = None, filter_name = None, filter_date_after = None):
if not category:
category = Category.HIGHLIGHTS
data = get("https://apicdn.nbl.com.au/nbl/custom/api/sportradar", params={
"route": "content",
"status_id": "2,3",
"cat3_id": "11191",
"limit": "200",
"page": "1",
"content_type_id": category,
"sort_direction": "desc",
"content_source_id": "81387",
"filter[tenant]": "nbl"
}).json()["data"]
matches = data
if filter_name:
matches = [match for match in matches if filter_name.lower() in match["editorial"]["translations"]["en"]["title"].lower()]
if filter_date_after:
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")]
return matches
def get_vod(vod_id, resolution=None):
stream_url = post(f"https://ott.nbl.com.au/api/v2/content/{vod_id}/access/hls", headers={
"Referer": f"https://ott.nbl.com.au/en-int/embed/{vod_id}",
"Origin": "https://ott.nbl.com.au",
"Content-Length": "0"
}).json()["data"]["stream"]
if not resolution:
return stream_url
m3u8 = get(stream_url).text.split("\n")
for n,line in enumerate(m3u8):
if line[0] == "#" and f",RESOLUTION={resolution}" in line:
return m3u8[n+1]
raise Exception("No stream urls matching resolution found")
def main():
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument("--name", default="perth wildcats")
parser.add_argument("--category", default="highlights")
parser.add_argument("--resolution", default="1280x720")
parser.add_argument("--date-after", default="2021-07-01")
args = parser.parse_args()
category = getattr(Category, args.category.upper()) if args.category else None
matches = get_vods(category=category, filter_name=args.name, filter_date_after=args.date_after)
for match in matches:
fn = match['startTime'] + " - " + match['editorial']['translations']['en']['title'] + (f" ({args.resolution.split('x')[1]}p)" if args.resolution else "") + f".{match['id']}.mp4"
print(fn)
try:
# skip existing files
with open(fn) as f:
continue
except FileNotFoundError:
pass
proc = Popen(["ffmpeg", "-v", "quiet", "-stats", "-n", "-i", get_vod(match['id'], resolution=args.resolution), "-c", "copy", "file:" + fn])
try:
proc.communicate()
finally:
# delete incomplete files
if proc.returncode != 0:
remove(fn)
if __name__ == "__main__":
main()
| 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() |