-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Change CLI backend to Typer (#19)
- Loading branch information
1 parent
0de66f4
commit 8223bd1
Showing
1 changed file
with
245 additions
and
126 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,135 +1,254 @@ | ||
"""Cli functions to define the arguments and to call Makim.""" | ||
import argparse | ||
import sys | ||
import typer | ||
|
||
from typing_extensions import Annotated | ||
|
||
from artbox import __version__ | ||
from artbox.sounds import Sound | ||
from artbox.videos import Video, Youtube | ||
from artbox.voices import Voice | ||
|
||
|
||
class CustomHelpFormatter(argparse.RawTextHelpFormatter): | ||
"""Formatter for generating usage messages and argument help strings. | ||
Only the name of this class is considered a public API. All the methods | ||
provided by the class are considered an implementation detail. | ||
""" | ||
|
||
def __init__( | ||
self, | ||
prog, | ||
indent_increment=2, | ||
max_help_position=4, | ||
width=None, | ||
**kwargs, | ||
): | ||
"""Define the parameters for the argparse help text.""" | ||
super().__init__( | ||
prog, | ||
indent_increment=indent_increment, | ||
max_help_position=max_help_position, | ||
width=width, | ||
**kwargs, | ||
) | ||
|
||
|
||
def _get_args(): | ||
"""Define the arguments for the CLI.""" | ||
parser = argparse.ArgumentParser( | ||
prog="ArtBox", | ||
description="A set of tools for handling multimedia files.", | ||
epilog=( | ||
"If you have any problem, open an issue at: " | ||
"https://github.com/ggpedia/artbox" | ||
), | ||
formatter_class=CustomHelpFormatter, | ||
) | ||
parser.add_argument( | ||
app = typer.Typer( | ||
name="Artbox", | ||
help="A set of tools for handling multimedia files.", | ||
epilog=( | ||
"If you have any problem, open an issue at: " | ||
"https://github.com/ggpedia/artbox" | ||
), | ||
) | ||
|
||
app_sound = typer.Typer( | ||
name="sound", | ||
help="Audio processing commands for Artbox.", | ||
short_help="Audio processing commands.", | ||
) | ||
app_video = typer.Typer( | ||
name="video", | ||
help="Video processing commands for Artbox.", | ||
short_help="Video processing commands.", | ||
) | ||
app_voice = typer.Typer( | ||
name="voice", | ||
help="Voice processing commands for Artbox.", | ||
short_help="Voice processing commands.", | ||
) | ||
app_youtube = typer.Typer( | ||
name="youtube", | ||
help="YouTube processing commands for Artbox.", | ||
short_help="YouTube processing commands.", | ||
) | ||
|
||
app.add_typer(app_sound, name="sound") | ||
app.add_typer(app_video, name="video") | ||
app.add_typer(app_voice, name="voice") | ||
app.add_typer(app_youtube, name="youtube") | ||
|
||
|
||
@app.callback(invoke_without_command=True) | ||
def main( | ||
ctx: typer.Context, | ||
version: bool = typer.Option( | ||
None, | ||
"--version", | ||
action="store_true", | ||
help="Show the version of the installed MakIm tool.", | ||
) | ||
parser.add_argument( | ||
"runner", | ||
default=None, | ||
help=( | ||
"Specify the runner to be performed. " | ||
"\nOptions are: audio, sounds, and video." | ||
"-v", | ||
is_flag=True, | ||
help="Show the version and exit.", | ||
), | ||
) -> None: | ||
"""Process commands for specific flags; otherwise, show the help menu.""" | ||
if version: | ||
typer.echo(f"Version: {__version__}") | ||
raise typer.Exit() | ||
|
||
if ctx.invoked_subcommand is None: | ||
typer.echo(ctx.get_help()) | ||
raise typer.Exit(0) | ||
|
||
|
||
@app_voice.command("text-to-speech") | ||
def text_to_speech( | ||
title: Annotated[ | ||
str, typer.Option("--title", help="Specify the name of the audio file") | ||
] = "artbox", | ||
text_path: Annotated[ | ||
str, | ||
typer.Option("--text-path", help="Specify the path of the text file"), | ||
] = "", | ||
output_path: Annotated[ | ||
str, | ||
typer.Option( | ||
"--output-path", help="Specify the path to store the audio file" | ||
), | ||
) | ||
parser.add_argument( | ||
"method", | ||
default=None, | ||
help=("Specify the runner method to be performed."), | ||
) | ||
return parser | ||
|
||
|
||
def extract_artbox_args(): | ||
"""Extract artbox arguments from the CLI call.""" | ||
artbox_args = {} | ||
index_to_remove = [] | ||
|
||
for ind, arg in enumerate(list(sys.argv)): | ||
if arg in [ | ||
"--help", | ||
"--version", | ||
]: | ||
continue | ||
|
||
if not arg.startswith("--"): | ||
continue | ||
|
||
index_to_remove.append(ind) | ||
|
||
arg_name = None | ||
arg_value = None | ||
|
||
next_ind = ind + 1 | ||
|
||
arg_name = sys.argv[ind][2:] | ||
|
||
if ( | ||
len(sys.argv) == next_ind | ||
or len(sys.argv) > next_ind | ||
and sys.argv[next_ind].startswith("--") | ||
): | ||
arg_value = True | ||
else: | ||
arg_value = sys.argv[next_ind] | ||
index_to_remove.append(next_ind) | ||
|
||
artbox_args[arg_name] = arg_value | ||
|
||
# remove exclusive artbox flags from original sys.argv | ||
for ind in sorted(index_to_remove, reverse=True): | ||
sys.argv.pop(ind) | ||
|
||
return artbox_args | ||
|
||
|
||
def show_version(): | ||
"""Show version.""" | ||
print(__version__) | ||
|
||
|
||
def app(): | ||
"""Call the artbox program with the arguments defined by the user.""" | ||
artbox_args = extract_artbox_args() | ||
|
||
args_parser = _get_args() | ||
args = args_parser.parse_args() | ||
|
||
if args.version: | ||
return show_version() | ||
|
||
if args.runner == "sound": | ||
runner = Sound(artbox_args) | ||
elif args.runner == "voice": | ||
runner = Voice(artbox_args) | ||
elif args.runner == "video": | ||
runner = Video(artbox_args) | ||
elif args.runner == "youtube": | ||
runner = Youtube(artbox_args) | ||
|
||
return getattr(runner, args.method.replace("-", "_"))() | ||
] = "", | ||
engine: Annotated[ | ||
str, | ||
typer.Option( | ||
"--engine", | ||
help="Choose the text-to-speech engine (Options: edge-tts, gtts)", | ||
), | ||
] = "edge-tts", | ||
lang: Annotated[ | ||
str, | ||
typer.Option( | ||
"--lang", help="Choose the language for audio generation" | ||
), | ||
] = "en", | ||
) -> None: | ||
"""Convert text to speech.""" | ||
args_dict = { | ||
"title": title, | ||
"text-path": text_path, | ||
"output-path": output_path, | ||
"engine": engine, | ||
"lang": lang, | ||
} | ||
|
||
runner = Voice(args_dict) | ||
runner.text_to_speech() | ||
|
||
|
||
@app_sound.command("notes-to-audio") | ||
def notes_to_audio( | ||
input_path: Annotated[ | ||
str, | ||
typer.Option( | ||
"--input-path", help="Specify the path of the input file" | ||
), | ||
] = "", | ||
output_path: Annotated[ | ||
str, | ||
typer.Option( | ||
"--output-path", help="Specify the path to store the audio file" | ||
), | ||
] = "", | ||
duration: Annotated[ | ||
str, | ||
typer.Option("--duration", help="Specify the duration of the audio"), | ||
] = "", | ||
) -> None: | ||
"""Convert notes to audio.""" | ||
args_dict = { | ||
"input-path": input_path, | ||
"output-path": output_path, | ||
"duration": duration, | ||
} | ||
|
||
runner = Sound(args_dict) | ||
runner.notes_to_audio() | ||
|
||
|
||
@app_video.command("remove-audio") | ||
def remove_audio( | ||
input_path: Annotated[ | ||
str, | ||
typer.Option( | ||
"--input-path", help="Specify the path of the input video file" | ||
), | ||
] = "", | ||
output_path: Annotated[ | ||
str, | ||
typer.Option( | ||
"--output-path", help="Specify the path to store the video file" | ||
), | ||
] = "", | ||
) -> None: | ||
"""Remove audio from video file.""" | ||
args_dict = { | ||
"input-path": input_path, | ||
"output-path": output_path, | ||
} | ||
|
||
runner = Video(args_dict) | ||
runner.remove_audio() | ||
|
||
|
||
@app_video.command("extract-audio") | ||
def extract_audio( | ||
input_path: Annotated[ | ||
str, | ||
typer.Option( | ||
"--input-path", help="Specify the path of the input video file" | ||
), | ||
] = "", | ||
output_path: Annotated[ | ||
str, | ||
typer.Option( | ||
"--output-path", | ||
help="Specify the path to store the extracted audio file", | ||
), | ||
] = "", | ||
) -> None: | ||
"""Extract audio from video file.""" | ||
args_dict = { | ||
"input-path": input_path, | ||
"output-path": output_path, | ||
} | ||
|
||
runner = Video(args_dict) | ||
runner.extract_audio() | ||
|
||
|
||
@app_video.command("combine-video-and-audio") | ||
def combine_audio_and_video( | ||
video_path: Annotated[ | ||
str, | ||
typer.Option( | ||
"--video-path", help="Specify the path of the video file" | ||
), | ||
] = "", | ||
audio_path: Annotated[ | ||
str, | ||
typer.Option( | ||
"--audio-path", help="Specify the path of the audio file" | ||
), | ||
] = "", | ||
output_path: Annotated[ | ||
str, | ||
typer.Option( | ||
"--output-path", | ||
help="Specify the path to store the combined video file", | ||
), | ||
] = "", | ||
) -> None: | ||
"""Combine audio and video files.""" | ||
args_dict = { | ||
"video-path": video_path, | ||
"audio-path": audio_path, | ||
"output-path": output_path, | ||
} | ||
|
||
runner = Video(args_dict) | ||
runner.combine_video_and_audio() | ||
|
||
|
||
@app_youtube.command("download") | ||
def download_youtube_video( | ||
url: Annotated[ | ||
str, | ||
typer.Option( | ||
"--url", help="Specify the URL of the YouTube video to download" | ||
), | ||
] = "", | ||
output_path: Annotated[ | ||
str, | ||
typer.Option( | ||
"--output-path", | ||
help="Specify the path to store the downloaded video file", | ||
), | ||
] = "", | ||
resolution: Annotated[ | ||
str, | ||
typer.Option( | ||
"--resolution", help="Set the quality of the downloaded video" | ||
), | ||
] = "", | ||
) -> None: | ||
"""Download youtube video.""" | ||
args_dict = { | ||
"url": url, | ||
"output-path": output_path, | ||
"resolution": resolution, | ||
} | ||
|
||
runner = Youtube(args_dict) | ||
runner.download() |