Compare commits

..

No commits in common. "trunk" and "iss10" have entirely different histories.
trunk ... iss10

27 changed files with 301 additions and 301 deletions

View file

@ -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

5
dcc/__main__.py Normal file
View file

@ -0,0 +1,5 @@
import sys
from dcc.main import main
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))

View file

@ -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

View file

@ -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

View file

@ -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:

View file

@ -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):

View file

@ -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

View file

@ -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]
)

View file

@ -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}"
)

View file

@ -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):

View file

@ -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)

View file

@ -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"
)

72
dcc/main.py Normal file
View file

@ -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())

View file

@ -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()])

View file

@ -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())

View file

@ -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'
}
)

View file

@ -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):

View file

@ -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)]
)

View file

@ -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")

View file

@ -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? ")

View file

@ -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)

View file

@ -1,5 +0,0 @@
import sys
from doomcc.main import main
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))

View file

@ -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())

View file

@ -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"]

View file

@ -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")

View file

@ -1,5 +1,5 @@
import cliff.app
import doomcc.play
import dcc.play
import logging
import os
import mockito.matchers
@ -26,15 +26,13 @@ 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"),
@ -48,8 +46,6 @@ def test_play(expect):
"4",
"-warp",
"01",
],
)
.thenReturn(None)
):
]
).thenReturn(None):
assert rig.run(parsed_args) is None