Skip to content

Commit 523603c

Browse files
jessielwjessielw
andauthored
Dev (#86)
* feat: add InvalidAtmosInputError * feat: add support to detect adm * refactor: swap logger type * refactor: use boolean for detection * feat: add support for adm * feat: update * feat: update * fix: automatic delay in output file name #84 * feat: improve docs #79 * feat: update * feat: re-work temp path system #82 * feat: v1.3.4rc1 * refactor: update * refactor: temporarily disable test * feat: deprecate `--parse-elementary-delay` * refactor: update payload for removed `--parse-elementary-delay` * refactor: update doc * refactor: get delay relative to video in the object now * feat: re worked delay handling logic #84 refactor: returned strings of delay are now consistent without brackets * feat: setup to handle delay better #84 * feat: update * feat: update * feat: update --------- Co-authored-by: jessielw <[email protected]>
1 parent 127f693 commit 523603c

34 files changed

Lines changed: 519 additions & 412 deletions

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [Unreleased]
9+
10+
### Added
11+
12+
- Added support for ADM BWF for **AC4** and **Atmos** profiles.
13+
14+
### Changed
15+
16+
- Logger for failure to get source bitrate from config is now a `debug` instead of a `warning`.
17+
- Default temporary path behavior changed to use short, platform-specific per-input job folders (reduces Windows long-path issues).
18+
- `--parse-elementary-delay` is now deprecated and will be removed on **1.4.0**. This is all now handled automatically and internally.
19+
- If the arg is passed you will get a warning and it'll be ignored.
20+
21+
### Fixed
22+
23+
- DDP resetting detected delay for the output file name only (was properly stripped still).
24+
- Output names being generated automatically could potentially output the wrong delay in the filename.
25+
826
## [1.3.3] - 2025-09-24
927

1028
### Added

deezy/audio_encoders/dee/ac4.py

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,7 @@ def encode(self):
6767

6868
# delay
6969
delay = self.get_delay(
70-
audio_track_info,
7170
self.payload.delay,
72-
self.payload.parse_elementary_delay,
7371
file_input,
7472
)
7573

@@ -89,27 +87,21 @@ def encode(self):
8987
# and will be ignored if not present. Keep existing generate_output_filename
9088
# as the fallback to avoid changing default behavior.
9189
if self.payload.output_template:
92-
ignore_delay, delay_was_stripped = self.compute_template_delay_flags(
93-
audio_track_info, delay, self.payload.parse_elementary_delay
94-
)
9590
output = mi_parser.render_output_template(
9691
template=str(self.payload.output_template),
9792
suffix=".ac4",
9893
output_channels="2.0",
94+
delay_was_stripped=delay.is_delay(),
95+
delay_relative_to_video=audio_track_info.delay_relative_to_video,
9996
worker_id=self.payload.worker_id,
100-
ignore_delay=ignore_delay,
101-
delay_was_stripped=delay_was_stripped,
10297
)
10398
if self.payload.output_preview:
10499
logger.info(f"Output preview: {output}")
105100
return output
106101
else:
107-
ignore_delay, delay_was_stripped = self.compute_template_delay_flags(
108-
audio_track_info, delay, self.payload.parse_elementary_delay
109-
)
110102
output = mi_parser.generate_output_filename(
111-
ignore_delay,
112-
delay_was_stripped,
103+
delay_was_stripped=delay.is_delay(),
104+
delay_relative_to_video=audio_track_info.delay_relative_to_video,
113105
suffix=".ac4",
114106
output_channels="2.0",
115107
worker_id=self.payload.worker_id,
@@ -123,14 +115,7 @@ def encode(self):
123115
logger.debug(f"Output path {output}.")
124116

125117
# temp dir: prefer a user-provided centralized temp base (per-input subfolder)
126-
# so users can collect all temp files in one place. If not provided, use
127-
# the adjacent per-input cache folder (<parent>/<stem>_deezy).
128-
user_temp_base = getattr(self.payload, "temp_dir", None)
129-
if user_temp_base:
130-
temp_dir = Path(user_temp_base) / f"{file_input.stem}_deezy"
131-
temp_dir.mkdir(parents=True, exist_ok=True)
132-
else:
133-
temp_dir = self._adjacent_temp_dir(file_input)
118+
temp_dir = self._get_temp_dir(file_input, self.payload.temp_dir)
134119
logger.debug(f"Temp directory {temp_dir}.")
135120

136121
# check disk space
@@ -149,11 +134,7 @@ def encode(self):
149134
self._early_output_exists_check(output, self.payload.overwrite)
150135

151136
# decode TrueHD to atmos mezz
152-
if audio_track_info.thd_atmos:
153-
if not self.payload.truehdd_path:
154-
raise DependencyNotFoundError(
155-
"Failed to locate truehdd, this is required for atmos work flows"
156-
)
137+
if self.payload.truehdd_path and audio_track_info.thd_atmos:
157138
# optionally stagger/jitter and limit concurrent TrueHD jobs
158139
self._maybe_jitter()
159140
self._acquire_truehdd()
@@ -175,6 +156,10 @@ def encode(self):
175156
logger.info("Reusing decoded atmos from temp folder")
176157
decoded_mezz_path = temp_dir / "atmos_meta.atmos"
177158
else:
159+
if not self.payload.truehdd_path:
160+
raise DependencyNotFoundError(
161+
"Failed to locate truehdd, this is required for atmos work flows"
162+
)
178163
decoded_mezz_path = decode_truehd_to_atmos(
179164
output_dir=temp_dir,
180165
file_input=self.payload.file_input,
@@ -200,7 +185,13 @@ def encode(self):
200185
self._release_truehdd()
201186
# make input_file_path point to the decoded mezz when TrueHD path used
202187
input_file_path = decoded_mezz_path
203-
# if not truehd we know it's valid channel based audio since we checked above
188+
189+
# check if we're processing adm atmos wav file
190+
elif audio_track_info.adm_atmos_wav:
191+
input_file_path = file_input
192+
logger.info("Using ADM BWF input")
193+
194+
# if not truehd/adm we know it's valid channel based audio since we checked above
204195
else:
205196
# generate ffmpeg cmd
206197
ffmpeg_cmd = self._generate_ffmpeg_cmd(
@@ -276,6 +267,9 @@ def encode(self):
276267
fps=fps,
277268
delay=delay,
278269
temp_dir=temp_dir,
270+
atmos_enabled=bool(
271+
audio_track_info.adm_atmos_wav or audio_track_info.thd_atmos
272+
),
279273
)
280274
logger.debug(f"{json_path=}.")
281275

deezy/audio_encoders/dee/atmos.py

Lines changed: 66 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from deezy.enums.codec_format import CodecFormat
99
from deezy.exceptions import (
1010
DependencyNotFoundError,
11+
InvalidAtmosInputError,
1112
InvalidExtensionError,
1213
OutputFileNotFoundError,
1314
)
@@ -62,9 +63,7 @@ def encode(self):
6263

6364
# delay
6465
delay = self.get_delay(
65-
audio_track_info,
6666
self.payload.delay,
67-
self.payload.parse_elementary_delay,
6867
file_input,
6968
)
7069

@@ -84,27 +83,21 @@ def encode(self):
8483
# and will be ignored if not present. Keep existing generate_output_filename
8584
# as the fallback to avoid changing default behavior.
8685
if self.payload.output_template:
87-
ignore_delay, delay_was_stripped = self.compute_template_delay_flags(
88-
audio_track_info, delay, self.payload.parse_elementary_delay
89-
)
9086
output = mi_parser.render_output_template(
9187
template=str(self.payload.output_template),
9288
suffix=".ec3",
9389
output_channels=str(self.payload.atmos_mode.get_str_channels()),
90+
delay_was_stripped=delay.is_delay(),
91+
delay_relative_to_video=audio_track_info.delay_relative_to_video,
9492
worker_id=self.payload.worker_id,
95-
ignore_delay=ignore_delay,
96-
delay_was_stripped=delay_was_stripped,
9793
)
9894
if self.payload.output_preview:
9995
logger.info(f"Output preview: {output}")
10096
return output
10197
else:
102-
ignore_delay, delay_was_stripped = self.compute_template_delay_flags(
103-
audio_track_info, delay, self.payload.parse_elementary_delay
104-
)
10598
output = mi_parser.generate_output_filename(
106-
ignore_delay,
107-
delay_was_stripped,
99+
delay_was_stripped=delay.is_delay(),
100+
delay_relative_to_video=audio_track_info.delay_relative_to_video,
108101
suffix=".ec3",
109102
output_channels=str(self.payload.atmos_mode.get_str_channels()),
110103
worker_id=self.payload.worker_id,
@@ -118,14 +111,7 @@ def encode(self):
118111
logger.debug(f"Output path {output}.")
119112

120113
# temp dir: prefer a user-provided centralized temp base (per-input subfolder)
121-
# so users can collect all temp files in one place. If not provided, use
122-
# the adjacent per-input cache folder (<parent>/<stem>_deezy).
123-
user_temp_base = getattr(self.payload, "temp_dir", None)
124-
if user_temp_base:
125-
temp_dir = Path(user_temp_base) / f"{file_input.stem}_deezy"
126-
temp_dir.mkdir(parents=True, exist_ok=True)
127-
else:
128-
temp_dir = self._adjacent_temp_dir(file_input)
114+
temp_dir = self._get_temp_dir(file_input, self.payload.temp_dir)
129115
logger.debug(f"Temp directory {temp_dir}.")
130116

131117
# check disk space
@@ -141,56 +127,72 @@ def encode(self):
141127
# destination already exists and the user didn't request overwrite.
142128
self._early_output_exists_check(output, self.payload.overwrite)
143129

144-
# decode TrueHD to atmos mezz
145-
if not self.payload.truehdd_path:
146-
raise DependencyNotFoundError(
147-
"Failed to locate truehdd, this is required for atmos work flows"
148-
)
130+
# dee input path variable
131+
dee_input_path: Path | None = None
149132

150-
# optionally stagger/jitter and limit concurrent TrueHD jobs
151-
self._maybe_jitter()
152-
self._acquire_truehdd()
153-
try:
154-
truehdd_signature = f"truehdd:{self.payload.thd_warp_mode.to_truehdd_cmd()}"
155-
metadata_path = self._metadata_path_for_output(temp_dir, output)
156-
if getattr(
157-
self.payload, "reuse_temp_files", False
158-
) and self._check_reuse_signature(
159-
metadata_path,
160-
str(CodecFormat.ATMOS),
161-
truehdd_signature,
162-
"atmos_meta.atmos",
163-
temp_dir,
164-
):
165-
logger.info("Reusing extracted wav from temp folder")
166-
decoded_mezz_path = temp_dir / "atmos_meta.atmos"
167-
else:
168-
decoded_mezz_path = decode_truehd_to_atmos(
169-
output_dir=temp_dir,
170-
file_input=self.payload.file_input,
171-
track_index=self.payload.track_index,
172-
ffmpeg_path=self.payload.ffmpeg_path,
173-
truehdd_path=self.payload.truehdd_path,
174-
bed_conform=self.payload.bed_conform,
175-
warp_mode=self.payload.thd_warp_mode,
176-
duration=audio_track_info.duration,
177-
step_info={"current": 1, "total": 3, "name": "truehdd"},
178-
no_progress_bars=self.payload.no_progress_bars,
133+
# check if we're processing truehd atmos
134+
if self.payload.truehdd_path and audio_track_info.thd_atmos:
135+
# optionally stagger/jitter and limit concurrent TrueHD jobs
136+
self._maybe_jitter()
137+
self._acquire_truehdd()
138+
try:
139+
truehdd_signature = (
140+
f"truehdd:{self.payload.thd_warp_mode.to_truehdd_cmd()}"
179141
)
180-
if getattr(self.payload, "reuse_temp_files", False):
181-
self._write_signature_metadata(
182-
metadata_path,
183-
str(CodecFormat.ATMOS),
184-
truehdd_signature,
185-
"atmos_meta.atmos",
186-
file_input,
142+
metadata_path = self._metadata_path_for_output(temp_dir, output)
143+
if getattr(
144+
self.payload, "reuse_temp_files", False
145+
) and self._check_reuse_signature(
146+
metadata_path,
147+
str(CodecFormat.ATMOS),
148+
truehdd_signature,
149+
"atmos_meta.atmos",
150+
temp_dir,
151+
):
152+
dee_input_path = temp_dir / "atmos_meta.atmos"
153+
logger.info("Reusing extracted wav from temp folder")
154+
else:
155+
if self.payload.truehdd_path:
156+
raise DependencyNotFoundError(
157+
"Failed to locate truehdd, this is required for TrueHD Atmos work flows"
158+
)
159+
dee_input_path = decode_truehd_to_atmos(
160+
output_dir=temp_dir,
161+
file_input=self.payload.file_input,
162+
track_index=self.payload.track_index,
163+
ffmpeg_path=self.payload.ffmpeg_path,
164+
truehdd_path=self.payload.truehdd_path,
165+
bed_conform=self.payload.bed_conform,
166+
warp_mode=self.payload.thd_warp_mode,
167+
duration=audio_track_info.duration,
168+
step_info={"current": 1, "total": 3, "name": "truehdd"},
169+
no_progress_bars=self.payload.no_progress_bars,
187170
)
188-
finally:
189-
self._release_truehdd()
171+
if getattr(self.payload, "reuse_temp_files", False):
172+
self._write_signature_metadata(
173+
metadata_path,
174+
str(CodecFormat.ATMOS),
175+
truehdd_signature,
176+
"atmos_meta.atmos",
177+
file_input,
178+
)
179+
finally:
180+
self._release_truehdd()
181+
182+
# check if we're processing adm atmos wav file
183+
elif audio_track_info.adm_atmos_wav:
184+
dee_input_path = file_input
185+
logger.info("Using ADM BWF input")
186+
187+
# if it's anything raise an error
188+
else:
189+
raise InvalidAtmosInputError(
190+
"Cannot process input, not a valid Atmos format"
191+
)
190192

191193
# generate JSON
192194
json_generator = DeeJSONGenerator(
193-
input_file_path=decoded_mezz_path,
195+
input_file_path=dee_input_path,
194196
output_file_path=output,
195197
output_dir=temp_dir,
196198
codec_format=CodecFormat.ATMOS,

0 commit comments

Comments
 (0)