ttvdrops/twitch/utils.py
2026-03-16 18:40:04 +01:00

83 lines
2.3 KiB
Python

import re
from functools import lru_cache
from typing import TYPE_CHECKING
from urllib.parse import urlparse
from urllib.parse import urlunparse
import dateparser
from django.utils import timezone
if TYPE_CHECKING:
from datetime import datetime
from urllib.parse import ParseResult
TWITCH_BOX_ART_HOST = "static-cdn.jtvnw.net"
TWITCH_BOX_ART_PATH_PREFIX = "/ttv-boxart/"
TWITCH_BOX_ART_SIZE_PATTERN: re.Pattern[str] = re.compile(
r"-(\{width\}|\d+)x(\{height\}|\d+)(?=\.[A-Za-z0-9]+$)",
)
def is_twitch_box_art_url(url: str) -> bool:
"""Return True when the URL points at Twitch's box art CDN."""
if not url:
return False
parsed: ParseResult = urlparse(url)
return parsed.netloc == TWITCH_BOX_ART_HOST and parsed.path.startswith(
TWITCH_BOX_ART_PATH_PREFIX,
)
def normalize_twitch_box_art_url(url: str) -> str:
"""Normalize Twitch box art URLs to remove size suffixes for higher quality.
Example:
https://static-cdn.jtvnw.net/ttv-boxart/65654_IGDB-120x160.jpg
-> https://static-cdn.jtvnw.net/ttv-boxart/65654_IGDB.jpg
Args:
url: The Twitch box art URL to normalize.
Returns:
The normalized Twitch box art URL without size suffixes.
"""
if not url:
return url
parsed: ParseResult = urlparse(url)
if parsed.netloc != TWITCH_BOX_ART_HOST:
return url
if not parsed.path.startswith(TWITCH_BOX_ART_PATH_PREFIX):
return url
normalized_path: str = TWITCH_BOX_ART_SIZE_PATTERN.sub("", parsed.path)
return str(urlunparse(parsed._replace(path=normalized_path)))
@lru_cache(maxsize=40 * 40 * 1024)
def parse_date(value: str) -> datetime | None:
"""Parse a datetime string into a timezone-aware datetime using dateparser.
Args:
value: The datetime string to parse.
Returns:
A timezone-aware datetime object or None if parsing fails.
"""
dateparser_settings: dict[str, bool | int] = {
"RETURN_AS_TIMEZONE_AWARE": True,
"CACHE_SIZE_LIMIT": 0,
}
dt: datetime | None = dateparser.parse(
date_string=value,
settings=dateparser_settings, # pyright: ignore[reportArgumentType]
)
if not dt:
return None
# Ensure aware in Django's current timezone
if timezone.is_naive(dt):
dt = timezone.make_aware(dt, timezone.get_current_timezone())
return dt