irs/src/bottle/cli.cr

191 lines
5.9 KiB
Crystal
Executable File

require "ydl_binaries"
require "./config"
require "./styles"
require "./version"
require "../glue/song"
require "../glue/album"
require "../glue/playlist"
class CLI
# layout:
# [[shortflag, longflag], key, type]
@options = [
[["-h", "--help"], "help", "bool"],
[["-v", "--version"], "version", "bool"],
[["-i", "--install"], "install", "bool"],
[["-c", "--config"], "config", "bool"],
[["-a", "--artist"], "artist", "string"],
[["-s", "--song"], "song", "string"],
[["-A", "--album"], "album", "string"],
[["-p", "--playlist"], "playlist", "string"],
[["-u", "--url"], "url", "string"],
[["-S", "--select"], "select", "bool"],
[["--ask-skip"], "ask_skip", "bool"],
[["--apply"], "apply_file", "string"]
]
@args : Hash(String, String)
def initialize(argv : Array(String))
@args = parse_args(argv)
end
def version
puts "irs v#{IRS::VERSION}"
end
def help
msg = <<-EOP
#{Style.bold "Usage: irs [--help] [--version] [--install]"}
#{Style.bold " [-s <song> -a <artist>]"}
#{Style.bold " [-A <album> -a <artist>]"}
#{Style.bold " [-p <playlist> -a <username>]"}
#{Style.bold "Arguments:"}
#{Style.blue "-h, --help"} Show this help message and exit
#{Style.blue "-v, --version"} Show the program version and exit
#{Style.blue "-i, --install"} Download binaries to config location
#{Style.blue "-c, --config"} Show config file location
#{Style.blue "-a, --artist <artist>"} Specify artist name for downloading
#{Style.blue "-s, --song <song>"} Specify song name to download
#{Style.blue "-A, --album <album>"} Specify the album name to download
#{Style.blue "-p, --playlist <playlist>"} Specify the playlist name to download
#{Style.blue "-u, --url <url>"} Specify the youtube url to download from
#{Style.blue " "} (for albums and playlists, the command-line
#{Style.blue " "} argument is ignored, and it should be '')
#{Style.blue "-S, --select"} Use a menu to choose each song's video source
#{Style.blue "--ask-skip"} Before every playlist/album song, ask to skip
#{Style.blue "--apply <file>"} Apply metadata to a existing file
#{Style.bold "Examples:"}
$ #{Style.green %(irs --song "Bohemian Rhapsody" --artist "Queen")}
#{Style.dim %(# => downloads the song "Bohemian Rhapsody" by "Queen")}
$ #{Style.green %(irs --album "Demon Days" --artist "Gorillaz")}
#{Style.dim %(# => downloads the album "Demon Days" by "Gorillaz")}
$ #{Style.green %(irs --playlist "a different drummer" --artist "prakkillian")}
#{Style.dim %(# => downloads the playlist "a different drummer" by the user prakkillian)}
#{Style.bold "This project is licensed under the MIT license."}
#{Style.bold "Project page: <https://github.com/cooperhammond/irs>"}
EOP
puts msg
end
def act_on_args
Config.check_necessities
if @args["help"]? || @args.keys.size == 0
help
elsif @args["version"]?
version
elsif @args["install"]?
YdlBinaries.get_both(Config.binary_location)
elsif @args["config"]?
puts ENV["IRS_CONFIG_LOCATION"]?
elsif @args["song"]? && @args["artist"]?
s = Song.new(@args["song"], @args["artist"])
s.provide_client_keys(Config.client_key, Config.client_secret)
s.grab_it(flags: @args)
s.organize_it()
elsif @args["album"]? && @args["artist"]?
a = Album.new(@args["album"], @args["artist"])
a.provide_client_keys(Config.client_key, Config.client_secret)
a.grab_it(flags: @args)
elsif @args["playlist"]? && @args["artist"]?
p = Playlist.new(@args["playlist"], @args["artist"])
p.provide_client_keys(Config.client_key, Config.client_secret)
p.grab_it(flags: @args)
else
puts Style.red("Those arguments don't do anything when used that way.")
puts "Type `irs -h` to see usage."
end
end
private def parse_args(argv : Array(String)) : Hash(String, String)
arguments = {} of String => String
i = 0
current_key = ""
pass_next_arg = false
argv.each do |arg|
# If the previous arg was an arg flag, this is an arg, so pass it
if pass_next_arg
pass_next_arg = false
i += 1
next
end
flag = [] of Array(String) | String
valid_flag = false
@options.each do |option|
if option[0].includes?(arg)
flag = option
valid_flag = true
break
end
end
# ensure the flag is actually defined
if !valid_flag
arg_error argv, i, %("#{arg}" is an invalid flag or argument.)
end
# ensure there's an argument if the program needs one
if flag[2] == "string" && i + 1 >= argv.size
arg_error argv, i, %("#{arg}" needs an argument.)
end
key = flag[1].as(String)
if flag[2] == "string"
arguments[key] = argv[i + 1]
pass_next_arg = true
elsif flag[2] == "bool"
arguments[key] = "true"
end
i += 1
end
return arguments
end
private def arg_error(argv : Array(String), arg : Int32, msg : String) : Nil
precursor = "irs"
precursor += " " if arg != 0
if arg == 0
start = [] of String
else
start = argv[..arg - 1]
end
last = argv[arg + 1..]
distance = (precursor + start.join(" ")).size
print Style.dim(precursor + start.join(" "))
print Style.bold(Style.red(" " + argv[arg]).to_s)
puts Style.dim (" " + last.join(" "))
(0..distance).each do |i|
print " "
end
puts "^"
puts Style.red(Style.bold(msg).to_s)
puts "Type `irs -h` to see usage."
exit 1
end
end