Last active
May 18, 2022 21:27
-
-
Save goncalomb/5c8724771d75140e6a6dc9ffeef800c8 to your computer and use it in GitHub Desktop.
Script to rename PSX and PS2 rom files.
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
#!/bin/env python3 | |
# Copyright (c) 2022 Gonçalo Baltazar <[email protected]> | |
# MIT License | |
import os, argparse, shutil, tempfile, requests, zipfile | |
import xml.etree.ElementTree as ET | |
tmp_dir = os.path.join(tempfile.gettempdir(), 'ps-names') | |
datfile_list = [ | |
('psx', 'http://redump.org/datfile/psx/serial,version'), | |
('ps2', 'http://redump.org/datfile/ps2/serial,version'), | |
] | |
def download_redump_datfile(url: str, local_path: str): | |
with tempfile.TemporaryFile() as tf: | |
with requests.get(url, stream=True) as r: | |
r.raise_for_status() | |
for chunk in r.iter_content(None): | |
tf.write(chunk) | |
with zipfile.ZipFile(tf) as zf: | |
fname = '' | |
for zi in zf.infolist(): | |
if os.path.splitext(zi.filename)[1] == '.dat': | |
fname = zi.filename | |
break | |
if fname: | |
with open(local_path, 'wb') as fp: | |
fp.write(zf.read(fname)) | |
return True | |
return False | |
def read_datfile(url: str, tmp_local_name: str): | |
local_file = os.path.join(tmp_dir, tmp_local_name) | |
if os.path.isfile(local_file): | |
print("using local file '%s'" % (local_file)) | |
else: | |
try: os.mkdir(tmp_dir) | |
except FileExistsError: pass | |
print("dowloading %s" % (url)) | |
if download_redump_datfile(url, local_file): | |
print("saved to '%s'" % (local_file)) | |
else: | |
print("datfile not found (outdated downloader?)") | |
data = [] | |
root = ET.parse(local_file).getroot() | |
def el_find_text(el: ET.Element, path: str): | |
el_found = el.find(path) | |
return None if el_found is None else el_found.text | |
for el_game in root.findall('./game'): | |
data.append({ | |
'name': el_game.get('name'), | |
'category': el_find_text(el_game, './category'), | |
'serial': el_find_text(el_game, './serial'), | |
'version': el_find_text(el_game, './version'), | |
'description': el_find_text(el_game, './description'), | |
}) | |
return data | |
def cleanup_serial(s: str): | |
return s.strip().replace(' ', '-').upper() | |
class Database: | |
def __init__(self): | |
self.data = {} | |
for (slug, url) in datfile_list: | |
self.data[slug] = read_datfile(url, slug + '.dat') | |
self.data_by_serial = {} | |
for d in self.data.values(): | |
for game in d: | |
if game['serial']: | |
for serial in map(cleanup_serial, game['serial'].split(',')): | |
if serial in self.data_by_serial: | |
# print("duplicate serial '%s', '%s' and '%s'" % (serial, self.data_by_serial[serial]['name'], game['name'])) | |
# duplicate serial, use shortest name (TODO: could use a better heuristic) | |
if len(game['name']) < len(self.data_by_serial[serial]['name']): | |
self.data_by_serial[serial] = game | |
else: | |
self.data_by_serial[serial] = game | |
def get_game_by_serial(self, serial): | |
s = serial | |
if s in self.data_by_serial: | |
return self.data_by_serial[s] | |
s = cleanup_serial(s) | |
if s in self.data_by_serial: | |
return self.data_by_serial[s] | |
s = s.replace('_', '-') | |
if s in self.data_by_serial: | |
return self.data_by_serial[s] | |
s = s.replace('.', '') | |
if s in self.data_by_serial: | |
return self.data_by_serial[s] | |
return None | |
def get_name_by_serial(self, serial): | |
game = self.get_game_by_serial(serial) | |
if game: | |
return game['name'] | |
return | |
if __name__ == '__main__': | |
parser = argparse.ArgumentParser(prog='ps-names') | |
subparsers = parser.add_subparsers(title='commands', dest='command', required=True) | |
parser_rename = subparsers.add_parser('rename', description='rename files') | |
parser_rename.add_argument('--confirm', action='store_true', help='confirm operation') | |
parser_rename.add_argument('files', nargs='+', help='list of files') | |
parser_delete_tmp = subparsers.add_parser('delete-tmp', description='delete temporary files') | |
args = parser.parse_args() | |
if args.command == 'rename': | |
db = Database() | |
for f in args.files: | |
if os.path.isfile(f): | |
(head, tail) = os.path.split(f) | |
(name, ext) = os.path.splitext(tail) | |
new_name = db.get_name_by_serial(name) | |
if new_name: | |
if args.confirm: | |
print("renaming '%s' to '%s'" % (f, new_name + ext)) | |
f_new = os.path.join(head, new_name + ext) | |
os.rename(f, f_new) | |
else: | |
print("would rename '%s' to '%s'" % (f, new_name + ext)) | |
else: | |
print("name not found for '%s'" % (tail)) | |
else: | |
print("'%s' is not a file" % (f)) | |
if args.confirm: | |
print("dry run, use --confirm to rename") | |
if args.command == 'delete-tmp': | |
if os.path.isdir(tmp_dir): | |
print("removing '%s'" % (tmp_dir)) | |
shutil.rmtree(tmp_dir) | |
else: | |
print("nothing to remove") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment