From 2438995093f433d3b1c1f62ca802469de273ddfd Mon Sep 17 00:00:00 2001 From: yrriban Date: Tue, 13 May 2025 05:46:26 -0400 Subject: [PATCH] Major refactor to make handling name/path manipulation easier. Most of this is now in doom_base.py, which stores wad/map/name, exposes them as properties, and also takes care of most common tasks needed for building command lines and such. --- dcc/config.py | 65 +---------------------------------- dcc/doom_base.py | 88 ++++++++++++++++++++++++++++++++++++++++++++++++ dcc/dsda.py | 15 ++++----- dcc/fabricate.py | 7 ++-- dcc/pb.py | 2 +- dcc/play.py | 2 +- dcc/put.py | 17 +++------- dcc/record.py | 5 ++- dcc/ss.py | 2 +- dcc/text.py | 2 +- dcc/thumb.py | 8 ++--- 11 files changed, 113 insertions(+), 100 deletions(-) diff --git a/dcc/config.py b/dcc/config.py index 4892118..1205d67 100644 --- a/dcc/config.py +++ b/dcc/config.py @@ -22,46 +22,6 @@ TEXT_STROKE_COLOR="srgb(176,0,0)" MIRROR="https://youfailit.net/pub/idgames" # NYC -def DsdaPreamble(wad, mapstr): - args = ["-iwad", IwadPath(wad)] - - pwadpath = PWADS.joinpath(wad) - wads = sorted(pwadpath.glob('*.wad', case_sensitive=False)) - if len(wads) > 0: - args = args + ["-file"] + wads - - dehs = sorted(pwadpath.glob('*.deh', case_sensitive=False)) - if len(dehs) > 0: - args = args + ["-deh"] + dehs - - complevel = pwadpath.joinpath("complevel") - if not complevel.exists(): - raise Exception("No complevel set in PWAD dir {}.".format(pwadpath)) - - with io.open(complevel) as f: - args = args + ["-complevel", Complevel(wad)] - - args = args + ["-skill", "4"] - args = args + ["-warp", mapstr] # TODO: UDoom - return args - -def DemoInPath(wad, mapstr, name=None): - candidates = [x for x in DEMOS.joinpath(wad).glob("{}_map{}{}*.lmp".format(wad, mapstr, NameString(name)))] - if len(candidates) == 0: - raise Exception("no suitable demo candidates for WAD {} MAP {}.".format(wad, mapstr)) - if len(candidates) == 1: - return candidates[0] - return sorted(filter(lambda s : re.search("-", str(s)), candidates))[-1] - -def DsdaTextPath(wad, mapstr): - return Ensure(DEMOS.joinpath(wad)).joinpath("{}_map{}.txt".format(wad, mapstr)) - -def DemoOutPath(wad, mapstr, name=None): - return Ensure(DEMOS.joinpath(wad)).joinpath(DemoName(wad, mapstr, name)) - -def DemoName(wad, mapstr, name=None): - return "{}_map{}{}.lmp".format(wad, mapstr, NameString(name)) - def Complevel(wad): complevel = PwadPath(wad).joinpath("complevel") if not complevel.exists(): @@ -79,27 +39,4 @@ def IwadPath(wad): return iwad def PwadPath(wad): - return Ensure(PWADS.joinpath(wad)) - -def BaseThumbPath(wad, mapstr, name=None): - return Ensure(OUTPUT.joinpath(wad)).joinpath("{}_map{}{}_base.png".format(wad, mapstr, NameString(name))) - -def MDoomPath(wad): - return Ensure(OUTPUT.joinpath(wad)).joinpath("M_DOOM_scaled.png") - -def TextThumbPath(wad, mapstr, name=None): - return Ensure(OUTPUT.joinpath(wad)).joinpath("{}_map{}_text.png".format(wad, mapstr, NameString(name))) - -def ThumbPath(wad, mapstr, name=None): - return Ensure(OUTPUT.joinpath(wad)).joinpath("{}_map{}_thumb.png".format(wad, mapstr, NameString(name))) - -def VideoPath(wad, mapstr, name=None): - return Ensure(OUTPUT.joinpath(wad)).joinpath("{}_map{}{}.mp4".format(wad, mapstr, NameString(name))) - -def NameString(name): - return "" if name is None else "_" + name - -def Ensure(path): - if not path.exists(): - os.mkdir(path) - return path + return PWADS.joinpath(wad) diff --git a/dcc/doom_base.py b/dcc/doom_base.py index afc9e78..668bdc7 100644 --- a/dcc/doom_base.py +++ b/dcc/doom_base.py @@ -1,4 +1,7 @@ +from abc import abstractmethod from cliff.command import Command +import dcc.config +import io class WadMap(Command): def get_parser(self, prog_name): @@ -7,3 +10,88 @@ class WadMap(Command): parser.add_argument('map') parser.add_argument('-n','--name','--demo_name') return parser + + def run(self, parsed_args): + self._wad = parsed_args.wad + self._map = parsed_args.map + self._name = parsed_args.name + self.take_action(parsed_args) + + @abstractmethod + def take_action(self, parsed_args): + pass + + @property + def wad(self): + return self._wad + + @property + def map(self): + return self._map + + @property + def name_string(self): + return "" if self._name is None else "_" + self._name + + def dsda_preamble(self): + args = ["-iwad", dcc.config.IwadPath(self.wad)] + + pwadpath = dcc.config.PWADS.joinpath(self.wad) + wads = sorted(pwadpath.glob('*.wad', case_sensitive=False)) + if len(wads) > 0: + args = args + ["-file"] + wads + + dehs = sorted(pwadpath.glob('*.deh', case_sensitive=False)) + if len(dehs) > 0: + args = args + ["-deh"] + dehs + + complevel = pwadpath.joinpath("complevel") + if not complevel.exists(): + raise Exception("No complevel set in PWAD dir {}.".format(pwadpath)) + + with io.open(complevel) as f: + args = args + ["-complevel", dcc.config.Complevel(self.wad)] + + args = args + ["-skill", "4"] + args = args + ["-warp", self.map] + return args + + def demo_in_path(self): + candidates = [x for x in dcc.config.DEMOS.joinpath(self.wad).glob(self._file_base("*.lmp"))] + if len(candidates) == 0: + raise Exception("no suitable demo candidates for WAD {} MAP {} name {}.".format(self.wad, self.map, self.name_string)) + if len(candidates) == 1: + return candidates[0] + return sorted(filter(lambda s : re.search("-", str(s)), candidates))[-1] + + def dsda_text_path(self): + return _ensure(dcc.config.DEMOS.joinpath(self.wad)).joinpath(self._file_base(".txt")) + + def video_path(self): + return _ensure(dcc.config.OUTPUT.joinpath(self.wad)).joinpath(self._file_base(".mp4")) + + def demo_out_path(self): + return _ensure(dcc.config.DEMOS.joinpath(self.wad)).joinpath(self._file_base(".lmp")) + + def target_bucket(self): + return "doom/" + self._file_base(".lmp") + + def base_thumb_path(self): + return _ensure(dcc.config.OUTPUT.joinpath(self.wad)).joinpath(_file_base("_base.png")) + + def m_doom_path(self): + return _ensure(OUTPUT.joinpath(self.wad)).joinpath("M_DOOM_scaled.png") + + def text_thumb_path(self): + return _ensure(dcc.config.OUTPUT.joinpath(self.wad)).joinpath(_file_base("_text.png")) + + def thumb_path(self): + return _ensure(dcc.config.OUTPUT.joinpath(self.wad)).joinpath(_file_base("_thumb.png")) + + def _file_base(self, ext): + return "{}_map{}{}{}".format(self.wad, self.map, self.name_string, ext) + + def _ensure(path): + if not path.exists(): + os.mkdir(path) + return path diff --git a/dcc/dsda.py b/dcc/dsda.py index df61a67..8f71f70 100644 --- a/dcc/dsda.py +++ b/dcc/dsda.py @@ -14,15 +14,14 @@ class DSDA(dcc.doom_base.WadMap): return parser def take_action(self, parsed_args): - dip = dcc.config.DemoInPath(parsed_args.wad, parsed_args.map, parsed_args.name) - dtp = dcc.config.DsdaTextPath(parsed_args.wad, parsed_args.map, parsed_args.name) + dip = self.demo_in_path() + dtp = self.dsda_text_path() if not dtp.exists(): command = [dcc.config.DSDA] if shutil.which("xvfb-run") is not None: command = ["xvfb-run"] + command # TODO: negative tics should seek from the end, but this doesn't seem to work. - subprocess.run(command + - dcc.config.DsdaPreamble(parsed_args.wad, parsed_args.map) + + subprocess.run(command + self.dsda_preamble() + ["-fastdemo", dip, "-nosound", "-skiptic", "999999999", "-export_text_file"]) editor = "nano" if "EDITOR" in os.environ: @@ -31,11 +30,11 @@ class DSDA(dcc.doom_base.WadMap): if parsed_args.abbreviation: fh1 = parsed_args.abbreviation if not parsed_args.single: - fh1 += parsed_args.map + fh1 += self.map else: - fh1 = parsed_args.wad[0:2] + parsed_args.map + fh1 = self.wad[0:2] + self.map if parsed_args.single: - fh1 = parsed_args.wad[0:min(len(parsed_args.wad), 4)] + fh1 = self.wad[0:min(len(self.wad), 4)] fh2 = "" with open(dtp, mode="r") as f: for line in f: @@ -52,6 +51,6 @@ class DSDA(dcc.doom_base.WadMap): # TODO: demo names other than uv-max. fnf = fh1 + "-" + fh2 + ".zip" - with zipfile.ZipFile(dcc.config.DEMOS.joinpath(parsed_args.wad).joinpath(fnf), mode="w") as zf: + with zipfile.ZipFile(dcc.config.DEMOS.joinpath(self.wad).joinpath(fnf), mode="w") as zf: zf.write(dip, arcname=dip.name) zf.write(dtp, arcname=dtp.name) diff --git a/dcc/fabricate.py b/dcc/fabricate.py index 2a1b54a..229f50e 100644 --- a/dcc/fabricate.py +++ b/dcc/fabricate.py @@ -17,7 +17,6 @@ class Fabricate(dcc.doom_base.WadMap): command = [dcc.config.DSDA] if not parsed_args.fg and shutil.which("xvfb-run") is not None: command = ["xvfb-run"] + command - subprocess.run(command + - dcc.config.DsdaPreamble(parsed_args.wad, parsed_args.map) + - ["-timedemo", dcc.config.DemoInPath(parsed_args.wad, parsed_args.map, parsed_args.name)] + - ["-viddump", dcc.config.VideoPath(parsed_args.wad, parsed_args.map, parsed_args.name)]) + subprocess.run(command + self.dsda_preamble() + + ["-timedemo", self.demo_in_path()] + + ["-viddump", self.video_path()]) diff --git a/dcc/pb.py b/dcc/pb.py index ce413e0..db6e907 100644 --- a/dcc/pb.py +++ b/dcc/pb.py @@ -4,4 +4,4 @@ import subprocess class PB(dcc.doom_base.WadMap): def take_action(self, parsed_args): - subprocess.run(["ffplay", dcc.config.VideoPath(parsed_args.wad, parsed_args.map, parsed_args.name)]) + subprocess.run(["ffplay", self.video_path()]) diff --git a/dcc/play.py b/dcc/play.py index 876439f..1a49caa 100644 --- a/dcc/play.py +++ b/dcc/play.py @@ -4,4 +4,4 @@ import subprocess class Play(dcc.doom_base.WadMap): def take_action(self, parsed_args): - subprocess.run([dcc.config.DSDA] + dcc.config.DsdaPreamble(parsed_args.wad, parsed_args.map)) + subprocess.run([dcc.config.DSDA] + self.dsda_preamble()) diff --git a/dcc/put.py b/dcc/put.py index c076bcc..105d5b4 100644 --- a/dcc/put.py +++ b/dcc/put.py @@ -5,22 +5,13 @@ import dcc.doom_base class Put(dcc.doom_base.WadMap): def get_parser(self, prog_name): parser = super().get_parser(prog_name) - parser.add_argument("-d", "--dsda", action="store_true") - parser.add_argument("-s", "--single", action="store_true") return parser # TODO: accept configuration for bucket name def take_action(self, parsed_args): s3_client = boto3.client('s3') - demo = dcc.config.DemoInPath(parsed_args.wad, parsed_args.map, parsed_args.name) - bucket = dcc.config.DemoName(parsed_args.wad, parsed_args.map, parsed_args.name) - if parsed_args.dsda: - pattern = ("{}{}".format(parsed_args.wad[0:2], parsed_args.map) if not parsed_args.single else parsed_args.wad[0:4]) + "*.zip" - zips = [x for x in dcc.config.DEMOS.joinpath(parsed_args.wad).glob(pattern)] - if len(zips) != 1: - raise Exception("Unable to identify exactly one suitable upload candidate (was {})".format(zips)) - demo = zips[0] - bucket = "dsda/{}".format(demo.name) - print("Uploading {} to bucket doom/{}.".format(demo, bucket)) - s3_client.upload_file(demo, 'yrriban', 'doom/' + bucket, + demo = self.demo_in_path() + bucket = self.target_bucket() + print("Uploading {} to bucket {}.".format(demo, bucket)) + s3_client.upload_file(demo, 'yrriban', bucket, ExtraArgs={'ContentType': 'binary/octet-stream', 'ACL': 'public-read'}) diff --git a/dcc/record.py b/dcc/record.py index 15aeea8..2c6aee2 100644 --- a/dcc/record.py +++ b/dcc/record.py @@ -4,6 +4,5 @@ import subprocess class Record(dcc.doom_base.WadMap): def take_action(self, parsed_args): - subprocess.run([dcc.config.DSDA] + - dcc.config.DsdaPreamble(parsed_args.wad, parsed_args.map) + - ["-record", dcc.config.DemoOutPath(parsed_args.wad, parsed_args.map, parsed_args.name)]) + subprocess.run([dcc.config.DSDA] + self.dsda_preamble() + + ["-record", self.demo_out_path()]) diff --git a/dcc/ss.py b/dcc/ss.py index 39061af..2579a3b 100644 --- a/dcc/ss.py +++ b/dcc/ss.py @@ -29,7 +29,7 @@ class SS(dcc.doom_base.WadMap): sys.exit("Gave up on image verification") if not accepted: return False - img.save(filename=dcc.config.BaseThumbPath(parsed_args.wad, parsed_args.map, parsed_args.name)) + img.save(filename=self.base_thumb_path()) return True while not try_screenshot(): diff --git a/dcc/text.py b/dcc/text.py index 5bd256c..d6497b2 100644 --- a/dcc/text.py +++ b/dcc/text.py @@ -31,4 +31,4 @@ class Text(dcc.doom_base.WadMap): draw(img) img.trim() img.reset_coords() - img.save(filename=dcc.config.TextThumbPath(parsed_args.wad, parsed_args.map, parsed_args.name)) + img.save(filename=self.text_thumb_path()) diff --git a/dcc/thumb.py b/dcc/thumb.py index 5982ef4..b0e1bcb 100644 --- a/dcc/thumb.py +++ b/dcc/thumb.py @@ -10,9 +10,9 @@ class Thumb(dcc.doom_base.WadMap): return parser def take_action(self, parsed_args): - base = dcc.config.BaseThumbPath(parsed_args.wad, parsed_args.map, parsed_args.name) - text = dcc.config.TextThumbPath(parsed_args.wad, parsed_args.map, parsed_args.name) - mdoom = dcc.config.MDoomPath(parsed_args.wad) + base = self.base_thumb_path() + text = self.text_thumb_path() + mdoom = self.m_doom_path() with wand.image.Image(filename=base) as bi, wand.color.Color("transparent") as tc: with wand.image.Image(filename=text) as ti: ti.border(tc, 5, 5) @@ -27,4 +27,4 @@ class Thumb(dcc.doom_base.WadMap): di.border(tc, 1, 1) bi.composite(di, gravity="north_east") - bi.save(filename=dcc.config.ThumbPath(parsed_args.wad, parsed_args.map, parsed_args.name)) + bi.save(filename=self.thumb_path())