Replace custom coloring fns with click.echo and style

This commit is contained in:
Ivan Habunek 2024-03-31 21:39:54 +02:00
parent 9cf3ec2f07
commit 3ae99fe159
No known key found for this signature in database
GPG Key ID: F5F0623FF5EBCB3D
7 changed files with 86 additions and 122 deletions

View File

@ -5,10 +5,12 @@ from typing import Literal
from itertools import islice from itertools import islice
from os import path from os import path
import click
from twitchdl import twitch, utils from twitchdl import twitch, utils
from twitchdl.commands.download import get_clip_authenticated_url from twitchdl.commands.download import get_clip_authenticated_url
from twitchdl.download import download_file from twitchdl.download import download_file
from twitchdl.output import print_out, print_clip, print_json from twitchdl.output import green, print_clip, print_json, yellow
ClipsPeriod = Literal["last_day", "last_week", "last_month", "all_time"] ClipsPeriod = Literal["last_day", "last_week", "last_month", "all_time"]
@ -41,7 +43,9 @@ def clips(
def _continue(): def _continue():
print_out("Press <green><b>Enter</green> to continue, <yellow><b>Ctrl+C</yellow> to break.") enter = click.style("Enter", bold=True, fg="green")
ctrl_c = click.style("Ctrl+C", bold=True, fg="yellow")
click.echo(f"Press {enter} to continue, {ctrl_c} to break.")
try: try:
input() input()
@ -76,22 +80,23 @@ def _download_clips(generator):
target = _target_filename(clip) target = _target_filename(clip)
if path.exists(target): if path.exists(target):
print_out(f"Already downloaded: <green>{target}</green>") click.echo(f"Already downloaded: {green(target)}")
else: else:
url = get_clip_authenticated_url(clip["slug"], "source") url = get_clip_authenticated_url(clip["slug"], "source")
print_out(f"Downloading: <yellow>{target}</yellow>") click.echo(f"Downloading: {yellow(target)}")
download_file(url, target) download_file(url, target)
def _print_all(generator, all: bool): def _print_all(generator, all: bool):
for clip in generator: for clip in generator:
print_out() click.echo()
print_clip(clip) print_clip(clip)
if not all: if not all:
print_out( click.secho(
"\n<dim>There may be more clips. " + "\nThere may be more clips. " +
"Increase the --limit, use --all or --pager to see the rest.</dim>" "Increase the --limit, use --all or --pager to see the rest.",
dim=True
) )
@ -103,17 +108,17 @@ def _print_paged(generator, page_size):
last = first + len(page) - 1 last = first + len(page) - 1
while True: while True:
print_out("-" * 80) click.echo("-" * 80)
print_out() click.echo()
for clip in page: for clip in page:
print_clip(clip) print_clip(clip)
print_out() click.echo()
last = first + len(page) - 1 last = first + len(page) - 1
print_out("-" * 80) click.echo("-" * 80)
print_out(f"<yellow>Clips {first}-{last}</yellow>") click.echo(f"Clips {first}-{last} of ???")
first = first + len(page) first = first + len(page)
last = first + 1 last = first + 1

View File

@ -19,7 +19,7 @@ from twitchdl.download import download_file
from twitchdl.entities import Data, DownloadOptions from twitchdl.entities import Data, DownloadOptions
from twitchdl.exceptions import ConsoleError from twitchdl.exceptions import ConsoleError
from twitchdl.http import download_all from twitchdl.http import download_all
from twitchdl.output import blue, bold, dim, green, print_log, print_out, yellow from twitchdl.output import blue, bold, dim, green, print_log, yellow
def download(ids: list[str], args: DownloadOptions): def download(ids: list[str], args: DownloadOptions):

View File

@ -1,9 +1,11 @@
import click
import m3u8 import m3u8
from twitchdl import utils, twitch from twitchdl import utils, twitch
from twitchdl.commands.download import get_video_placeholders from twitchdl.commands.download import get_video_placeholders
from twitchdl.entities import Data
from twitchdl.exceptions import ConsoleError from twitchdl.exceptions import ConsoleError
from twitchdl.output import print_table, print_video, print_clip, print_json, print_out, print_log from twitchdl.output import bold, print_table, print_video, print_clip, print_json, print_log
def info(id: str, *, json: bool = False): def info(id: str, *, json: bool = False):
video_id = utils.parse_video_identifier(id) video_id = utils.parse_video_identifier(id)
@ -46,25 +48,25 @@ def info(id: str, *, json: bool = False):
def video_info(video, playlists, chapters): def video_info(video, playlists, chapters):
print_out() click.echo()
print_video(video) print_video(video)
print_out() click.echo()
print_out("Playlists:") click.echo("Playlists:")
for p in m3u8.loads(playlists).playlists: for p in m3u8.loads(playlists).playlists:
print_out(f"<b>{p.stream_info.video}</b> {p.uri}") click.echo(f"{bold(p.stream_info.video)} {p.uri}")
if chapters: if chapters:
print_out() click.echo()
print_out("Chapters:") click.echo("Chapters:")
for chapter in chapters: for chapter in chapters:
start = utils.format_time(chapter["positionMilliseconds"] // 1000, force_hours=True) start = utils.format_time(chapter["positionMilliseconds"] // 1000, force_hours=True)
duration = utils.format_time(chapter["durationMilliseconds"] // 1000) duration = utils.format_time(chapter["durationMilliseconds"] // 1000)
print_out(f'{start} <b>{chapter["description"]}</b> ({duration})') click.echo(f'{start} {bold(chapter["description"])} ({duration})')
placeholders = get_video_placeholders(video, format = "mkv") placeholders = get_video_placeholders(video, format = "mkv")
placeholders = [[f"{{{k}}}", v] for k, v in placeholders.items()] placeholders = [[f"{{{k}}}", v] for k, v in placeholders.items()]
print_out("") click.echo("")
print_table(["Placeholder", "Value"], placeholders) print_table(["Placeholder", "Value"], placeholders)
@ -86,11 +88,11 @@ def video_json(video, playlists, chapters):
print_json(video) print_json(video)
def clip_info(clip): def clip_info(clip: Data):
print_out() click.echo()
print_clip(clip) print_clip(clip)
print_out() click.echo()
print_out("Download links:") click.echo("Download links:")
for q in clip["videoQualities"]: for q in clip["videoQualities"]:
print_out("<b>{quality}p{frameRate}</b> {sourceURL}".format(**q)) click.echo(f"{bold(q['quality'])} [{q['frameRate']} fps] {q['sourceURL']}")

View File

@ -1,8 +1,10 @@
import sys import sys
import click
from twitchdl import twitch from twitchdl import twitch
from twitchdl.exceptions import ConsoleError from twitchdl.exceptions import ConsoleError
from twitchdl.output import print_log, print_out, print_paged_videos, print_video, print_json, print_video_compact from twitchdl.output import print_log, print_paged_videos, print_video, print_json, print_video_compact
def videos( def videos(
@ -38,7 +40,7 @@ def videos(
return return
if total_count == 0: if total_count == 0:
print_out("<yellow>No videos found</yellow>") click.echo("No videos found")
return return
if pager: if pager:
@ -50,18 +52,18 @@ def videos(
if compact: if compact:
print_video_compact(video) print_video_compact(video)
else: else:
print_out() click.echo()
print_video(video) print_video(video)
count += 1 count += 1
print_out() click.echo()
print_out("-" * 80) click.echo("-" * 80)
print_out(f"<yellow>Videos 1-{count} of {total_count}</yellow>") click.echo(f"Videos 1-{count} of {total_count}")
if total_count > count: if total_count > count:
print_out() click.secho(
print_out( "\nThere are more videos. Increase the --limit, use --all or --pager to see the rest.",
"<dim>There are more videos. Increase the --limit, use --all or --pager to see the rest.</dim>" dim=True
) )

View File

@ -1,51 +1,11 @@
import click import click
import json import json
import re
import sys
from itertools import islice from itertools import islice
from twitchdl import utils from twitchdl import utils
from typing import Any, Match from typing import Any, Generator
from twitchdl.entities import Data
START_CODES = {
'b': '\033[1m',
'dim': '\033[2m',
'i': '\033[3m',
'u': '\033[4m',
'red': '\033[91m',
'green': '\033[92m',
'yellow': '\033[93m',
'blue': '\033[94m',
'magenta': '\033[95m',
'cyan': '\033[96m',
}
END_CODE = '\033[0m'
START_PATTERN = "<(" + "|".join(START_CODES.keys()) + ")>"
END_PATTERN = "</(" + "|".join(START_CODES.keys()) + ")>"
USE_ANSI_COLOR = "--no-color" not in sys.argv
def start_code(match: Match[str]) -> str:
name = match.group(1)
return START_CODES[name]
def colorize(text: str) -> str:
text = re.sub(START_PATTERN, start_code, text)
text = re.sub(END_PATTERN, END_CODE, text)
return text
def strip_tags(text: str) -> str:
text = re.sub(START_PATTERN, '', text)
text = re.sub(END_PATTERN, '', text)
return text
def truncate(string: str, length: int) -> str: def truncate(string: str, length: int) -> str:
@ -55,11 +15,6 @@ def truncate(string: str, length: int) -> str:
return string return string
def print_out(*args, **kwargs):
args = [colorize(a) if USE_ANSI_COLOR else strip_tags(a) for a in args]
print(*args, **kwargs)
def print_json(data: Any): def print_json(data: Any):
click.echo(json.dumps(data)) click.echo(json.dumps(data))
@ -87,38 +42,38 @@ def print_table(headers: list[str], data: list[list[str]]):
print_row(row) print_row(row)
def print_video(video): def print_video(video: Data):
published_at = video["publishedAt"].replace("T", " @ ").replace("Z", "") published_at = video["publishedAt"].replace("T", " @ ").replace("Z", "")
length = utils.format_duration(video["lengthSeconds"]) length = utils.format_duration(video["lengthSeconds"])
channel = f"<blue>{video['creator']['displayName']}</blue>" if video["creator"] else "" channel = blue(video['creator']['displayName']) if video["creator"] else ""
playing = f"playing <blue>{video['game']['name']}</blue>" if video["game"] else "" playing = f"playing {blue(video['game']['name'])}" if video["game"] else ""
# Can't find URL in video object, strange # Can't find URL in video object, strange
url = f"https://www.twitch.tv/videos/{video['id']}" url = f"https://www.twitch.tv/videos/{video['id']}"
print_out(f"<b>Video {video['id']}</b>") click.secho(f"Video {video['id']}", bold=True)
print_out(f"<green>{video['title']}</green>") click.secho(f"{video['title']}", fg="green")
if channel or playing: if channel or playing:
print_out(" ".join([channel, playing])) click.echo(" ".join([channel, playing]))
if video["description"]: if video["description"]:
print_out(f"Description: {video['description']}") click.echo(f"Description: {video['description']}")
print_out(f"Published <blue>{published_at}</blue> Length: <blue>{length}</blue> ") click.echo(f"Published {blue(published_at)} Length: {blue(length)} ")
print_out(f"<i>{url}</i>") click.secho(url, italic=True)
def print_video_compact(video): def print_video_compact(video: Data):
id = video["id"] id = video["id"]
date = video["publishedAt"][:10] date = video["publishedAt"][:10]
game = video["game"]["name"] if video["game"] else "" game = video["game"]["name"] if video["game"] else ""
title = truncate(video["title"], 80).ljust(80) title = truncate(video["title"], 80).ljust(80)
print_out(f'<b>{id}</b> {date} <green>{title}</green> <blue>{game}</blue>') click.echo(f"{bold(id)} {date} {green(title)} {blue(game)}")
def print_paged_videos(generator, page_size: int, total_count: int): def print_paged_videos(generator: Generator[Data, None, None], page_size: int, total_count: int):
iterator = iter(generator) iterator = iter(generator)
page = list(islice(iterator, page_size)) page = list(islice(iterator, page_size))
@ -126,17 +81,17 @@ def print_paged_videos(generator, page_size: int, total_count: int):
last = first + len(page) - 1 last = first + len(page) - 1
while True: while True:
print_out("-" * 80) click.echo("-" * 80)
print_out() click.echo()
for video in page: for video in page:
print_video(video) print_video(video)
print_out() click.echo()
last = first + len(page) - 1 last = first + len(page) - 1
print_out("-" * 80) click.echo("-" * 80)
print_out(f"<yellow>Videos {first}-{last} of {total_count}</yellow>") click.echo(f"Videos {first}-{last} of {total_count}")
first = first + len(page) first = first + len(page)
last = first + 1 last = first + 1
@ -146,28 +101,27 @@ def print_paged_videos(generator, page_size: int, total_count: int):
break break
def print_clip(clip): def print_clip(clip: Data):
published_at = clip["createdAt"].replace("T", " @ ").replace("Z", "") published_at = clip["createdAt"].replace("T", " @ ").replace("Z", "")
length = utils.format_duration(clip["durationSeconds"]) length = utils.format_duration(clip["durationSeconds"])
channel = clip["broadcaster"]["displayName"] channel = clip["broadcaster"]["displayName"]
playing = ( playing = f"playing {blue(clip['game']['name'])}" if clip["game"] else ""
f"playing <blue>{clip['game']['name']}</blue>"
if clip["game"] else ""
)
print_out(f"Clip <b>{clip['slug']}</b>") click.echo(f"Clip {bold(clip['slug'])}")
print_out(f"<green>{clip['title']}</green>") click.secho(clip["title"], fg="green")
print_out(f"<blue>{channel}</blue> {playing}") click.echo(f"{blue(channel)} {playing}")
print_out( click.echo(
f"Published <blue>{published_at}</blue>" + f"Published {blue(published_at)}" +
f" Length: <blue>{length}</blue>" + f" Length: {blue(length)}" +
f" Views: <blue>{clip["viewCount"]}</blue>" f" Views: {blue(clip['viewCount'])}"
) )
print_out(f"<i>{clip['url']}</i>") click.secho(clip["url"], italic=True)
def _continue(): def _continue():
print_out("Press <green><b>Enter</green> to continue, <yellow><b>Ctrl+C</yellow> to break.") enter = click.style("Enter", bold=True, fg="green")
ctrl_c = click.style("Ctrl+C", bold=True, fg="yellow")
click.echo(f"Press {enter} to continue, {ctrl_c} to break.")
try: try:
input() input()

View File

@ -1,3 +1,4 @@
import click
import logging import logging
import time import time
@ -6,7 +7,7 @@ from dataclasses import dataclass, field
from statistics import mean from statistics import mean
from typing import Dict, NamedTuple, Optional, Deque from typing import Dict, NamedTuple, Optional, Deque
from twitchdl.output import print_out from twitchdl.output import blue
from twitchdl.utils import format_size, format_time from twitchdl.utils import format_size, format_time
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -127,11 +128,11 @@ class Progress:
progress = " ".join([ progress = " ".join([
f"Downloaded {self.vod_downloaded_count}/{self.vod_count} VODs", f"Downloaded {self.vod_downloaded_count}/{self.vod_count} VODs",
f"<blue>{self.progress_perc}%</blue>", blue(self.progress_perc),
f"of <blue>~{format_size(self.estimated_total)}</blue>" if self.estimated_total else "", f"of ~{blue(format_size(self.estimated_total))}" if self.estimated_total else "",
f"at <blue>{format_size(self.speed)}/s</blue>" if self.speed else "", f"at {blue(format_size(self.speed))}/s" if self.speed else "",
f"ETA <blue>{format_time(self.remaining_time)}</blue>" if self.remaining_time is not None else "", f"ETA {blue(format_time(self.remaining_time))}" if self.remaining_time is not None else "",
]) ])
print_out(f"\r{progress} ", end="") click.echo(f"\r{progress} ", nl=False)
self.last_printed = now self.last_printed = now

View File

@ -11,7 +11,7 @@ def _format_size(value: float, digits: int, unit: str):
return f"{int(value)}{unit}" return f"{int(value)}{unit}"
def format_size(bytes_: int, digits: int = 1): def format_size(bytes_: int | float, digits: int = 1):
if bytes_ < 1024: if bytes_ < 1024:
return _format_size(bytes_, digits, "B") return _format_size(bytes_, digits, "B")