minitch/minitch.py

175 lines
6.3 KiB
Python
Executable File

#!/usr/bin/env python3
import os
import subprocess
import argparse
import atexit
from shutil import unpack_archive
import yaml
import mergedeep
from lib import handlers
from appdirs import user_data_dir, user_config_dir
config = {
'global': {
'game_dir': os.path.join(user_data_dir(), 'minitch'),
'os': 'linux'
},
'games': {}
}
def read_config():
global config
path = os.path.join(user_config_dir(), 'minitchrc')
try:
with open(path, 'r') as f:
c = {**config, **yaml.safe_load(f)}
mergedeep.merge(config, c)
except FileNotFoundError:
pass
except yaml.YAMLError:
raise SystemExit('Invalid config')
def write_config():
global config
path = os.path.join(user_config_dir(), 'minitchrc')
with open(path, 'w') as f:
yaml.dump(config, f, default_flow_style=False)
def parse_config(args):
if args.api_key:
config['global']['api_key'] = args.api_key
def parse_search(args):
global config
if not 'api_key' in config['global']:
raise SystemExit('You have to set your api key first.')
game_handler = handlers.GameHandler(config['global']['api_key'], output_format=args.output)
game_handler.find(args.query)
def parse_uploads(args):
global config
if not 'api_key' in config['global']:
raise SystemExit('You have to set your api key first.')
game_handler = handlers.GameHandler(config['global']['api_key'], output_format=args.output)
game_handler.uploads(args.game_id)
def install_game(game_id, dst_path, output_format):
global config
# Download game
game_handler = handlers.GameHandler(config['global']['api_key'], output_format=output_format)
dst, upload, meta = game_handler.download(game_id, dst_path, os_type=config['global']['os'])
# Unpack game
print('Extracting...')
unpack_archive(dst, dst_path)
return dst, upload, meta
def parse_install(args):
global config
# Create target directory
game_path = os.path.join(config['global']['game_dir'], args.game_id)
os.makedirs(game_path, exist_ok=True)
try:
dst, upload, meta = install_game(args.game_id, game_path, args.output)
# Write game to config
config['games'][args.game_id] = {
'path': game_path,
'last_update': upload['updated_at'],
'url': meta['url'],
'title': meta['title'],
'description': meta['short_text']
}
print('FYI: As itch.io does not have any information on how to execute the game, you have to set the executable manually on first run with the --executable option')
except handlers.HandlerException as err:
raise SystemExit(str(err))
def parser_run(args):
global config
if not args.game_id in config['games']:
raise SystemExit('Game is not installed.')
if args.executable:
config['games'][args.game_id]['executable'] = args.executable
if not 'executable' in config['games'][args.game_id]:
raise SystemExit('You have to define the executable before you can run the game.')
if args.update:
game_handler = handlers.GameHandler(config['global']['api_key'], output_format=args.output)
print('Checking for updates...')
if game_handler.check_update(args.game_id, config['games'][args.game_id]['last_update'], config['global']['os']):
dst, upload, meta = install_game(args.game_id, config['games'][args.game_id]['path'], args.output)
config['games'][args.game_id]['last_update'] = upload['updated_at']
# Run executable
os.chdir(config['games'][args.game_id]['path'])
os.chmod(config['games'][args.game_id]['executable'], 755)
game_args = config['games'][args.game_id]['args'] if 'args' in config['games'][args.game_id] else []
try:
subprocess.run([config['games'][args.game_id]['executable'], *game_args])
except KeyboardInterrupt:
pass
def parser_list(args):
global config
game_handler = handlers.GameHandler(config['global']['api_key'], output_format=args.output)
game_handler.list_local(config['games'])
def main():
read_config()
# Write config on exit
atexit.register(write_config)
parser = argparse.ArgumentParser(description='Tiny little itch.io client')
subparsers = parser.add_subparsers(title='subcommands', description='valid subcommands', help='additional help')
# Global arguments
parser.add_argument('--output', '-o', choices=('table', 'json'), default='table', help='set the output format')
# Parser for "config" command
parser_config = subparsers.add_parser('config', aliases=['c'], help='config help')
parser_config.add_argument('--api_key', type=str, help='set api key')
parser_config.set_defaults(func=parse_config)
# Parser for "search" command
parser_search = subparsers.add_parser('search', aliases=['s'], help='search help')
parser_search.add_argument('query', type=str, help='the search query')
parser_search.set_defaults(func=parse_search)
# Parser for "uploads" command
parser_search = subparsers.add_parser('uploads', help='uploads help')
parser_search.add_argument('game_id', type=str, help='game_id to query')
parser_search.set_defaults(func=parse_uploads)
# Parser for "install" command
parser_search = subparsers.add_parser('install', aliases=['i'], help='install help')
parser_search.add_argument('game_id', type=str, help='game_id to query')
parser_search.set_defaults(func=parse_install)
# Parser for "run" command
parser_search = subparsers.add_parser('run', aliases=['r'], help='run help')
parser_search.add_argument('game_id', type=str, help='game_id to run')
parser_search.add_argument('--executable', '-e', type=str, help='set executable relative to game path (e.g. -e "chess_2_linux_v2/chess 2.x86_64")')
parser_search.add_argument('--update', '-u', help='check for update before running', action='store_true')
parser_search.set_defaults(func=parser_run)
# Parser for "list" command
parser_search = subparsers.add_parser('list', aliases=['l'], help='list help')
parser_search.set_defaults(func=parser_list)
args = parser.parse_args()
try:
args.func(args)
except AttributeError:
parser.error('too few arguments')
if __name__ == "__main__":
main()