Skip to content
Extraits de code Groupes Projets

Add first version of Kurisu client Kagari

3 files
+ 294
10
Comparer les modifications
  • Côte à côte
  • En ligne

Fichiers

+ 274
0
 
#!/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()
Chargement en cours