"""Stream Logic.""" import base64 import hashlib import time from video import config from video.constants.stream import STREAMABLE_PREVIEW_BUCKET, STREAMABLE_PREVIEW_PATH from video.exceptions import JobNotFound, ProductVideoNotFound from video.models.sql.classes import product_video def get_hls_streaming_url( product_id: int, referrer: str | None = None, user_id: str | None = None ) -> str: """Get a HLS URL for a product. Args: product_id (int): ID of the product. referrer (str): The request's referrer. user_id (str): The orchard user ID. """ product = product_video.get(product_id) if not product: raise ProductVideoNotFound() job_id = product.get("latest_pipeline_run_id") if not job_id: raise JobNotFound() key_path = STREAMABLE_PREVIEW_PATH.format(job_id=job_id, product_id=product_id) wowza_url = generate_wowza_url( STREAMABLE_PREVIEW_BUCKET, key_path, referrer, user_id ) return wowza_url def generate_wowza_url( bucket: str, key_path: str, referrer: str | None, user_id: str | None ) -> str: """Generate a signed and time-limited Wowza URL. Args: bucket (str): Bucket where the file is, e.g. qa-orcd-video-assets key_path (str): Key path to the file, e.g. mezzanine/h.264/1/video.mp4 referrer (str): The request's referrer. user_id (str): The orchard user ID. Returns: str: The Wowza URL. """ if referrer and "workstation74" in referrer: path_prefix = "{}qaworkstation74".format(config.WOWZA_PATH_PREFIX) elif referrer and "workstation-cf" in referrer: path_prefix = "{}workstationcf".format(config.WOWZA_PATH_PREFIX) elif referrer and "workstation.awal.com" in referrer: path_prefix = "{}workstationawal".format(config.WOWZA_PATH_PREFIX) elif user_id and user_id.startswith("oa:"): path_prefix = "{}oa".format(config.WOWZA_PATH_PREFIX) else: path_prefix = config.WOWZA_PATH_PREFIX url_prefix = "{}/{}/{}".format(path_prefix, "_definst_", "amazons3/mp4:") full_path = "{}{}/{}".format(url_prefix, bucket, key_path) start_time = int(time.time()) end_time = start_time + config.WOWZA_TOKEN_TTL_IN_SECONDS start_time = start_time - config.WOWZA_GRACE_PERIOD secure_access_token = get_wowza_secure_access_token( url_prefix, bucket, key_path, start_time, end_time, config.WOWZA_SHARED_SECRET ) full_url = ( "{host}/{path}/playlist.m3u8?wowzatokenendtime={end}&wowzatokenstarttime={start}&wowzatokenhash={hash}" ).format( host=config.WOWZA_URL, path=full_path, end=end_time, start=start_time, hash=secure_access_token, ) return full_url def get_wowza_secure_access_token( url_prefix: str, bucket: str, key_path: str, start_time: int, end_time: int, shared_secret: str, ) -> str: """Return a Wowza secure access token. Args: url_prefix (str): URL prefix, e.g. vod/_definst_/mp4: bucket (str): Bucket where the file is, e.g. qa-orcd-video-assets key_path (str): Key path to the file, e.g. mezzanine/h.264/1/video.mp4 start_time (int): Unix timestamp at which token should become valid end_time (int): Unix timestamp at which token should become invalid shared_secret (str): The Wowza shared secret Returns: str: Secure token value to put in querystring. """ encoding = "utf-8" sha256 = hashlib.sha256() sha256.update(url_prefix.encode(encoding)) sha256.update("{}/".format(bucket).encode(encoding)) sha256.update(key_path.encode(encoding)) sha256.update(("?{}".format(shared_secret)).encode(encoding)) sha256.update("&wowzatokenendtime={}".format(end_time).encode(encoding)) sha256.update("&wowzatokenstarttime={}".format(start_time).encode(encoding)) digest = sha256.digest() result = base64.urlsafe_b64encode(digest).decode(encoding) return result