Skip to content
Extraits de code Groupes Projets
Valider ef99b02c rédigé par Krocoh's avatar Krocoh
Parcourir les fichiers

Add first version of Kurisu client Kagari

parent 7d1d7532
Aucune branche associée trouvée
Aucune étiquette associée trouvée
1 requête de fusion!146Add first version of Kurisu client Kagari
Pipeline #2117 réussi avec des avertissements
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import json
import shutil
import argparse
from urllib.error import URLError
from urllib.parse import urlencode
from urllib.request import urlopen
class CustomFormatter(
argparse.RawTextHelpFormatter,
argparse.RawDescriptionHelpFormatter,
):
pass
def create_parser():
"Creates and returns a parser for the CLI arguments"
usage = "kagari [-h] (dl [dl options] | search [search options])"
desc = """Client to interact with the Kurisu API. Two main modes:
-dl: downloads the specified file, or a tar file of the entire database
-search: searches the database with filters for info on the files"""
parser = argparse.ArgumentParser(
usage=usage,
description=desc,
formatter_class=CustomFormatter,
add_help=False,
)
parser.add_argument(
"-h",
"--help",
action="help",
default=argparse.SUPPRESS,
help="Show this help message and exit.\n ",
)
parser.add_argument(
"mode",
help='The mode used. Can be either "dl" or "search".',
)
parser.add_argument(
"-i",
"--id",
help='In "dl" mode, if specified, downloads target video file.'
+ "\nIf absent in this mode, downloads the tar of the whole base."
+ '\nIn "search" mode, returns info of the target file.\n ',
)
parser.add_argument(
"-p",
"--path",
help='In "dl" mode, indicates the folder in which to store the file.'
+ "\nIf absent in this mode, defaults to current working directory."
+ '\nIn "search" mode, if specified, saves the output as a file'
+ " at the given path.\n ",
)
parser.add_argument(
"-C",
"--check",
action="store_true",
help='In "dl" mode, first displays info on the targeted'
+ "\nfile, and asks for confirmation before downloading.\n ",
)
parser.add_argument(
"-a",
"--author",
help='In "search" mode, filters by author. (case insensitive)'
)
parser.add_argument(
"-c",
"--category",
dest="cat",
help='In "search" mode, filters by category.\n ',
)
parser.add_argument(
"-t", "--type", help='In "search" mode, filters by type.\n '
)
parser.add_argument(
"-s",
"--string",
dest="search",
help='In "search" mode, filters on the names of sources and songs'
+ "\nmatching the given string. (case insensitive)\n "
)
parser.add_argument(
"-r",
"--random",
action="store_true",
help='In "search" mode, randomizes the order of the files '
+ "in the response.\n ",
)
return parser
def make_request(url):
"Executes an HTTP request with basic error handling"
try:
response = urlopen(url)
except URLError as err:
if hasattr(err, "reason"):
emsg = f"Failed to reach target URL. Reason: {err.reason}"
print(emsg)
sys.exit(1)
elif hasattr(err, "code"):
emsg = f"The server could not fulfill the request. Code {err.code}"
print(emsg)
sys.exit(1)
return response
def build_kara_name(data):
"Builds the name of the file from the response dictionary"
src_name = data["source_name"]
song_name = data["song_name"]
song_type = data["song_type"]
song_nbr = data["song_number"]
category = data["category"]
language = data["language"]
author = data["author_name"]
kara_name = f"{src_name} - {song_type}{song_nbr} - {song_name}"
kara_additional = f" ({category}/{language}) by {author}"
return kara_name, kara_additional
def kara_prompt(kara_string):
"Simple yes/no prompt to confirm the download or not"
prompt_str = "You are about to download " + kara_string
prompt_str += "\nDo you want to proceed ? ([y]/n) "
answers = {"y": True, "n": False, "ye": True, "yes": True, "no": False}
ans = None
while ans not in answers.keys():
try:
ans = input(prompt_str).lower()
if not ans:
ans = "y"
except KeyboardInterrupt:
ans = "n"
print("\n")
except EOFError:
ans = "y"
return answers[ans]
def dl_mode(args):
"Downloads either the whole database as a tar, or a single video file"
# No ID, download the tar of the database
if not args.id:
url = "https://kurisu.iiens.net/kara.tar"
path = args.path if args.path else os.getcwd()
filename = path + "/kara.tar"
print(f'Downloading and writing archive as "{filename}"')
response = make_request(url)
# Writing the tar file
with open(filename, 'wb') as out_file:
try:
shutil.copyfileobj(response, out_file)
except KeyboardInterrupt:
print("\nDownload interrupted by user input.")
else:
print("All done !")
# An ID, download target file
else:
search_url = f"https://kurisu.iiens.net/api?id={args.id}"
search = make_request(search_url)
kara_data = json.loads(search.read().decode("utf-8"))
kara_name, kara_additional = build_kara_name(kara_data)
# If asked for check, display the prompt
if args.check:
kara_string = '"' + kara_name + '"' + kara_additional
prompt = kara_prompt(kara_string)
if not prompt:
print("Download cancelled.")
return
url = f"https://kurisu.iiens.net/api/download/{args.id}"
path = args.path if args.path else os.getcwd()
filename = path + "/" + kara_name + ".mkv"
print(f'Downloading and writing video as "{filename}"')
response = make_request(url)
# Writing the video file
with open(filename, 'wb') as out_file:
try:
shutil.copyfileobj(response, out_file)
except KeyboardInterrupt:
print("\nDownload interrupted by user input.")
else:
print("All done !")
def search_mode(args):
"Performs a search request on the database and returns the answer"
url = "https://kurisu.iiens.net/api"
url_values = dict()
# If ID, it's the only parameter needed
if args.id:
url_values["id"] = args.id
# No ID, building the query parameters
else:
for arg_name in ["author", "cat", "type", "search", "random"]:
arg = getattr(args, arg_name)
if arg:
url_values[arg_name] = arg
url_args = urlencode(url_values)
if url_args:
url += "?" + url_args
response = make_request(url)
try:
kara_json = json.loads(response.read().decode("utf-8"))
except ValueError:
emsg = "The response JSON could not be loaded."
print(emsg)
sys.exit(1)
# Displaying the received JSON, or writing it to a file
pretty_output = json.dumps(kara_json, indent=4, ensure_ascii=False)
if not args.path:
print(pretty_output)
else:
try:
with open(args.path, "w") as output_file:
output_file.write(pretty_output)
except OSError as err:
emsg = "Cannot create/open the search output file: "
emsg += err
print(emsg)
sys.exit(1)
def run():
"Runs the command"
parser = create_parser()
args = parser.parse_args()
if args.mode == "dl":
dl_mode(args)
elif args.mode == "search":
search_mode(args)
else:
print(f'Unknown mode "{args.mode}". Use either "dl" or "search".')
sys.exit(1)
if __name__ == "__main__":
run()
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter