Source code for ayt_api.utils
import pathlib
import warnings
from typing import Optional, Any
from urllib import parse
[docs]
def id_str_to_int(youtube_id: str) -> int:
"""Converts a base 64 YouTube ID string into an integer.
Args:
youtube_id (str): The YouTube ID as a base 64 string.
Returns:
int: The YouTube ID as an integer.
Raises:
ValueError: There were invalid characters in the YouTube ID.
"""
number = 0
last_chars = ["-", "_"]
for idx, char in enumerate(reversed(youtube_id)):
ord_var = ord(char)
if 65 <= ord_var <= 90:
number += (ord_var - 65) * 64 ** idx
elif 97 <= ord_var <= 122:
number += (ord_var - 71) * 64 ** idx
elif 48 <= ord_var <= 57:
number += (ord_var + 4) * 64 ** idx
elif char in last_chars:
number += (62 + last_chars.index(char)) * 64 ** idx
else:
raise ValueError(f"Invalid YouTube ID character: {char}")
return number
[docs]
def camel_to_snake(string: str) -> str:
"""Converts words in the camel case convention to the snake case convention.
e.g. Converts ``fooBar`` to ``foo_bar``.
Args:
string (str): The words in the camel case convention.
Returns:
str: The words in the snake case convention.
"""
snake_string = ""
for char in string:
if char.isupper():
snake_string += "_" + char.lower()
else:
snake_string += char
return snake_string
[docs]
def snake_to_camel(string: str) -> str:
"""Converts words in the snake case convention to the camel case convention.
e.g. Converts ``foo_bar`` to ``fooBar``.
Args:
string (str): The words in the snake case convention.
Returns:
str: The words in the camel case convention.
"""
camel_string = ""
capitalise = False
for char in string:
if char == "_":
capitalise = True
elif capitalise:
camel_string += char.upper()
capitalise = False
else:
camel_string += char
return camel_string[0].lower() + camel_string[1:]
[docs]
def snake_keys(dictionary: dict) -> dict:
"""Converts keys in a dictionary from camel case to snake case.
Args:
dictionary (dict): The dictionary with keys using the camel case convention.
Returns:
dict: The dictionary with keys using the snake case convention.
"""
snake_dict = {}
for key, value in dictionary.items():
snake_dict[camel_to_snake(key)] = value
return snake_dict
[docs]
def censor_key(call_url: str) -> str:
"""Censors the api key in an api call url.
Args:
call_url (str): The api call url containing the uncensored api key.
Returns:
str: The url with the api key censored.
"""
components = parse.urlparse(call_url)
queries = parse.parse_qs(components.query)
if "key" in queries:
queries["key"] = ["API_KEY"]
censored_components = components._replace(query=parse.urlencode(queries, doseq=True))
return censored_components.geturl()
[docs]
def censor_token(call_url: str) -> str:
"""Alias of censor_key
.. deprecated:: 0.4.0
Use :func:`censor_key` instead
Args:
call_url (str): The api call url containing the uncensored api key.
Returns:
str: The url with the api key censored.
"""
warnings.warn(
"censor_token is deprecated since 0.4.0 and is scheduled "
"for removal in a later release. Use censor_key instead.",
DeprecationWarning
)
return censor_key(call_url)
[docs]
def basic_html_page(title: str, description: str) -> str:
"""
Builds a basic html page
.. versionadded:: 0.4.0
This is used in :func:`ayt_api.api.AsyncYoutubeAPI.with_oauth_flow_generator`
Args:
title (str): The title and heading for the page
description (str): The description that will be displayed on the page
Returns:
str: The html page
"""
return f"""\
<!doctype html>
<html lang="en">
<head>
<title>{title}</title>
<link rel="icon" href="https://ayt-api.revnoplex.xyz/ayt-api-square.svg">
<link rel="stylesheet" type="text/css" href="https://revnoplex.xyz/css/main.css">
</head>
<body>
<h1>{title}</h1>
<p>{description}.</p>
</body>
</html>\
"""
[docs]
def use_existing(existing_value: Any, argument: Any) -> Any:
"""
A check used in the updated functions to decide when to use the existing value if the argument has a value of
``EXISTING`` or use the value of the argument.
.. versionadded:: 0.4.0
Args:
existing_value (Any): The existing value that will be used if ``argument`` is ``EXISTING``.
argument (Any): The value to overwrite ``existing_value`` if not ``EXISTING``.
Returns:
Any: The existing value or argument.
"""
from ayt_api.types import EXISTING
return existing_value if argument is EXISTING else argument
[docs]
def ensure_missing_keys(original: dict, minimised: dict) -> dict:
"""
Ensure a dictionary with possible missing keys from the first dictionary includes them if the value for the key
was ``None`` or an empty value.
.. versionadded:: 0.4.0
Note:
This util will only check the first layer of keys and will not check any deeper nested keys.
Args:
original (dict): The original dictionary with the full set of keys.
minimised (dict): The version of the dictionary that had keys with empty values removed.
Returns:
dict: The ``minimised`` version of the dictionary with values added back from the original depending on if they
were empty values.
"""
updated = minimised.copy()
for key, value in original.items():
if key not in minimised and (not value):
updated[key] = value
return updated