From 8c1d031defb6b0188d469489375c907355d50bc9 Mon Sep 17 00:00:00 2001 From: yrriban Date: Wed, 11 Jun 2025 02:45:17 -0400 Subject: [PATCH 1/9] Support excluding the overlay from concatenated videos. --- dcc/concat.py | 58 ++++++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/dcc/concat.py b/dcc/concat.py index 46df15a..660d3d0 100644 --- a/dcc/concat.py +++ b/dcc/concat.py @@ -13,6 +13,7 @@ class Concat(dcc.doom_base.Wad): parser = super().get_parser(prog_name) parser.add_argument("start_map") parser.add_argument("end_map") + parser.add_argument("-n", "--nooverlay", action="store_true") return parser def take_action(self, parsed_args): @@ -38,19 +39,20 @@ class Concat(dcc.doom_base.Wad): sink = ograph.add("buffersink") asink = ograph.add("abuffersink") - img = wand.image.Image(height=chunk.streams[0].height,width=chunk.streams[0].width) - mapstring = v.name[-6:-4] - text = self._config["map_names"][f"map{mapstring}"] - dcc.text.draw_text(img, f"MAP{mapstring}: {text}", font_size=120) - img.trim(reset_coords=True) - img.border("graya(25%, 25%)", 10, 10) - img.border(dcc.config.TEXT_STROKE_COLOR, 16, 16) - # for this to work... the image needs to have a width that's a multiple - # of 8. dude whyyyyyyy - padfactor=8 - img.border("transparent", padfactor, 0) - img.crop(width=img.width-img.width%padfactor, height=img.height) - text_frame = av.video.frame.VideoFrame(img.width, img.height, format="rgba") + if not parsed_args.nooverlay: + img = wand.image.Image(height=chunk.streams[0].height,width=chunk.streams[0].width) + mapstring = v.name[-6:-4] + text = self._config["map_names"][f"map{mapstring}"] + dcc.text.draw_text(img, f"MAP{mapstring}: {text}", font_size=120) + img.trim(reset_coords=True) + img.border("graya(25%, 25%)", 10, 10) + img.border(dcc.config.TEXT_STROKE_COLOR, 16, 16) + # for this to work... the image needs to have a width that's a multiple + # of 8. dude whyyyyyyy + padfactor=8 + img.border("transparent", padfactor, 0) + img.crop(width=img.width-img.width%padfactor, height=img.height) + text_frame = av.video.frame.VideoFrame(img.width, img.height, format="rgba") if len(output.streams.get()) == 0: # TODO: less hardcoding. @@ -72,17 +74,20 @@ class Concat(dcc.doom_base.Wad): output.streams[1].bit_rate=chunk.streams[1].bit_rate src = ograph.add_buffer(template=chunk.streams[0], time_base=chunk.streams[0].time_base) asrc = ograph.add_abuffer(template=chunk.streams[1], time_base=chunk.streams[1].time_base) - overlay = ograph.add_buffer(width=img.width, height=img.height, format="rgba", time_base=chunk.streams[0].time_base) - overlay_fo = ograph.add("fade", args="out:240:60") - overlay.link_to(overlay_fo, 0, 0) - composite = ograph.add("overlay", args="x=4:y=4") - src.link_to(composite, 0, 0) - overlay_fo.link_to(composite, 0, 1) ifade = ograph.add("fade", args="in:0:60") iafade = ograph.add("afade", args="in:{}:48000".format(offset*48000/1000000)) ofade = ograph.add("fade", args="out:{}:60".format((chunk.duration*60/1000000)-60)) oafade = ograph.add("afade", args="out:{}:48000".format(((offset+chunk.duration)*48000/1000000)-48000)) - composite.link_to(ifade, 0, 0) + if not parsed_args.nooverlay: + overlay = ograph.add_buffer(width=img.width, height=img.height, format="rgba", time_base=chunk.streams[0].time_base) + overlay_fo = ograph.add("fade", args="out:240:60") + overlay.link_to(overlay_fo, 0, 0) + composite = ograph.add("overlay", args="x=4:y=4") + src.link_to(composite, 0, 0) + overlay_fo.link_to(composite, 0, 1) + composite.link_to(ifade, 0, 0) + else: + src.link_to(ifade, 0, 0) asrc.link_to(iafade, 0, 0) ifade.link_to(ofade, 0, 0) iafade.link_to(oafade, 0, 0) @@ -97,12 +102,13 @@ class Concat(dcc.doom_base.Wad): packet.pts += (offset * packet.time_base.denominator) / (packet.time_base.numerator * 1000000) if packet.stream_index == 0: # TODO: robustness for ifr in packet.decode(): - 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 - text_frame.time_base = ifr.time_base - overlay.push(text_frame) + if not parsed_args.nooverlay: + 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 + text_frame.time_base = ifr.time_base + overlay.push(text_frame) src.push(ifr) ofr = sink.pull() for p in output.streams[packet.stream_index].encode(ofr): From 8071d1a8765f050ab527579b59af16b91798174b Mon Sep 17 00:00:00 2001 From: yrriban Date: Wed, 11 Jun 2025 03:05:54 -0400 Subject: [PATCH 2/9] PEP 8 compliance. --- dcc/play.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dcc/play.py b/dcc/play.py index 809862a..ea0af3c 100644 --- a/dcc/play.py +++ b/dcc/play.py @@ -2,6 +2,7 @@ import dcc.config import dcc.doom_base import subprocess + class Play(dcc.doom_base.WadMap): - def take_action(self, parsed_args): - subprocess.run([self.dsda] + self.dsda_preamble()) + def take_action(self, parsed_args): + subprocess.run([self.dsda] + self.dsda_preamble()) From 95132370e72bbedf0516469ea406bfcb4380d4ec Mon Sep 17 00:00:00 2001 From: yrriban Date: Wed, 11 Jun 2025 22:40:44 -0400 Subject: [PATCH 3/9] PEP 8 compliance. --- setup.py | 62 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/setup.py b/setup.py index a8d8aa9..6d11056 100644 --- a/setup.py +++ b/setup.py @@ -8,35 +8,35 @@ VERSION = '0.0.1' long_description = '' setup( - name=PROJECT, - version=VERSION, - description='Doom Command Center', - long_description=long_description, - author='yrriban', - author_email='yrriban@gmail.com', - platforms=['Any'], - install_requires=['cliff'], - packages=find_packages(), - include_package_data=True, - entry_points={ - 'console_scripts': ['dcc=dcc.main:main'], - 'dcc': [ - 'play = dcc.play:Play', - 'record = dcc.record:Record', - 'fabricate = dcc.fabricate:Fabricate', - 'put = dcc.put:Put', - 'pb = dcc.pb:PB', - 'ss = dcc.ss:SS', - 'extract = dcc.extract:Extract', - 'fetch = dcc.fetch:Fetch', - 'text = dcc.text:Text', - 'thumb = dcc.thumb:Thumb', - 'dsda = dcc.dsda:DSDA', - 'eureka = dcc.eureka:Eureka', - 'ls = dcc.ls:List', - 'configure = dcc.configure:Configure', - 'concat = dcc.concat:Concat', - ], - }, - zip_safe=False, + name=PROJECT, + version=VERSION, + description='Doom Command Center', + long_description=long_description, + author='yrriban', + author_email='yrriban@gmail.com', + platforms=['Any'], + install_requires=['cliff'], + packages=find_packages(), + include_package_data=True, + entry_points={ + 'console_scripts': ['dcc=dcc.main:main'], + 'dcc': [ + 'play = dcc.play:Play', + 'record = dcc.record:Record', + 'fabricate = dcc.fabricate:Fabricate', + 'put = dcc.put:Put', + 'pb = dcc.pb:PB', + 'ss = dcc.ss:SS', + 'extract = dcc.extract:Extract', + 'fetch = dcc.fetch:Fetch', + 'text = dcc.text:Text', + 'thumb = dcc.thumb:Thumb', + 'dsda = dcc.dsda:DSDA', + 'eureka = dcc.eureka:Eureka', + 'ls = dcc.ls:List', + 'configure = dcc.configure:Configure', + 'concat = dcc.concat:Concat', + ], + }, + zip_safe=False, ) From 2e9b8b148ebc81078d3175cb92e6e9831584ad2c Mon Sep 17 00:00:00 2001 From: yrriban Date: Wed, 11 Jun 2025 22:43:16 -0400 Subject: [PATCH 4/9] PEP 8 compliance. --- dcc/record.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/dcc/record.py b/dcc/record.py index ce8329e..190844b 100644 --- a/dcc/record.py +++ b/dcc/record.py @@ -2,7 +2,10 @@ import dcc.config import dcc.doom_base import subprocess + class Record(dcc.doom_base.WadMap): - def take_action(self, parsed_args): - subprocess.run([self.dsda] + self.dsda_preamble() + - ["-record", self.demo_out_path()]) + def take_action(self, parsed_args): + subprocess.run( + [self.dsda] + self.dsda_preamble() + + ["-record", self.demo_out_path()] + ) From 97616b341d4c96a78ca1c33c073f34e4b57d5b6a Mon Sep 17 00:00:00 2001 From: yrriban Date: Thu, 12 Jun 2025 23:47:22 -0400 Subject: [PATCH 5/9] Hardcode fewer things. Convert tabs to spaces. --- dcc/concat.py | 230 ++++++++++++++++++++++++++------------------------ 1 file changed, 122 insertions(+), 108 deletions(-) diff --git a/dcc/concat.py b/dcc/concat.py index 660d3d0..764f977 100644 --- a/dcc/concat.py +++ b/dcc/concat.py @@ -9,117 +9,131 @@ import numpy as np import wand.image class Concat(dcc.doom_base.Wad): - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument("start_map") - parser.add_argument("end_map") - parser.add_argument("-n", "--nooverlay", action="store_true") - return parser + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument("start_map") + parser.add_argument("end_map") + parser.add_argument("-n", "--nooverlay", action="store_true") + return parser - def take_action(self, parsed_args): - logging.basicConfig() - av.logging.set_level(av.logging.VERBOSE) - av.logging.restore_default_callback() - videos = self.fabricate.joinpath(parsed_args.wad).glob(f"{parsed_args.wad}_map*.mp4") - output = av.open(self.fabricate.joinpath(parsed_args.wad).joinpath(f"{parsed_args.wad}_maps{parsed_args.start_map}to{parsed_args.end_map}.mp4"), "w") - offset = 0 - # We'd like to use the concat filter here and connect everything into a - # single filter graph... but it produces a "Resource temporarily - # unavailable" error when switching to inputs after the first. Presumably - # fixable, but it's easier to just make one graph per video and mux - # everything together at the end. - for v in sorted(videos): - # TODO: Support UDoom in literally any way. - if not (v.name >= f"{parsed_args.wad}_map{parsed_args.start_map}.mp4" and - v.name <= f"{parsed_args.wad}_map{parsed_args.end_map}.mp4"): - continue + def take_action(self, parsed_args): + logging.basicConfig() + av.logging.set_level(av.logging.VERBOSE) + av.logging.restore_default_callback() + videos = self.fabricate.joinpath(parsed_args.wad).glob(f"{parsed_args.wad}_map*.mp4") + output = av.open(self.fabricate.joinpath(parsed_args.wad).joinpath(f"{parsed_args.wad}_maps{parsed_args.start_map}to{parsed_args.end_map}.mp4"), "w") + offset = 0 + # We'd like to use the concat filter here and connect everything into a + # single filter graph... but it produces a "Resource temporarily + # unavailable" error when switching to inputs after the first. Presumably + # fixable, but it's easier to just make one graph per video and mux + # everything together at the end. + for v in sorted(videos): + # TODO: Support UDoom in literally any way. + if not (v.name >= f"{parsed_args.wad}_map{parsed_args.start_map}.mp4" and + v.name <= f"{parsed_args.wad}_map{parsed_args.end_map}.mp4"): + continue - chunk = av.open(v) - ograph = av.filter.Graph() - sink = ograph.add("buffersink") - asink = ograph.add("abuffersink") + chunk = av.open(v) + if not (len(chunk.streams.video) == 1 and len(chunk.streams.audio) == 1): + raise Exception(f"irregular chunk {v}: streams {chunk.streams} (expected 1 video & 1 audio)") - if not parsed_args.nooverlay: - img = wand.image.Image(height=chunk.streams[0].height,width=chunk.streams[0].width) - mapstring = v.name[-6:-4] - text = self._config["map_names"][f"map{mapstring}"] - dcc.text.draw_text(img, f"MAP{mapstring}: {text}", font_size=120) - img.trim(reset_coords=True) - img.border("graya(25%, 25%)", 10, 10) - img.border(dcc.config.TEXT_STROKE_COLOR, 16, 16) - # for this to work... the image needs to have a width that's a multiple - # of 8. dude whyyyyyyy - padfactor=8 - img.border("transparent", padfactor, 0) - img.crop(width=img.width-img.width%padfactor, height=img.height) - text_frame = av.video.frame.VideoFrame(img.width, img.height, format="rgba") + ograph = av.filter.Graph() + sink = ograph.add("buffersink") + asink = ograph.add("abuffersink") - if len(output.streams.get()) == 0: - # TODO: less hardcoding. - output.add_stream("h264", rate=61440) - output.streams[0].extradata = copy.deepcopy(chunk.streams[0].extradata) - output.streams[0].height=1440 - output.streams[0].width=2560 - output.streams[0].profile="High" - output.streams[0].qmax = chunk.streams[0].qmax - output.streams[0].qmin = chunk.streams[0].qmin - output.streams[0].codec_context.gop_size=30 - output.streams[0].codec_context.max_b_frames=2 - output.streams[0].codec_context.framerate = fractions.Fraction(60,1) - output.streams[0].codec_context.pix_fmt="yuv420p" - output.streams[0].codec_context.bit_rate = chunk.streams[0].codec_context.bit_rate - output.add_stream("aac", rate=48000) - output.streams[1].extradata = copy.deepcopy(output.streams[1].extradata) - output.streams[1].rate=48000 - output.streams[1].bit_rate=chunk.streams[1].bit_rate - src = ograph.add_buffer(template=chunk.streams[0], time_base=chunk.streams[0].time_base) - asrc = ograph.add_abuffer(template=chunk.streams[1], time_base=chunk.streams[1].time_base) - ifade = ograph.add("fade", args="in:0:60") - iafade = ograph.add("afade", args="in:{}:48000".format(offset*48000/1000000)) - ofade = ograph.add("fade", args="out:{}:60".format((chunk.duration*60/1000000)-60)) - oafade = ograph.add("afade", args="out:{}:48000".format(((offset+chunk.duration)*48000/1000000)-48000)) - if not parsed_args.nooverlay: - overlay = ograph.add_buffer(width=img.width, height=img.height, format="rgba", time_base=chunk.streams[0].time_base) - overlay_fo = ograph.add("fade", args="out:240:60") - overlay.link_to(overlay_fo, 0, 0) - composite = ograph.add("overlay", args="x=4:y=4") - src.link_to(composite, 0, 0) - overlay_fo.link_to(composite, 0, 1) - composite.link_to(ifade, 0, 0) - else: - src.link_to(ifade, 0, 0) - asrc.link_to(iafade, 0, 0) - ifade.link_to(ofade, 0, 0) - iafade.link_to(oafade, 0, 0) - ofade.link_to(sink, 0, 0) - oafade.link_to(asink, 0, 0) + if not parsed_args.nooverlay: + img = wand.image.Image(height=chunk.streams[0].height,width=chunk.streams[0].width) + mapstring = v.name[-6:-4] + text = self._config["map_names"][f"map{mapstring}"] + dcc.text.draw_text(img, f"MAP{mapstring}: {text}", font_size=120) + img.trim(reset_coords=True) + img.border("graya(25%, 25%)", 10, 10) + img.border(dcc.config.TEXT_STROKE_COLOR, 16, 16) + # for this to work... the image needs to have a width that's a multiple + # of 8. dude whyyyyyyy + padfactor=8 + img.border("transparent", padfactor, 0) + img.crop(width=img.width-img.width%padfactor, height=img.height) - ograph.configure() - for packet in chunk.demux(): - if packet.dts is None: - continue - packet.dts += (offset * packet.time_base.denominator) / (packet.time_base.numerator * 1000000) - packet.pts += (offset * packet.time_base.denominator) / (packet.time_base.numerator * 1000000) - if packet.stream_index == 0: # TODO: robustness - for ifr in packet.decode(): - if not parsed_args.nooverlay: - 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 - text_frame.time_base = ifr.time_base - overlay.push(text_frame) - src.push(ifr) - ofr = sink.pull() - for p in output.streams[packet.stream_index].encode(ofr): - output.mux(p) - else: - for ifr in packet.decode(): - asrc.push(ifr) - ofr = asink.pull() - for p in output.streams[packet.stream_index].encode(ofr): - output.mux(p) - offset += chunk.duration - chunk.close() - output.close() + 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] + output.add_stream("h264", rate=int(vs.time_base.denominator/vs.time_base.numerator)) + output.streams[0].extradata = copy.deepcopy(vs.extradata) + output.streams[0].height=vs.height + output.streams[0].width=vs.width + output.streams[0].qmax = vs.qmax + output.streams[0].qmin = vs.qmin + output.streams[0].codec_context.bit_rate = vs.codec_context.bit_rate + output.streams[0].codec_context.framerate = vs.base_rate + output.streams[0].codec_context.pix_fmt = vs.codec_context.pix_fmt + # The following are only used for encoding and have no equivalent on the input stream. + output.streams[0].profile="High" + output.streams[0].codec_context.gop_size=30 + output.streams[0].codec_context.max_b_frames=2 + + astr = chunk.streams.audio[0] + output.add_stream("aac", rate=astr.rate) + output.streams[1].extradata = copy.deepcopy(astr.extradata) + output.streams[1].bit_rate=astr.bit_rate + + src = ograph.add_buffer(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) + # TODO: video fades are absolute relative to the input video; audio + # fades need to have their timestamps offset by the position in the + # final video. Clarify if this is really necessary. + frame_rate = chunk.streams.video[0].base_rate + sample_rate = chunk.streams.audio[0].rate + ifade = ograph.add("fade", args="in:0:{}".format(frame_rate)) + ofade = ograph.add("fade", args="out:{}:{}".format((chunk.duration*frame_rate/1000000)-frame_rate, frame_rate)) + iafade = ograph.add("afade", args="in:{}:{}".format(offset*sample_rate/1000000, sample_rate)) + oafade = ograph.add("afade", args="out:{}:{}".format(((offset+chunk.duration)*sample_rate/1000000)-sample_rate, sample_rate)) + if not parsed_args.nooverlay: + overlay = ograph.add_buffer(width=img.width, height=img.height, format="rgba", time_base=chunk.streams[0].time_base) + overlay_fo = ograph.add("fade", args="out:{}:{}".format(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) + overlay_fo.link_to(composite, 0, 1) + composite.link_to(ifade, 0, 0) + else: + src.link_to(ifade, 0, 0) + + asrc.link_to(iafade, 0, 0) + ifade.link_to(ofade, 0, 0) + iafade.link_to(oafade, 0, 0) + ofade.link_to(sink, 0, 0) + oafade.link_to(asink, 0, 0) + ograph.configure() + + for packet in chunk.demux(): + if packet.dts is None: + continue + packet.dts += (offset * packet.time_base.denominator) / (packet.time_base.numerator * 1000000) + packet.pts += (offset * packet.time_base.denominator) / (packet.time_base.numerator * 1000000) + if packet.stream == chunk.streams.video[0]: + for ifr in packet.decode(): + if not parsed_args.nooverlay: + 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 + text_frame.time_base = ifr.time_base + overlay.push(text_frame) + src.push(ifr) + ofr = sink.pull() + for p in output.streams[packet.stream_index].encode(ofr): + output.mux(p) + else: + for ifr in packet.decode(): + asrc.push(ifr) + ofr = asink.pull() + for p in output.streams[packet.stream_index].encode(ofr): + output.mux(p) + offset += chunk.duration + chunk.close() + output.close() From b29e2675d17457f3a6bf8e4b0eaf4057e31fabed Mon Sep 17 00:00:00 2001 From: yrriban Date: Fri, 13 Jun 2025 01:38:07 -0400 Subject: [PATCH 6/9] PEP 8 compliance. --- dcc/fetch.py | 61 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/dcc/fetch.py b/dcc/fetch.py index d58dc1a..00121f5 100644 --- a/dcc/fetch.py +++ b/dcc/fetch.py @@ -5,32 +5,43 @@ import pathlib import urllib.request import zipfile + class Fetch(dcc.config.Base): - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument("id_or_name") - return parser - - def take_action(self, parsed_args): - idgames_id = parsed_args.id_or_name - if not parsed_args.id_or_name.isdigit(): - idgames_id = self.search_idgames(parsed_args.id_or_name) + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument("id_or_name") + return parser - with urllib.request.urlopen("https://www.doomworld.com/idgames/api/api.php?action=get&id={}&out=json".format(idgames_id)) as response: - reply = json.loads(response.read()) - rpath = "/".join([dcc.config.MIRROR, reply["content"]["dir"], reply["content"]["filename"]]) - wad = reply["content"]["filename"][0:-4] + def take_action(self, parsed_args): + idgames_id = parsed_args.id_or_name + if not parsed_args.id_or_name.isdigit(): + idgames_id = self.search_idgames(parsed_args.id_or_name) - with urllib.request.urlopen(rpath) as response: - z = zipfile.ZipFile(io.BytesIO(response.read())) - z.extractall(path=self.pwads.joinpath(wad)) + with urllib.request.urlopen( + "https://www.doomworld.com/idgames/api/" + + "api.php?action=get&id={}&out=json".format(idgames_id) + ) as response: + reply = json.loads(response.read()) + rpath = "/".join([ + dcc.config.MIRROR, + reply["content"]["dir"], + reply["content"]["filename"] + ]) + wad = reply["content"]["filename"][0:-4] - # TODO: explicit error handling. Let users choose when >1 result. - def search_idgames(self, wad): - with urllib.request.urlopen("https://www.doomworld.com/idgames/api/api.php?action=search&query={}&out=json".format(wad)) as response: - reply = json.loads(response.read()) - files = reply["content"]["file"] - if type(files) is dict: # One result. - return files["id"] - else: # More than one. Zero will raise an error. - return files[0]["id"] + with urllib.request.urlopen(rpath) as response: + z = zipfile.ZipFile(io.BytesIO(response.read())) + z.extractall(path=self.pwads.joinpath(wad)) + + # TODO: explicit error handling. Let users choose when >1 result. + def search_idgames(self, wad): + with urllib.request.urlopen( + "https://www.doomworld.com/idgames/api/" + + "api.php?action=search&query={}&out=json".format(wad) + ) as response: + reply = json.loads(response.read()) + files = reply["content"]["file"] + if type(files) is dict: # One result. + return files["id"] + else: # More than one. Zero will raise an error. + return files[0]["id"] From 3fde5791a31f5391d2192b31e2e8e3dd1eb3eada Mon Sep 17 00:00:00 2001 From: yrriban Date: Fri, 13 Jun 2025 01:52:57 -0400 Subject: [PATCH 7/9] Tabs to spaces; purge some commented-out code. --- dcc/doom_base.py | 166 +++++++++++++++++++++++------------------------ 1 file changed, 82 insertions(+), 84 deletions(-) diff --git a/dcc/doom_base.py b/dcc/doom_base.py index b29eb33..fc3bddb 100644 --- a/dcc/doom_base.py +++ b/dcc/doom_base.py @@ -7,113 +7,111 @@ import re import tomlkit class Wad(dcc.config.Base): - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument('wad') - return parser + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument('wad') + return parser - def wad_init(self, parsed_args): - self.init_base(parsed_args) - self._wad = parsed_args.wad - wcp = self.pwads.joinpath(self.wad).joinpath(self.config_name) - if wcp.exists(): - self._wad_config = tomlkit.toml_file.TOMLFile(wcp).read() - self._config.update(self._wad_config.value) - #for k,v in self._wad_config.value.items(): - #self._config.add(k,v) + def wad_init(self, parsed_args): + self.init_base(parsed_args) + self._wad = parsed_args.wad + wcp = self.pwads.joinpath(self.wad).joinpath(self.config_name) + if wcp.exists(): + self._wad_config = tomlkit.toml_file.TOMLFile(wcp).read() + self._config.update(self._wad_config.value) - def run(self, parsed_args): - self.wad_init(parsed_args) - self.take_action(parsed_args) + def run(self, parsed_args): + self.wad_init(parsed_args) + self.take_action(parsed_args) - @property - def wad(self): - return self._wad + @property + def wad(self): + return self._wad class WadMap(Wad): - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument('map') - parser.add_argument('-n','--name','--demo_name') - return parser + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument('map') + parser.add_argument('-n','--name','--demo_name') + return parser - def run(self, parsed_args): - self._map = parsed_args.map - self._name = parsed_args.name - self.wad_init(parsed_args) + def run(self, parsed_args): + self._map = parsed_args.map + self._name = parsed_args.name + self.wad_init(parsed_args) - self.take_action(parsed_args) + self.take_action(parsed_args) - @property - def map(self): - return self._map - - @property - def name_string(self): - return "" if self._name is None else "_" + self._name + @property + def map(self): + return self._map + + @property + def name_string(self): + return "" if self._name is None else "_" + self._name - def dsda_preamble(self): - args = ["-iwad", self.iwad_path(self.wad)] + def dsda_preamble(self): + args = ["-iwad", self.iwad_path(self.wad)] - pwadpath = self.pwads.joinpath(self.wad) - wads = sorted(pwadpath.glob('*.wad', case_sensitive=False)) - if len(wads) > 0: - args = args + ["-file"] + wads + pwadpath = self.pwads.joinpath(self.wad) + wads = sorted(pwadpath.glob('*.wad', case_sensitive=False)) + if len(wads) > 0: + args = args + ["-file"] + wads - dehs = sorted(pwadpath.glob('*.deh', case_sensitive=False)) - if len(dehs) > 0: - args = args + ["-deh"] + dehs + dehs = sorted(pwadpath.glob('*.deh', case_sensitive=False)) + if len(dehs) > 0: + args = args + ["-deh"] + dehs - args = args + ["-complevel", self.complevel()] - args = args + ["-skill", "4"] - args = args + ["-warp", self.map] - return args + args = args + ["-complevel", self.complevel()] + args = args + ["-skill", "4"] + args = args + ["-warp", self.map] + return args - def complevel(self): - complevel = self.pwads.joinpath(self.wad).joinpath("complevel") - if not complevel.exists(): - raise Exception("No complevel set for wad {}.".format(self.wad)) + def complevel(self): + complevel = self.pwads.joinpath(self.wad).joinpath("complevel") + if not complevel.exists(): + raise Exception("No complevel set for wad {}.".format(self.wad)) - with io.open(complevel) as f: - return f.read().strip() + with io.open(complevel) as f: + return f.read().strip() - def demo_in_path(self): - candidates = [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 {} 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 demo_in_path(self): + candidates = [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 {} 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")) + def dsda_text_path(self): + 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")) + def video_path(self): + 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")) + def demo_out_path(self): + return self._ensure(self.demos.joinpath(self.wad)).joinpath(self._file_base(".lmp")) - def target_bucket(self): - return "doom/" + 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")) + def base_thumb_path(self): + return self._ensure(self.fabricate.joinpath(self.wad)).joinpath(self._file_base("_base.png")) - def m_doom_path(self): - return self._ensure(self.fabricate.joinpath(self.wad)).joinpath("M_DOOM_scaled.png") + def m_doom_path(self): + return self._ensure(self.fabricate.joinpath(self.wad)).joinpath("M_DOOM_scaled.png") - def text_thumb_path(self): - return self._ensure(self.fabricate.joinpath(self.wad)).joinpath(self._file_base("_text.png")) + def text_thumb_path(self): + 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")) + def thumb_path(self): + return self._ensure(self.fabricate.joinpath(self.wad)).joinpath(self._file_base("_thumb.png")) - def _file_base(self, ext): - return "{}_map{}{}{}".format(self.wad, self.map, self.name_string, ext) + def _file_base(self, ext): + return "{}_map{}{}{}".format(self.wad, self.map, self.name_string, ext) - def _ensure(self, path): - if not path.exists(): - os.mkdir(path) - return path + def _ensure(self, path): + if not path.exists(): + os.mkdir(path) + return path From d5394908982b7c399a88924cc5e04843b0a9b369 Mon Sep 17 00:00:00 2001 From: yrriban Date: Fri, 13 Jun 2025 01:58:09 -0400 Subject: [PATCH 8/9] If the complevel file doesn't exist, try to read the complevel out of the config instead. --- dcc/doom_base.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dcc/doom_base.py b/dcc/doom_base.py index fc3bddb..74a77bc 100644 --- a/dcc/doom_base.py +++ b/dcc/doom_base.py @@ -71,6 +71,9 @@ class WadMap(Wad): def complevel(self): complevel = self.pwads.joinpath(self.wad).joinpath("complevel") if not complevel.exists(): + complevel = self._config.get("complevel") + if complevel is not None: + return complevel raise Exception("No complevel set for wad {}.".format(self.wad)) with io.open(complevel) as f: From 6baacd9b899ef00a6472209532a2c9ce318d4d94 Mon Sep 17 00:00:00 2001 From: yrriban Date: Fri, 13 Jun 2025 02:05:52 -0400 Subject: [PATCH 9/9] PEP 8 compliance. --- dcc/doom_base.py | 52 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/dcc/doom_base.py b/dcc/doom_base.py index 74a77bc..48d411a 100644 --- a/dcc/doom_base.py +++ b/dcc/doom_base.py @@ -6,6 +6,7 @@ import os import re import tomlkit + class Wad(dcc.config.Base): def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -33,7 +34,7 @@ class WadMap(Wad): def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('map') - parser.add_argument('-n','--name','--demo_name') + parser.add_argument('-n', '--name', '--demo_name') return parser def run(self, parsed_args): @@ -46,7 +47,7 @@ class WadMap(Wad): @property def map(self): return self._map - + @property def name_string(self): return "" if self._name is None else "_" + self._name @@ -80,36 +81,63 @@ class WadMap(Wad): return f.read().strip() def demo_in_path(self): - candidates = [x for x in self.demos.joinpath(self.wad).glob(self._file_base("*.lmp"))] + candidates = [ + 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 {} name {}.".format(self.wad, self.map, self.name_string)) + raise Exception( + "no suitable demo candidates for WAD {} MAP {} name {}." + .format(self.wad, self.map, self.name_string) + ) if len(candidates) == 1: return candidates[0] - return sorted(filter(lambda s : re.search("-", str(s)), candidates))[-1] + 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 m_doom_path(self): - return self._ensure(self.fabricate.joinpath(self.wad)).joinpath("M_DOOM_scaled.png") + return ( + self._ensure(self.fabricate.joinpath(self.wad)) + .joinpath("M_DOOM_scaled.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): return "{}_map{}{}{}".format(self.wad, self.map, self.name_string, ext)