diff --git a/Makefile b/Makefile index d471b52..0b244ed 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,2 @@ install: - cp dist/pyinstaller/manylinux_2_39_x86_64/doomcc /home/tynan/.local/bin/dcc + cp dist/pyinstaller/manylinux_2_39_x86_64/dcc /home/tynan/.local/bin/dcc diff --git a/doomcc/__init__.py b/dcc/__init__.py similarity index 100% rename from doomcc/__init__.py rename to dcc/__init__.py diff --git a/dcc/__main__.py b/dcc/__main__.py new file mode 100644 index 0000000..dbb5b19 --- /dev/null +++ b/dcc/__main__.py @@ -0,0 +1,5 @@ +import sys +from dcc.main import main + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/doomcc/concat.py b/dcc/concat.py similarity index 79% rename from doomcc/concat.py rename to dcc/concat.py index 03a106b..4be4705 100644 --- a/doomcc/concat.py +++ b/dcc/concat.py @@ -1,6 +1,6 @@ import av import copy -import doomcc.doom_base +import dcc.doom_base import enum import fractions import io @@ -16,7 +16,7 @@ class State(enum.Enum): DONE = 3 -class Concat(doomcc.doom_base.Wad): +class Concat(dcc.doom_base.Wad): def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument("start_map") @@ -34,10 +34,12 @@ class Concat(doomcc.doom_base.Wad): + f"to{parsed_args.end_map}" ) output = av.open( - self.fabricate.joinpath(parsed_args.wad).joinpath(f"{fn_base}.mp4"), "w" + self.fabricate.joinpath(parsed_args.wad).joinpath( + f"{fn_base}.mp4"), "w" ) summary_file = open( - self.fabricate.joinpath(parsed_args.wad).joinpath(f"{fn_base}.txt"), "w" + self.fabricate.joinpath(parsed_args.wad).joinpath( + f"{fn_base}.txt"), "w" ) self._offset = 0 @@ -64,13 +66,11 @@ class Concat(doomcc.doom_base.Wad): start_time = self._offset / 1000000 text = self._add_chunk( videodir.joinpath(f"{parsed_args.wad}_map{idx}.mp4"), - output, - not parsed_args.nooverlay, + output, not parsed_args.nooverlay ) list.append( - summary, - f"{text} {math.floor(start_time / 60):02}:" - + f"{math.floor(start_time % 60):02}", + summary, f"{text} {math.floor(start_time / 60):02}:" + + f"{math.floor(start_time % 60):02}" ) if state == State.DONE: break @@ -82,7 +82,8 @@ class Concat(doomcc.doom_base.Wad): def _add_chunk(self, v, output, overlay): chunk = av.open(v) - if not (len(chunk.streams.video) == 1 and len(chunk.streams.audio) == 1): + if not (len(chunk.streams.video) == 1 + and len(chunk.streams.audio) == 1): raise Exception( f"irregular chunk {v}: streams {chunk.streams} " + f"(expected 1 video & 1 audio)" @@ -95,11 +96,16 @@ class Concat(doomcc.doom_base.Wad): text = "" if overlay: img = wand.image.Image( - height=chunk.streams[0].height, width=chunk.streams[0].width + height=chunk.streams[0].height, + width=chunk.streams[0].width ) mapstring = v.name[-6:-4] text = self._config["map_names"][f"map{mapstring}"] - self.draw_text(img, f"MAP{mapstring}: {text}", font_size=120) + self.draw_text( + img, + f"MAP{mapstring}: {text}", + font_size=120 + ) img.trim(reset_coords=True) img.border("graya(25%, 25%)", 10, 10) img.border(self.thumbnail_text_stroke, 16, 16) @@ -107,14 +113,17 @@ class Concat(doomcc.doom_base.Wad): # multiple of 8. dude whyyyyyyy padfactor = 8 img.border("transparent", padfactor, 0) - img.crop(width=img.width - img.width % padfactor, height=img.height) + img.crop( + width=img.width - img.width % padfactor, + height=img.height + ) if len(output.streams.get()) == 0: # We can't use the input stream as a template here; it doesn't # have everything needed to do encoding and will fail # mysteriously later. vs = chunk.streams.video[0] - vr = int(vs.time_base.denominator / vs.time_base.numerator) + vr = int(vs.time_base.denominator/vs.time_base.numerator) ovs = output.add_stream("h264", rate=vr) ovs.extradata = copy.deepcopy(vs.extradata) ovs.height = vs.height @@ -136,10 +145,12 @@ class Concat(doomcc.doom_base.Wad): oas.bit_rate = astr.bit_rate src = ograph.add_buffer( - template=chunk.streams.video[0], time_base=chunk.streams.video[0].time_base + template=chunk.streams.video[0], + time_base=chunk.streams.video[0].time_base ) asrc = ograph.add_abuffer( - template=chunk.streams.audio[0], time_base=chunk.streams.audio[0].time_base + template=chunk.streams.audio[0], + time_base=chunk.streams.audio[0].time_base ) # TODO: video fades are absolute relative to the input video; audio # fades need to have their timestamps offset by the position in the @@ -152,18 +163,19 @@ class Concat(doomcc.doom_base.Wad): iafade_start = self._offset * sample_rate / 1000000 iafade = ograph.add("afade", args=f"in:{iafade_start}:{sample_rate}") oafade_start = ( - self._offset + chunk.duration - ) * sample_rate / 1000000 - sample_rate + (self._offset + chunk.duration) * sample_rate / 1000000 + - sample_rate + ) oafade = ograph.add("afade", args=f"out:{oafade_start}:{sample_rate}") if overlay: overlay = ograph.add_buffer( - width=img.width, - height=img.height, - format="rgba", - time_base=chunk.streams.video[0].time_base, + width=img.width, height=img.height, + format="rgba", time_base=chunk.streams.video[0].time_base + ) + overlay_fo = ograph.add( + "fade", args=f"out:{4 * frame_rate}:{frame_rate}" ) - overlay_fo = ograph.add("fade", args=f"out:{4 * frame_rate}:{frame_rate}") overlay.link_to(overlay_fo, 0, 0) composite = ograph.add("overlay", args="x=4:y=4") src.link_to(composite, 0, 0) @@ -182,8 +194,9 @@ class Concat(doomcc.doom_base.Wad): for packet in chunk.demux(): if packet.dts is None: continue - pof = (self._offset * packet.time_base.denominator) / ( - packet.time_base.numerator * 1000000 + pof = ( + (self._offset * packet.time_base.denominator) + / (packet.time_base.numerator * 1000000) ) packet.dts += pof packet.pts += pof @@ -208,7 +221,9 @@ class Concat(doomcc.doom_base.Wad): def _make_text_frame(self, img, ifr): # We need to give each frame its own memory it can own. - text_frame = av.video.frame.VideoFrame(img.width, img.height, format="rgba") + text_frame = av.video.frame.VideoFrame( + img.width, img.height, format="rgba" + ) text_frame.planes[0].update(img.make_blob(format="rgba")) text_frame.pts = ifr.pts text_frame.dts = ifr.dts diff --git a/doomcc/config.py b/dcc/config.py similarity index 84% rename from doomcc/config.py rename to dcc/config.py index 9aae491..03243b6 100644 --- a/doomcc/config.py +++ b/dcc/config.py @@ -12,15 +12,13 @@ class ConfigBase(object): self._doom = pathlib.Path(parsed_args.doom) self._config_name = parsed_args.config_name self._config = tomlkit.toml_file.TOMLFile( - self.doom.joinpath(self.config_name) - ).read() + self.doom.joinpath(self.config_name)).read() self._dsda = self._config.get("dsda") if self._dsda is None: raise Exception( "required key 'dsda' not set in config " - + f"{self.doom.joinpath(self.config_name)}." - ) + + f"{self.doom.joinpath(self.config_name)}.") for d in ("iwads", "pwads", "demos", "fabricate"): self._init_attr([d], d, fn=self.doom.joinpath) @@ -31,7 +29,10 @@ class ConfigBase(object): self._init_attr(["thumbnail", "text_fill"], "white") self._init_attr(["thumbnail", "text_stroke"], "red") self._init_attr(["thumbnail", "overlay_name"], "M_DOOM_scaled.png") - self._init_attr(["fetch", "mirror"], "https://youfailit.net/pub/idgames") + self._init_attr( + ["fetch", "mirror"], + "https://youfailit.net/pub/idgames" + ) def _init_attr(self, what, default, fn=lambda x: x): propname = "_".join(what) @@ -44,7 +45,8 @@ class ConfigBase(object): setattr(self, f"_{propname}", fn(val)) setattr( - type(self), propname, property(lambda self: getattr(self, f"_{propname}")) + type(self), propname, + property(lambda self: getattr(self, f"_{propname}")) ) @property @@ -63,10 +65,10 @@ class ConfigBase(object): def get_parser_func(toc): def add_common_args(self, prog_name): parser = super(toc, self).get_parser(prog_name) - parser.add_argument("--doom", default=pathlib.Path.home().joinpath("doom")) + parser.add_argument( + "--doom", default=pathlib.Path.home().joinpath("doom")) parser.add_argument("--config-name", default="config.toml") return parser - return add_common_args diff --git a/doomcc/configure.py b/dcc/configure.py similarity index 85% rename from doomcc/configure.py rename to dcc/configure.py index 080f613..31ed4b5 100644 --- a/doomcc/configure.py +++ b/dcc/configure.py @@ -1,9 +1,9 @@ -import doomcc.doom_base +import dcc.doom_base import omg import tomlkit.toml_file -class Configure(doomcc.doom_base.Wad): +class Configure(dcc.doom_base.Wad): def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument("--complevel", "--cl") @@ -22,10 +22,12 @@ class Configure(doomcc.doom_base.Wad): wad = omg.WadIO(w) complevel = wad.read("COMPLVL").decode("ascii").strip() except Exception as e: - print(f"Wad {w} likely has no lump COMPLVL (exception {e})") + print( + f"Wad {w} likely has no lump COMPLVL (exception {e})") if complevel is None: complevel = input("Complevel? ") + doc = tomlkit.document() doc.add("complevel", complevel) if parsed_args.iwad is not None: diff --git a/doomcc/doom_base.py b/dcc/doom_base.py similarity index 79% rename from doomcc/doom_base.py rename to dcc/doom_base.py index c12a867..aedae79 100644 --- a/doomcc/doom_base.py +++ b/dcc/doom_base.py @@ -1,13 +1,13 @@ from abc import abstractmethod from cliff.command import Command -import doomcc.config +import dcc.config import io import os import re import tomlkit -class Wad(doomcc.config.Base): +class Wad(dcc.config.Base): def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument("wad") @@ -70,8 +70,9 @@ class Wad(doomcc.config.Base): return [] def thumb_overlay_path(self): - return self._ensure(self.fabricate.joinpath(self.wad)).joinpath( - self.thumbnail_overlay_name + return ( + self._ensure(self.fabricate.joinpath(self.wad)) + .joinpath(self.thumbnail_overlay_name) ) @@ -128,51 +129,55 @@ class WadMap(Wad): def demo_in_path(self): candidates = [ - x for x in self.demos.joinpath(self.wad).glob(self._file_base("*.lmp")) + x for x in self.demos.joinpath(self.wad) + .glob(self._file_base("*.lmp")) ] if len(candidates) == 0: raise Exception( - "no suitable demo candidates for WAD {} MAP {}{}.".format( - self.wad, - self.map, - f" name {self._name}" if self._name else "", - ) + "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 self._ensure(self.demos.joinpath(self.wad)).joinpath( - self._file_base(".txt") + return ( + self._ensure(self.demos.joinpath(self.wad)) + .joinpath(self._file_base(".txt")) ) def video_path(self): - return self._ensure(self.fabricate.joinpath(self.wad)).joinpath( - self._file_base(".mp4") + return ( + self._ensure(self.fabricate.joinpath(self.wad)) + .joinpath(self._file_base(".mp4")) ) def demo_out_path(self): - return self._ensure(self.demos.joinpath(self.wad)).joinpath( - self._file_base(".lmp") + return ( + self._ensure(self.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 self._ensure(self.fabricate.joinpath(self.wad)).joinpath( - self._file_base("_base.png") + return ( + self._ensure(self.fabricate.joinpath(self.wad)) + .joinpath(self._file_base("_base.png")) ) def text_thumb_path(self): - return self._ensure(self.fabricate.joinpath(self.wad)).joinpath( - self._file_base("_text.png") + return ( + self._ensure(self.fabricate.joinpath(self.wad)) + .joinpath(self._file_base("_text.png")) ) def thumb_path(self): - return self._ensure(self.fabricate.joinpath(self.wad)).joinpath( - self._file_base("_thumb.png") + return ( + self._ensure(self.fabricate.joinpath(self.wad)) + .joinpath(self._file_base("_thumb.png")) ) def _file_base(self, ext): diff --git a/doomcc/dsda.py b/dcc/dsda.py similarity index 83% rename from doomcc/dsda.py rename to dcc/dsda.py index 932d855..8fb1e00 100644 --- a/doomcc/dsda.py +++ b/dcc/dsda.py @@ -1,5 +1,5 @@ -import doomcc.config -import doomcc.doom_base +import dcc.config +import dcc.doom_base import os import re import shutil @@ -7,7 +7,7 @@ import subprocess import zipfile -class DSDA(doomcc.doom_base.WadMap): +class DSDA(dcc.doom_base.WadMap): def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument("-s", "--single", action="store_true") @@ -22,9 +22,10 @@ class DSDA(doomcc.doom_base.WadMap): if shutil.which("xvfb-run") is not None: command = ["xvfb-run"] + command subprocess.run( - command - + self.dsda_preamble(warp=False) - + ["-fastdemo", dip, "-nosound", "-skiptic", "-1", "-export_text_file"] + command + self.dsda_preamble(warp=False) + [ + "-fastdemo", dip, "-nosound", + "-skiptic", "-1", "-export_text_file" + ] ) editor = "nano" if "EDITOR" in os.environ: @@ -37,7 +38,7 @@ class DSDA(doomcc.doom_base.WadMap): else: fh1 = self.wad[0:2] + self.map if parsed_args.single: - fh1 = self.wad[0 : min(len(self.wad), 4)] + fh1 = self.wad[0:min(len(self.wad), 4)] fh2 = "" with open(dtp, mode="r") as f: for line in f: @@ -45,7 +46,7 @@ class DSDA(doomcc.doom_base.WadMap): m = re.search("[^0-9]*([0-9]*):([0-9]*).[0-9]*", line) if m is None: continue - fh2 = m[1] + m[2] + fh2 = m[1]+m[2] if len(fh2) % 2 == 1: fh2 = "0" + fh2 break diff --git a/doomcc/eureka.py b/dcc/eureka.py similarity index 70% rename from doomcc/eureka.py rename to dcc/eureka.py index 8618287..4d7b9cd 100644 --- a/doomcc/eureka.py +++ b/dcc/eureka.py @@ -1,9 +1,9 @@ -import doomcc.doom_base -import doomcc.config +import dcc.doom_base +import dcc.config import os -class Eureka(doomcc.doom_base.WadMap): +class Eureka(dcc.doom_base.WadMap): def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument("--main") @@ -24,11 +24,7 @@ class Eureka(doomcc.doom_base.WadMap): if complevel == "11" or complevel == "21": port = "mbf" - os.execvp( - "eureka", - ["eureka"] - + ["-iwad", iwad] - + ["-w", parsed_args.map] - + ["-p", port] - + [mw], + os.execvp("eureka", + ["eureka"] + ["-iwad", iwad] + ["-w", parsed_args.map] + + ["-p", port] + [mw] ) diff --git a/doomcc/extract.py b/dcc/extract.py similarity index 79% rename from doomcc/extract.py rename to dcc/extract.py index d0412ea..95228f7 100644 --- a/doomcc/extract.py +++ b/dcc/extract.py @@ -1,14 +1,14 @@ -import doomcc.doom_base +import dcc.doom_base import omg import numpy as np import wand.color import wand.image -class Extract(doomcc.doom_base.Wad): +class Extract(dcc.doom_base.Wad): def get_parser(self, prog_name): parser = super().get_parser(prog_name) - parser.add_argument("lump") + parser.add_argument('lump') return parser def take_action(self, parsed_args): @@ -24,9 +24,8 @@ class Extract(doomcc.doom_base.Wad): ) as img: img.transparent_color(wand.color.Color("#ff00ff"), 0.0) img.save( - filename=self.fabricate.joinpath(parsed_args.wad).joinpath( - parsed_args.lump + ".png" - ) + filename=self.fabricate.joinpath(parsed_args.wad) + .joinpath(parsed_args.lump + ".png") ) return except Exception as e: @@ -36,5 +35,6 @@ class Extract(doomcc.doom_base.Wad): ) print( - f"Lump {parsed_args.lump} not found in any wad in " + f"{parsed_args.wad}" + f"Lump {parsed_args.lump} not found in any wad in " + + f"{parsed_args.wad}" ) diff --git a/doomcc/fabricate.py b/dcc/fabricate.py similarity index 74% rename from doomcc/fabricate.py rename to dcc/fabricate.py index 36f81d5..8c279ef 100644 --- a/doomcc/fabricate.py +++ b/dcc/fabricate.py @@ -1,12 +1,12 @@ import contextlib -import doomcc.config -import doomcc.doom_base +import dcc.config +import dcc.doom_base import os import shutil import tempfile -class Fabricate(doomcc.doom_base.WadMap): +class Fabricate(dcc.doom_base.WadMap): def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument("--fg", action="store_true") @@ -18,12 +18,10 @@ class Fabricate(doomcc.doom_base.WadMap): command = [self.dsda] if not parsed_args.fg and shutil.which("xvfb-run") is not None: command = ["xvfb-run"] + command - os.execvp( - command[0], - command - + self.dsda_preamble() + os.execvp(command[0], + command + self.dsda_preamble() + ["-timedemo", self.demo_in_path()] - + ["-viddump", self.video_path()], + + ["-viddump", self.video_path()] ) def options_dict(self): diff --git a/doomcc/fetch.py b/dcc/fetch.py similarity index 76% rename from doomcc/fetch.py rename to dcc/fetch.py index 3e5e411..fd53f55 100644 --- a/doomcc/fetch.py +++ b/dcc/fetch.py @@ -1,4 +1,4 @@ -import doomcc.config +import dcc.config import io import json import pathlib @@ -9,7 +9,7 @@ import urllib.request import zipfile -class Fetch(doomcc.config.Base): +class Fetch(dcc.config.Base): def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument("id_or_name") @@ -21,12 +21,14 @@ class Fetch(doomcc.config.Base): idgames_id = self.search_idgames(parsed_args.id_or_name) reply = self.fetch_url( - "https://www.doomworld.com/idgames/api/" - + "api.php?action=get&id={}&out=json".format(idgames_id) - ) - rpath = "/".join( - [self.fetch_mirror, reply["content"]["dir"], reply["content"]["filename"]] + "https://www.doomworld.com/idgames/api/" + + "api.php?action=get&id={}&out=json".format(idgames_id) ) + rpath = "/".join([ + self.fetch_mirror, + reply["content"]["dir"], + reply["content"]["filename"] + ]) wad = reply["content"]["filename"][0:-4] with urllib.request.urlopen(rpath) as response: @@ -36,8 +38,8 @@ class Fetch(doomcc.config.Base): # TODO: explicit error handling. Let users choose when >1 result. def search_idgames(self, wad): reply = self.fetch_url( - "https://www.doomworld.com/idgames/api/" - + "api.php?action=search&query={}&out=json".format(wad) + "https://www.doomworld.com/idgames/api/" + + "api.php?action=search&query={}&out=json".format(wad) ) if "content" not in reply: sys.exit(f"No WAD named {wad} found on idgames.") @@ -60,5 +62,8 @@ class Fetch(doomcc.config.Base): if fetcher_path is None: raise Exception(f"Fetch util {fetcher} not found on PATH.") - proc = subprocess.run([fetcher_path, url], capture_output=True, check=True) + proc = subprocess.run( + [fetcher_path, url], + capture_output=True, check=True + ) return json.loads(proc.stdout) diff --git a/doomcc/ls.py b/dcc/ls.py similarity index 59% rename from doomcc/ls.py rename to dcc/ls.py index ffb8f70..b7fb0ce 100644 --- a/doomcc/ls.py +++ b/dcc/ls.py @@ -1,8 +1,8 @@ -import doomcc.config +import dcc.config import os -class List(doomcc.config.ListerBase): +class List(dcc.config.ListerBase): def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument("target") @@ -12,19 +12,22 @@ class List(doomcc.config.ListerBase): match parsed_args.target: case "pwads": return ( - ("pwads",), - sorted((x.name,) for x in os.scandir(self.pwads) if x.is_dir()), + ("pwads",), sorted( + (x.name,) for x in os.scandir(self.pwads) if x.is_dir() + ) ) case "iwads": return ( - ("iwads",), - sorted((x.name,) for x in os.scandir(self.iwads) if x.is_file()), + ("iwads",), sorted( + (x.name,) for x in + os.scandir(self.iwads) if x.is_file() + ) ) case _: raise Exception(f"unknown target {parsed_args.target}") -class WadList(doomcc.config.ListerBase): +class WadList(dcc.config.ListerBase): def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument("wad") @@ -32,12 +35,11 @@ class WadList(doomcc.config.ListerBase): def _get_results(self, name, base, wad, ext): return ( - (name,), - sorted( - (x.name,) - for x in os.scandir(base.joinpath(wad)) + (name,), sorted( + (x.name,) for x in + os.scandir(base.joinpath(wad)) if x.name.endswith(ext) - ), + ) ) @@ -48,4 +50,6 @@ class ListDemos(WadList): class ListVideos(WadList): def take_action(self, parsed_args): - return self._get_results("videos", self.fabricate, parsed_args.wad, ".mp4") + return self._get_results( + "videos", self.fabricate, parsed_args.wad, ".mp4" + ) diff --git a/dcc/main.py b/dcc/main.py new file mode 100644 index 0000000..7fb436b --- /dev/null +++ b/dcc/main.py @@ -0,0 +1,72 @@ +import sys + +from cliff.app import App +from cliff.commandmanager import CommandManager + +import dcc.concat +import dcc.configure +import dcc.dsda +import dcc.eureka +import dcc.extract +import dcc.fabricate +import dcc.fetch +import dcc.ls +import dcc.pb +import dcc.play +import dcc.put +import dcc.record +import dcc.rib +import dcc.ss +import dcc.text +import dcc.thumb + + +class DCC(App): + def __init__(self): + cm = CommandManager(None) + commands = { + "concat": dcc.concat.Concat, + "configure": dcc.configure.Configure, + "dsda": dcc.dsda.DSDA, + "eureka": dcc.eureka.Eureka, + "extract": dcc.extract.Extract, + "fabricate": dcc.fabricate.Fabricate, + "fetch": dcc.fetch.Fetch, + "ls": dcc.ls.List, + "ls demos": dcc.ls.ListDemos, + "ls videos": dcc.ls.ListVideos, + "pb": dcc.pb.PB, + "play": dcc.play.Play, + "put": dcc.put.Put, + "record": dcc.record.Record, + "rib": dcc.rib.RIB, + "ss": dcc.ss.SS, + "text": dcc.text.Text, + "thumb": dcc.thumb.Thumb, + } + for n, c in commands.items(): + cm.add_command(n, c) + super().__init__( + description="Doom Command Center", + version="0.0.1", + command_manager=cm, + deferred_help=True, + ) + + def initialize_app(self, argv): + pass + + def prepare_to_run_command(self, cmd): + pass + + def clean_up(self, cmd, result, err): + pass + + +def main(argv=sys.argv[1:]): + dcc = DCC() + return dcc.run(argv) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/doomcc/pb.py b/dcc/pb.py similarity index 58% rename from doomcc/pb.py rename to dcc/pb.py index 841aba7..661d538 100644 --- a/doomcc/pb.py +++ b/dcc/pb.py @@ -1,8 +1,8 @@ -import doomcc.config -import doomcc.doom_base +import dcc.config +import dcc.doom_base import os -class PB(doomcc.doom_base.WadMap): +class PB(dcc.doom_base.WadMap): def take_action(self, parsed_args): os.execvp("ffplay", ["ffplay", self.video_path()]) diff --git a/doomcc/play.py b/dcc/play.py similarity index 58% rename from doomcc/play.py rename to dcc/play.py index ee135ca..5459b6e 100644 --- a/doomcc/play.py +++ b/dcc/play.py @@ -1,8 +1,8 @@ -import doomcc.config -import doomcc.doom_base +import dcc.config +import dcc.doom_base import os -class Play(doomcc.doom_base.WadMap): +class Play(dcc.doom_base.WadMap): def take_action(self, parsed_args): os.execv(self.dsda, [self.dsda] + self.dsda_preamble()) diff --git a/doomcc/put.py b/dcc/put.py similarity index 58% rename from doomcc/put.py rename to dcc/put.py index 77215b4..59e1dbc 100644 --- a/doomcc/put.py +++ b/dcc/put.py @@ -1,22 +1,23 @@ import boto3 -import doomcc.config -import doomcc.doom_base +import dcc.config +import dcc.doom_base -class Put(doomcc.doom_base.WadMap): +class Put(dcc.doom_base.WadMap): def get_parser(self, prog_name): parser = super().get_parser(prog_name) return parser # TODO: accept configuration for bucket name def take_action(self, parsed_args): - s3_client = boto3.client("s3") + s3_client = boto3.client('s3') 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"}, + demo, 'yrriban', bucket, + ExtraArgs={ + 'ContentType': 'binary/octet-stream', + 'ACL': 'public-read' + } ) diff --git a/doomcc/record.py b/dcc/record.py similarity index 54% rename from doomcc/record.py rename to dcc/record.py index 55e5af2..21a5bd5 100644 --- a/doomcc/record.py +++ b/dcc/record.py @@ -1,13 +1,13 @@ -import doomcc.config -import doomcc.doom_base +import dcc.config +import dcc.doom_base import os -class Record(doomcc.doom_base.WadMap): +class Record(dcc.doom_base.WadMap): def take_action(self, parsed_args): - os.execv( - self.dsda, - [self.dsda] + self.dsda_preamble() + ["-record", self.demo_out_path()], + os.execv(self.dsda, + [self.dsda] + self.dsda_preamble() + + ["-record", self.demo_out_path()] ) def options_dict(self): diff --git a/doomcc/rib.py b/dcc/rib.py similarity index 75% rename from doomcc/rib.py rename to dcc/rib.py index 6a456f7..ffcc16a 100644 --- a/doomcc/rib.py +++ b/dcc/rib.py @@ -1,10 +1,10 @@ -import doomcc.doom_base +import dcc.doom_base import pathlib import os import time -class RIB(doomcc.doom_base.WadMap): +class RIB(dcc.doom_base.WadMap): def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument("-s", "--secs_before", type=int, default=10) @@ -16,10 +16,10 @@ class RIB(doomcc.doom_base.WadMap): demo = "" dt = 0 demodir = ( - pathlib.Path.home() - / ".dsda-doom" - / "dsda_doom_data" - / self.iwad_path.stem.lower() + pathlib.Path.home() / + ".dsda-doom" / + "dsda_doom_data" / + self.iwad_path.stem.lower() ) for w in self.load_order(): demodir = demodir.joinpath(w.stem.lower()) @@ -41,10 +41,8 @@ class RIB(doomcc.doom_base.WadMap): + f"and map {parsed_args.map} (tried to look in {demodir})" ) - os.execv( - self.dsda, - [self.dsda] - + self.dsda_preamble(warp=False) + os.execv(self.dsda, + [self.dsda] + self.dsda_preamble(warp=False) + ["-playdemo", demo] - + ["-skiptic", str(-35 * parsed_args.secs_before)], + + ["-skiptic", str(-35 * parsed_args.secs_before)] ) diff --git a/doomcc/ss.py b/dcc/ss.py similarity index 81% rename from doomcc/ss.py rename to dcc/ss.py index 835dd00..de610ff 100644 --- a/doomcc/ss.py +++ b/dcc/ss.py @@ -1,12 +1,12 @@ from tkinter import messagebox -import doomcc.config -import doomcc.doom_base +import dcc.config +import dcc.doom_base import sys import wand.display import wand.image -class SS(doomcc.doom_base.WadMap): +class SS(dcc.doom_base.WadMap): def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument("-g", "--gravity", default="center") @@ -22,11 +22,11 @@ class SS(doomcc.doom_base.WadMap): height = self.thumbnail_height with wand.image.Image(width=width, height=height, pseudo="x:") as img: img.reset_coords() - if img.width < width or img.height < height: + if (img.width < width or img.height < height): if not messagebox.askretrycancel( - title="Doom Command Center", - message=f"Image too small ({img.width}x{img.height})." - + "Try again?", + title="DCC", + message=f"Image too small ({img.width}x{img.height})." + + "Try again?" ): sys.exit("Gave up trying to select an image.") return False @@ -35,7 +35,7 @@ class SS(doomcc.doom_base.WadMap): if not yolo: wand.display.display(img) accepted = messagebox.askyesnocancel( - title="Doom Command Center", message="Is this image acceptable?" + title="DCC", message="Is this image acceptable?" ) if accepted is None: sys.exit("Gave up on image verification") diff --git a/doomcc/text.py b/dcc/text.py similarity index 83% rename from doomcc/text.py rename to dcc/text.py index 421f37d..a93d72a 100644 --- a/doomcc/text.py +++ b/dcc/text.py @@ -1,5 +1,5 @@ -import doomcc.config -import doomcc.doom_base +import dcc.config +import dcc.doom_base import sys import textwrap import wand.drawing @@ -30,7 +30,9 @@ def draw_text(self, img, text, font_size=64, wrap_dist=0.75): columns = len(wrapped_text) while columns > 0: columns -= 1 - wrapped_text = "\n".join(textwrap.wrap(wrapped_text, columns)) + wrapped_text = '\n'.join( + textwrap.wrap(wrapped_text, columns) + ) wrapped_width, _ = eval_metrics(wrapped_text) if wrapped_width <= target_width: break @@ -41,20 +43,20 @@ def draw_text(self, img, text, font_size=64, wrap_dist=0.75): ) textlines[idx] = wrapped_text - wrapped_text = "\n".join(textlines) + wrapped_text = '\n'.join(textlines) draw.text(5, int(draw.font_size) + 5, wrapped_text) draw(img) draw.stroke_color = wand.color.Color("none") draw.stroke_width = 0 - draw.text(5, int(draw.font_size) + 5, wrapped_text) + draw.text(5, int(draw.font_size)+5, wrapped_text) draw(img) -doomcc.config.Base.draw_text = draw_text +dcc.config.Base.draw_text = draw_text -class Text(doomcc.doom_base.WadMap): +class Text(dcc.doom_base.WadMap): def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument("--nomap", action="store_true") @@ -72,9 +74,12 @@ class Text(doomcc.doom_base.WadMap): text += "\n" text = "{}{}".format(text, parsed_args.demotype) with wand.image.Image( - height=self.thumbnail_height, width=self.thumbnail_width + height=self.thumbnail_height, + width=self.thumbnail_width ) as img: - self.draw_text(img, text, wrap_dist=0.95) + self.draw_text( + img, text, wrap_dist=0.95 + ) img.trim() img.reset_coords() img.save(filename=self.text_thumb_path()) @@ -86,7 +91,7 @@ class Text(doomcc.doom_base.WadMap): map_names = self._config.get("map_names") if map_names is not None: text = map_names.get(f"map{mapnum}") - if text is not None: + if text != "": return text return input("Map Name? ") diff --git a/doomcc/thumb.py b/dcc/thumb.py similarity index 89% rename from doomcc/thumb.py rename to dcc/thumb.py index 5ee8265..01a0c39 100644 --- a/doomcc/thumb.py +++ b/dcc/thumb.py @@ -1,10 +1,10 @@ -import doomcc.config -import doomcc.doom_base +import dcc.config +import dcc.doom_base import wand.color import wand.image -class Thumb(doomcc.doom_base.WadMap): +class Thumb(dcc.doom_base.WadMap): def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument("--index", action="store_true") @@ -17,7 +17,7 @@ class Thumb(doomcc.doom_base.WadMap): overlay = self.thumb_overlay_path() with ( wand.image.Image(filename=base) as bi, - wand.color.Color("transparent") as tc, + wand.color.Color("transparent") as tc ): with wand.image.Image(filename=text) as ti: ti.border(tc, 5, 5) diff --git a/doomcc/__main__.py b/doomcc/__main__.py deleted file mode 100644 index 21747bf..0000000 --- a/doomcc/__main__.py +++ /dev/null @@ -1,5 +0,0 @@ -import sys -from doomcc.main import main - -if __name__ == "__main__": - sys.exit(main(sys.argv[1:])) diff --git a/doomcc/main.py b/doomcc/main.py deleted file mode 100644 index 745e1d7..0000000 --- a/doomcc/main.py +++ /dev/null @@ -1,72 +0,0 @@ -import sys - -from cliff.app import App -from cliff.commandmanager import CommandManager - -import doomcc.concat -import doomcc.configure -import doomcc.dsda -import doomcc.eureka -import doomcc.extract -import doomcc.fabricate -import doomcc.fetch -import doomcc.ls -import doomcc.pb -import doomcc.play -import doomcc.put -import doomcc.record -import doomcc.rib -import doomcc.ss -import doomcc.text -import doomcc.thumb - - -class DoomCC(App): - def __init__(self): - cm = CommandManager(None) - commands = { - "concat": doomcc.concat.Concat, - "configure": doomcc.configure.Configure, - "dsda": doomcc.dsda.DSDA, - "eureka": doomcc.eureka.Eureka, - "extract": doomcc.extract.Extract, - "fabricate": doomcc.fabricate.Fabricate, - "fetch": doomcc.fetch.Fetch, - "ls": doomcc.ls.List, - "ls demos": doomcc.ls.ListDemos, - "ls videos": doomcc.ls.ListVideos, - "pb": doomcc.pb.PB, - "play": doomcc.play.Play, - "put": doomcc.put.Put, - "record": doomcc.record.Record, - "rib": doomcc.rib.RIB, - "ss": doomcc.ss.SS, - "text": doomcc.text.Text, - "thumb": doomcc.thumb.Thumb, - } - for n, c in commands.items(): - cm.add_command(n, c) - super().__init__( - description="Doom Command Center", - version="0.0.1", - command_manager=cm, - deferred_help=True, - ) - - def initialize_app(self, argv): - pass - - def prepare_to_run_command(self, cmd): - pass - - def clean_up(self, cmd, result, err): - pass - - -def main(argv=sys.argv[1:]): - doomcc = DoomCC() - return doomcc.run(argv) - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/pyproject.toml b/pyproject.toml index 520742e..891d8db 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,11 +1,11 @@ [project] -name = "doomcc" +name = "dcc" version = "0.1.0" description = "Doom Command Center" authors = [ {name = "yrriban",email = "yrriban@gmail.com"} ] -license = "MIT" +license = {text = "MIT"} readme = "README.md" requires-python = ">=3.12" dependencies = [ @@ -27,10 +27,10 @@ requires = ["poetry-core>=2.0.0,<3.0.0"] build-backend = "poetry.core.masonry.api" [project.scripts] -doomcc = "doomcc.main:main" +dcc = "dcc.main:main" [tool.poetry-pyinstaller-plugin.scripts] -doomcc = { source = "doomcc/main.py", type = "onefile" } +dcc = { source = "dcc/main.py", type = "onefile" } [tool.poetry-pyinstaller-plugin.collect] all = ["cliff"] diff --git a/tests/doom_base_test.py b/tests/doom_base_test.py deleted file mode 100644 index 5510fee..0000000 --- a/tests/doom_base_test.py +++ /dev/null @@ -1,28 +0,0 @@ -import doomcc.doom_base -import os -import pathlib -import pytest -import tempfile - -class Workbench: - pass - -def test_demo_in_path(): - w = Workbench() - w.demo_in_path = lambda: doomcc.doom_base.WadMap.demo_in_path(w) - w._file_base = lambda path: doomcc.doom_base.WadMap._file_base(w, path) - w.wad = pathlib.Path("scythe") - w.map = "01" - w.name_string = "_index" - w._name = "index" - with tempfile.TemporaryDirectory() as td: - w.demos = pathlib.Path(td) - with pytest.raises(Exception): - w.demo_in_path() - - dp = pathlib.Path(td).joinpath("scythe") - os.mkdir(dp) - dp.joinpath("scythe_map01_index.lmp").touch() - assert w.demo_in_path() == dp.joinpath("scythe_map01_index.lmp") - dp.joinpath("scythe_map01_index-00028.lmp").touch() - assert w.demo_in_path() == dp.joinpath("scythe_map01_index-00028.lmp") diff --git a/tests/play_test.py b/tests/play_test.py index e778251..1fa3956 100644 --- a/tests/play_test.py +++ b/tests/play_test.py @@ -1,5 +1,5 @@ import cliff.app -import doomcc.play +import dcc.play import logging import os import mockito.matchers @@ -26,30 +26,26 @@ def test_play(expect): scp.mkdir() scp.joinpath("config.toml").touch() scp.joinpath("scythe.wad").touch() - doomcc.play.Play.__init__ = lambda self: None - doomcc.play.Play.get_epilog = lambda self: "" - rig = doomcc.play.Play() + dcc.play.Play.__init__ = lambda self: None + dcc.play.Play.get_epilog = lambda self: "" + rig = dcc.play.Play() rig._hooks = [] parser = rig.get_parser("test_play") parsed_args = parser.parse_args(args=["--doom", td, "scythe", "01"]) - with ( - expect(os, times=1) - .execv( + with expect(os, times=1).execv( + tdp.joinpath("dsda-doom").joinpath("exe"), + [ tdp.joinpath("dsda-doom").joinpath("exe"), - [ - tdp.joinpath("dsda-doom").joinpath("exe"), - "-iwad", - tdp.joinpath("iwads").joinpath("DOOM2.WAD"), - "-file", - tdp.joinpath("pwads").joinpath("scythe").joinpath("scythe.wad"), - "-complevel", - "2", - "-skill", - "4", - "-warp", - "01", - ], - ) - .thenReturn(None) - ): + "-iwad", + tdp.joinpath("iwads").joinpath("DOOM2.WAD"), + "-file", + tdp.joinpath("pwads").joinpath("scythe").joinpath("scythe.wad"), + "-complevel", + "2", + "-skill", + "4", + "-warp", + "01", + ] + ).thenReturn(None): assert rig.run(parsed_args) is None