twitch-dl/twitchdl/utils.py

110 lines
3.0 KiB
Python
Raw Normal View History

import re
import unicodedata
2024-03-30 14:36:53 +00:00
import click
2024-03-26 09:23:50 +00:00
def _format_size(value: float, digits: int, unit: str):
if digits > 0:
2024-03-28 11:06:50 +00:00
return f"{{:.{digits}f}}{unit}".format(value)
else:
2024-03-28 11:06:50 +00:00
return f"{int(value)}{unit}"
def format_size(bytes_: int | float, digits: int = 1):
2020-04-11 11:08:42 +00:00
if bytes_ < 1024:
return _format_size(bytes_, digits, "B")
2020-04-11 11:08:42 +00:00
kilo = bytes_ / 1024
if kilo < 1024:
return _format_size(kilo, digits, "kB")
2020-04-11 11:08:42 +00:00
mega = kilo / 1024
if mega < 1024:
return _format_size(mega, digits, "MB")
2020-04-11 11:08:42 +00:00
return _format_size(mega / 1024, digits, "GB")
2020-04-11 11:08:42 +00:00
2024-03-26 09:23:50 +00:00
def format_duration(total_seconds: int | float) -> str:
2020-04-11 11:08:42 +00:00
total_seconds = int(total_seconds)
hours = total_seconds // 3600
remainder = total_seconds % 3600
minutes = remainder // 60
seconds = total_seconds % 60
if hours:
2024-03-28 11:06:50 +00:00
return f"{hours} h {minutes} min"
2020-04-11 11:08:42 +00:00
if minutes:
2024-03-28 11:06:50 +00:00
return f"{minutes} min {seconds} sec"
2020-04-11 11:08:42 +00:00
2024-03-28 11:06:50 +00:00
return f"{seconds} sec"
2020-04-11 11:08:42 +00:00
2024-03-26 09:23:50 +00:00
def format_time(total_seconds: int | float, force_hours: bool = False) -> str:
total_seconds = int(total_seconds)
hours = total_seconds // 3600
remainder = total_seconds % 3600
minutes = remainder // 60
seconds = total_seconds % 60
2022-11-20 08:42:09 +00:00
if hours or force_hours:
return f"{hours:02}:{minutes:02}:{seconds:02}"
return f"{minutes:02}:{seconds:02}"
2024-03-26 09:23:50 +00:00
def read_int(msg: str, min: int, max: int, default: int | None = None) -> int:
2020-04-11 11:08:42 +00:00
while True:
try:
2024-03-30 14:36:53 +00:00
val = click.prompt(msg, default=default, type=int)
if default and not val:
2020-04-11 11:08:42 +00:00
return default
if min <= int(val) <= max:
return int(val)
except ValueError:
pass
2024-03-26 09:23:50 +00:00
def slugify(value: str) -> str:
2022-01-23 08:14:40 +00:00
value = unicodedata.normalize('NFKC', str(value))
value = re.sub(r'[^\w\s_-]', '', value)
value = re.sub(r'[\s_-]+', '_', value)
return value.strip("_").lower()
2024-03-26 09:23:50 +00:00
def titlify(value: str) -> str:
2022-01-23 08:14:40 +00:00
value = unicodedata.normalize('NFKC', str(value))
value = re.sub(r'[^\w\s\[\]().-]', '', value)
value = re.sub(r'\s+', ' ', value)
return value.strip()
2021-01-14 20:38:56 +00:00
VIDEO_PATTERNS = [
r"^(?P<id>\d+)?$",
r"^https://(www.)?twitch.tv/videos/(?P<id>\d+)(\?.+)?$",
]
CLIP_PATTERNS = [
r"^(?P<slug>[A-Za-z0-9]+(?:-[A-Za-z0-9_-]{16})?)$",
r"^https://(www.)?twitch.tv/\w+/clip/(?P<slug>[A-Za-z0-9]+(?:-[A-Za-z0-9_-]{16})?)(\?.+)?$",
r"^https://clips.twitch.tv/(?P<slug>[A-Za-z0-9]+(?:-[A-Za-z0-9_-]{16})?)(\?.+)?$",
2021-01-14 20:38:56 +00:00
]
2024-03-26 09:23:50 +00:00
def parse_video_identifier(identifier: str) -> str | None:
2021-01-14 20:38:56 +00:00
"""Given a video ID or URL returns the video ID, or null if not matched"""
for pattern in VIDEO_PATTERNS:
match = re.match(pattern, identifier)
if match:
return match.group("id")
2024-03-26 09:23:50 +00:00
def parse_clip_identifier(identifier: str) -> str | None:
2021-01-14 20:38:56 +00:00
"""Given a clip slug or URL returns the clip slug, or null if not matched"""
for pattern in CLIP_PATTERNS:
match = re.match(pattern, identifier)
if match:
return match.group("slug")