#!/usr/bin/env python3 import argparse import gzip import html.parser import logging import re import subprocess import urllib.request ## The URL of a dndbeyond character which won't be deleted DND_BEYOND_CHARACTER_URL = "https://www.dndbeyond.com/characters/147022047" ## Regex to find the matching JS file MAIN_JS_RE = re.compile(r"https://media\.dndbeyond\.com\/character-app.*main.*\.js") ## Path to sourcemapper bin SOURCEMAPPER_BIN = "/home/prometheus/go/bin/sourcemapper" def main(): parser = argparse.ArgumentParser( description="Utility for scanning and converting videos." ) parser.add_argument( "-u", "--character-url", default=DND_BEYOND_CHARACTER_URL, help="URL of the dndbeyond character to grab the source from.", ) parser.add_argument( "-v", "--verbose", action="store_true", help="Enable verbose logging" ) parser.add_argument( "-q", "--quiet", action="store_true", help="Only display errors" ) args = parser.parse_args() if args.verbose: logging.basicConfig(level=logging.DEBUG) elif args.quiet: logging.basicConfig(level=logging.ERROR) else: logging.basicConfig(level=logging.INFO) char_data = download_character() parser = MainJSExtractor() parser.feed(char_data.decode("utf-8")) print(f"Found URL: {parser.js_url}") subprocess.check_call( [ SOURCEMAPPER_BIN, "-output", "ddb_main", "-jsurl", parser.js_url, ] ) def download_character() -> bytes: try: req = urllib.request.Request( DND_BEYOND_CHARACTER_URL, headers={ "user-agent": "Mozilla/5.0", "authority": "www.dndbeyond.com", "cache-control": "max-age=0", "upgrade-insecure-requests": "1", "sec-fetch-user": "?1", "accept": "text/json", "sec-fetch-site": "none", "sec-fetch-mode": "navigate", "accept-encoding": "gzip, deflate, br", "accept-language": "en-US,en;q=0.9", }, ) with urllib.request.urlopen(req) as resp: if resp.info().get("Content-Encoding") == "gzip": return gzip.decompress(resp.read()) else: return resp.read() except Exception: logging.exception(f"Failed to load the character") class MainJSExtractor(html.parser.HTMLParser): def __init__(self): super().__init__() self.js_url = None def handle_starttag(self, tag, attrs): if tag == "script": for attr_name, attr_val in attrs: if attr_name == "src": m = MAIN_JS_RE.match(attr_val) if m is not None: self.js_url = attr_val if __name__ == "__main__": main()