Compare commits
No commits in common. "trunk" and "iss10" have entirely different histories.
27 changed files with 301 additions and 301 deletions
2
Makefile
2
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
|
||||
|
|
|
|||
5
dcc/__main__.py
Normal file
5
dcc/__main__.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import sys
|
||||
from dcc.main import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
|
|
@ -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,7 +113,10 @@ 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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -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:
|
||||
|
|
@ -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):
|
||||
|
|
@ -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:
|
||||
|
|
@ -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]
|
||||
)
|
||||
|
|
@ -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}"
|
||||
)
|
||||
|
|
@ -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):
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
72
dcc/main.py
Normal 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())
|
||||
|
|
@ -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()])
|
||||
|
|
@ -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())
|
||||
|
|
@ -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'
|
||||
}
|
||||
)
|
||||
|
|
@ -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):
|
||||
|
|
@ -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)]
|
||||
)
|
||||
|
|
@ -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")
|
||||
|
|
@ -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,7 +43,7 @@ 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)
|
||||
|
|
@ -51,10 +53,10 @@ def draw_text(self, img, text, font_size=64, wrap_dist=0.75):
|
|||
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? ")
|
||||
|
|
@ -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)
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
import sys
|
||||
from doomcc.main import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
|
|
@ -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())
|
||||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue