"""
CmafHam helper functions.
:copyright: (c) 2024 Shayne Reese.
:license: MIT, see LICENSE for more details.
"""
import os
import re
import io
from uuid import uuid4
from typing import Union, List
import requests
[docs]
def gen_uuid() -> str:
""" Generate a random uuid string.
:returns: version 4(random) uuid string.
:rtype: str
"""
return str(uuid4())
[docs]
def get_manifest_string(manifest_uri: str) -> str:
""" Load a manifest as a string from a file or url.
:param str manifest_uri: web url or uri path of manifest.
:returns: string of the manifest or an empty string.
:rtype: str
"""
if "http" in manifest_uri:
res = requests.get(manifest_uri, timeout=10)
if res and res.text:
return res.text
if os.path.exists(manifest_uri):
with open(manifest_uri, "r", encoding="utf-8") as file:
return file.read()
return ""
[docs]
def load_file(file_uri: str) -> Union[io.BufferedReader, io.BytesIO, None]:
""" Download or open a file-like object.
:param str file_uri : str - url or path to file.
:returns: file like object or nothing if unsucessful.
:rtype: io.BufferedReader or io.BytesIO or None
"""
if "http" in file_uri:
head = requests.head(file_uri, timeout=20)
if head and hasattr(head, "headers"):
size = head.headers.get("Content-Length")
if size and int(size) < 536870912:
# limit to < 500 MB for now..
res = requests.get(file_uri, timeout=20)
if res and res.content:
return io.BytesIO(res.content)
if os.path.exists(file_uri):
return open(file_uri, "rb")
return None
[docs]
def float_fr(framerate: Union[float, int, str]) -> float:
""" Parse various frame rate representations and return as float value.
:param framerate: video frame rate.
:type framerate: float or int or str
:returns: float value of framerate if parsed, else 0.0.
:rtype: float
"""
if isinstance(framerate, float):
# ex. 30.0, 29.97
return framerate
if isinstance(framerate, int):
# ex. 25, 30
return float(framerate)
if isinstance(framerate, str):
if "/" in framerate:
# ex. "30000/1001"
res = re.search(r'([0-9]+)/([0-9]+)', framerate)
if res and hasattr(res, "group"):
val = int(res.group(1)) / int(res.group(2))
return float(val)
else:
# ex. "25", "59.94"
res = re.search(r'[0-9]+', framerate)
if res and hasattr(res, "group"):
return float(res.group(0))
return 0.0
[docs]
def parse_codec(codec_string: str) -> List[tuple]:
""" Return the codec(s) and their type from a string.
TODO: Implement the 'codec_strings' parser module eventually for more meaningful data.
:param str codec_string: mime type codec string.
:returns: list of present codecs and their type.
:rtype: list[tuple]
"""
parsed: list = []
if isinstance(codec_string, str):
video_codecs = ("avc", "hvc", "hevc", "AVC", "HVC", "HEVC")
audio_codecs = ("mp4a", "MP4A", "m4a", "M4A")
text_codecs = ("wvtt", "vtt", "WEBVTT", "WVTT", "VTT", "WebVTT")
for codec in codec_string.split(","):
if any(v in codec for v in video_codecs):
parsed.append(("video", codec))
elif any(a in codec for a in audio_codecs):
parsed.append(("audio", codec))
elif any(t in codec for t in text_codecs):
parsed.append(("text", codec))
return parsed
[docs]
def remove_ext(filename: str) -> str:
""" Remove the extension of a filename.
:param str filename: full uri of file.
:returns: tail of path(filename) without extension.
:rtype: str
"""
res = os.path.splitext(filename)
return res[0]
[docs]
def get_path(file_path: str = "") -> str:
""" Obtain the base path by removing the filename,
otherwise, return the current dir.
:param str file_path: uri of the file
:returns: base path to parent file, or cwd
:rtype: str
"""
path = os.getcwd()
if file_path:
res = os.path.split(file_path)
if res and res[0]:
path = res[0]
return path