monkeyplug-enhanced 2.2.3__tar.gz → 2.2.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {monkeyplug_enhanced-2.2.3 → monkeyplug_enhanced-2.2.4}/PKG-INFO +1 -1
- {monkeyplug_enhanced-2.2.3 → monkeyplug_enhanced-2.2.4}/pyproject.toml +1 -1
- {monkeyplug_enhanced-2.2.3 → monkeyplug_enhanced-2.2.4}/src/monkeyplug/monkeyplug.py +285 -29
- {monkeyplug_enhanced-2.2.3 → monkeyplug_enhanced-2.2.4}/.gitignore +0 -0
- {monkeyplug_enhanced-2.2.3 → monkeyplug_enhanced-2.2.4}/LICENSE +0 -0
- {monkeyplug_enhanced-2.2.3 → monkeyplug_enhanced-2.2.4}/README.md +0 -0
- {monkeyplug_enhanced-2.2.3 → monkeyplug_enhanced-2.2.4}/src/monkeyplug/__init__.py +0 -0
- {monkeyplug_enhanced-2.2.3 → monkeyplug_enhanced-2.2.4}/src/monkeyplug/data/profanity_list.json +0 -0
- {monkeyplug_enhanced-2.2.3 → monkeyplug_enhanced-2.2.4}/src/monkeyplug/groq_config.py +0 -0
- {monkeyplug_enhanced-2.2.3 → monkeyplug_enhanced-2.2.4}/src/monkeyplug/separation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: monkeyplug-enhanced
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.4
|
|
4
4
|
Summary: Enhanced fork of monkeyplug — censors profanity in audio files using speech recognition with Groq API, AI instrumental generation, and batch processing.
|
|
5
5
|
Project-URL: Homepage, https://github.com/ljbred08/monkeyplug
|
|
6
6
|
Project-URL: Issues, https://github.com/ljbred08/monkeyplug/issues
|
|
@@ -16,6 +16,8 @@ import requests
|
|
|
16
16
|
import shutil
|
|
17
17
|
import string
|
|
18
18
|
import sys
|
|
19
|
+
import threading
|
|
20
|
+
import time
|
|
19
21
|
import wave
|
|
20
22
|
from tqdm import tqdm
|
|
21
23
|
|
|
@@ -221,6 +223,63 @@ def GetCodecs(local_filename, debug=False):
|
|
|
221
223
|
return result
|
|
222
224
|
|
|
223
225
|
|
|
226
|
+
###################################################################################################
|
|
227
|
+
class _SmoothProgressTicker:
|
|
228
|
+
"""Background thread that smoothly advances a tqdm bar based on elapsed time.
|
|
229
|
+
|
|
230
|
+
Used when historical timing data allows estimating step durations.
|
|
231
|
+
The bar advances linearly within each step's estimated range, clamped
|
|
232
|
+
so it never overshoots. When the step completes, stop() snaps to actual.
|
|
233
|
+
"""
|
|
234
|
+
|
|
235
|
+
def __init__(self, bar):
|
|
236
|
+
self._bar = bar
|
|
237
|
+
self._cumulative = 0.0 # Position where current step begins
|
|
238
|
+
self._step_estimate = 0.0 # Estimated seconds for current step
|
|
239
|
+
self._step_start = 0.0 # time.time() when step started
|
|
240
|
+
self._stop_event = threading.Event()
|
|
241
|
+
self._thread = None
|
|
242
|
+
|
|
243
|
+
def start(self, cumulative, step_estimated_seconds):
|
|
244
|
+
"""Begin ticking for a new step."""
|
|
245
|
+
self.stop() # Stop any previous tick
|
|
246
|
+
self._cumulative = cumulative
|
|
247
|
+
self._step_estimate = step_estimated_seconds
|
|
248
|
+
self._step_start = time.time()
|
|
249
|
+
self._stop_event.clear()
|
|
250
|
+
self._thread = threading.Thread(target=self._tick, daemon=True)
|
|
251
|
+
self._thread.start()
|
|
252
|
+
|
|
253
|
+
def _tick(self):
|
|
254
|
+
while not self._stop_event.is_set():
|
|
255
|
+
try:
|
|
256
|
+
elapsed = time.time() - self._step_start
|
|
257
|
+
position = self._cumulative + min(elapsed, self._step_estimate)
|
|
258
|
+
# Never exceed the bar's total
|
|
259
|
+
if self._bar.total is not None:
|
|
260
|
+
position = min(position, self._bar.total)
|
|
261
|
+
self._bar.n = position
|
|
262
|
+
self._bar.refresh()
|
|
263
|
+
except (TypeError, ValueError, AttributeError):
|
|
264
|
+
break # Bar was closed externally
|
|
265
|
+
self._stop_event.wait(0.25)
|
|
266
|
+
|
|
267
|
+
def stop(self):
|
|
268
|
+
"""Stop the ticker and return actual elapsed seconds for this step."""
|
|
269
|
+
self._stop_event.set()
|
|
270
|
+
if self._thread and self._thread.is_alive():
|
|
271
|
+
self._thread.join(timeout=1.0)
|
|
272
|
+
self._thread = None
|
|
273
|
+
if self._step_start > 0:
|
|
274
|
+
return time.time() - self._step_start
|
|
275
|
+
return 0.0
|
|
276
|
+
|
|
277
|
+
def adjust_total(self, delta):
|
|
278
|
+
"""Adjust the bar's total by delta (e.g., remove an unused step estimate)."""
|
|
279
|
+
if self._bar.total is not None:
|
|
280
|
+
self._bar.total = max(self._bar.total + delta, self._bar.n)
|
|
281
|
+
|
|
282
|
+
|
|
224
283
|
#################################################################################
|
|
225
284
|
class Plugger(object):
|
|
226
285
|
debug = False
|
|
@@ -670,10 +729,30 @@ class Plugger(object):
|
|
|
670
729
|
|
|
671
730
|
######## CreateCleanMuteList #################################################
|
|
672
731
|
def CreateCleanMuteList(self):
|
|
673
|
-
|
|
732
|
+
smooth = hasattr(self, '_smooth_ticker') and self._smooth_ticker is not None
|
|
733
|
+
cumulative = getattr(self, '_smooth_cumulative', 0.0)
|
|
734
|
+
will_transcribe = getattr(self, '_will_transcribe', False)
|
|
735
|
+
|
|
736
|
+
# Start ticker for transcribe step (if applicable)
|
|
737
|
+
if smooth and will_transcribe:
|
|
738
|
+
est = getattr(self, '_smooth_transcribe_est', 0)
|
|
739
|
+
if hasattr(self, '_progress') and self._progress:
|
|
740
|
+
self._progress.set_description("Transcribing")
|
|
741
|
+
self._smooth_ticker.start(cumulative, est)
|
|
742
|
+
|
|
743
|
+
transcribe_start = time.time() if will_transcribe else 0
|
|
674
744
|
if not self.LoadTranscriptFromFile():
|
|
675
745
|
self.RecognizeSpeech()
|
|
676
746
|
|
|
747
|
+
if will_transcribe:
|
|
748
|
+
actual_transcribe = time.time() - transcribe_start
|
|
749
|
+
if smooth:
|
|
750
|
+
self._smooth_ticker.stop()
|
|
751
|
+
cumulative += actual_transcribe
|
|
752
|
+
self._smooth_cumulative = cumulative
|
|
753
|
+
if hasattr(self, '_step_timings') and self._step_timings is not None:
|
|
754
|
+
self._step_timings['transcribe'] = (actual_transcribe, getattr(self, '_timing_file_duration', 0))
|
|
755
|
+
|
|
677
756
|
self.naughtyWordList = [word for word in self.wordList if word["scrub"] is True]
|
|
678
757
|
|
|
679
758
|
# Handle auto-generation mode
|
|
@@ -684,30 +763,58 @@ class Plugger(object):
|
|
|
684
763
|
# Extract, separate, and get instrumental file
|
|
685
764
|
if self.instrumentalSegments:
|
|
686
765
|
try:
|
|
687
|
-
# Update progress bar
|
|
766
|
+
# Update progress bar for extraction step
|
|
688
767
|
if hasattr(self, '_progress') and self._progress and not self.debug:
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
768
|
+
if smooth:
|
|
769
|
+
extract_est = getattr(self, '_smooth_extract_est', 0)
|
|
770
|
+
self._progress.set_description("Extracting instrumental")
|
|
771
|
+
self._smooth_ticker.start(cumulative, extract_est)
|
|
772
|
+
else:
|
|
773
|
+
self._progress.update(1)
|
|
774
|
+
self._progress.total = 3
|
|
775
|
+
self._progress.set_description("Extracting instrumental")
|
|
692
776
|
|
|
777
|
+
extract_start = time.time()
|
|
693
778
|
self.instrumentalFileSpec = self._create_combined_profanity_file()
|
|
694
779
|
|
|
695
|
-
|
|
696
|
-
if
|
|
780
|
+
actual_extract = time.time() - extract_start
|
|
781
|
+
if smooth:
|
|
782
|
+
self._smooth_ticker.stop()
|
|
783
|
+
cumulative += actual_extract
|
|
784
|
+
self._smooth_cumulative = cumulative
|
|
785
|
+
if hasattr(self, '_step_timings') and self._step_timings is not None:
|
|
786
|
+
self._step_timings['extract'] = (actual_extract, getattr(self, '_timing_file_duration', 0))
|
|
787
|
+
|
|
788
|
+
# Update progress after extraction completes (step-based mode)
|
|
789
|
+
if not smooth and hasattr(self, '_progress') and self._progress and not self.debug:
|
|
697
790
|
self._progress.update(1)
|
|
791
|
+
|
|
698
792
|
if self.instrumentalFileSpec:
|
|
699
793
|
self.instrumentalMode = True
|
|
700
794
|
self._build_instrumental_filters()
|
|
701
795
|
return [] # Return empty list for muteTimeList
|
|
702
796
|
except Exception as e:
|
|
703
797
|
# Fallback to mute if generation fails
|
|
798
|
+
if smooth:
|
|
799
|
+
self._smooth_ticker.stop()
|
|
704
800
|
if self.debug:
|
|
705
801
|
mmguero.eprint(f"Generation failed: {e}, falling back to mute mode")
|
|
706
802
|
self.instrumentalMode = False
|
|
707
803
|
return self._create_mute_beep_list()
|
|
708
804
|
else:
|
|
805
|
+
# No instrumental segments — remove extract estimate from smooth bar
|
|
806
|
+
if smooth and hasattr(self, '_progress') and self._progress:
|
|
807
|
+
extract_est = getattr(self, '_smooth_extract_est', 0)
|
|
808
|
+
self._smooth_ticker.adjust_total(-extract_est)
|
|
709
809
|
return []
|
|
710
810
|
|
|
811
|
+
else:
|
|
812
|
+
# No profanity found in auto mode — remove extract estimate if applicable
|
|
813
|
+
if smooth and hasattr(self, 'autoGenerateMode') and self.autoGenerateMode:
|
|
814
|
+
extract_est = getattr(self, '_smooth_extract_est', 0)
|
|
815
|
+
if extract_est > 0 and hasattr(self, '_progress') and self._progress:
|
|
816
|
+
self._smooth_ticker.adjust_total(-extract_est)
|
|
817
|
+
|
|
711
818
|
# Handle traditional instrumental file mode or mute/beep mode
|
|
712
819
|
if self.instrumentalMode:
|
|
713
820
|
return self._create_instrumental_splice_list()
|
|
@@ -912,28 +1019,84 @@ class Plugger(object):
|
|
|
912
1019
|
if (self.forceDespiteTag is True) or (GetMonkeyplugTagged(self.inputFileSpec, debug=self.debug) is False):
|
|
913
1020
|
# Initialize progress (only when not in debug mode)
|
|
914
1021
|
progress = None
|
|
1022
|
+
smooth_ticker = None
|
|
1023
|
+
step_timings = None
|
|
1024
|
+
timing_log = None
|
|
1025
|
+
file_duration = 0.0
|
|
1026
|
+
|
|
915
1027
|
if not self.debug:
|
|
916
|
-
#
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1028
|
+
# Load timing log and file duration for progress estimation
|
|
1029
|
+
timing_log = load_timing_log()
|
|
1030
|
+
file_duration = self._get_file_duration(self.inputFileSpec)
|
|
1031
|
+
step_timings = {}
|
|
1032
|
+
|
|
1033
|
+
# Determine which steps will run
|
|
1034
|
+
will_transcribe = not self.inputTranscript
|
|
1035
|
+
will_extract = hasattr(self, 'autoGenerateMode') and self.autoGenerateMode
|
|
1036
|
+
# encode always runs
|
|
1037
|
+
|
|
1038
|
+
# Check if we have estimates for all needed steps
|
|
1039
|
+
est_transcribe = estimate_step_duration(timing_log, 'transcribe', file_duration) if will_transcribe else None
|
|
1040
|
+
est_extract = estimate_step_duration(timing_log, 'extract', file_duration) if will_extract else None
|
|
1041
|
+
est_encode = estimate_step_duration(timing_log, 'encode', file_duration)
|
|
1042
|
+
|
|
1043
|
+
can_smooth = (
|
|
1044
|
+
file_duration > 0
|
|
1045
|
+
and est_encode is not None
|
|
1046
|
+
and (not will_transcribe or est_transcribe is not None)
|
|
1047
|
+
and (not will_extract or est_extract is not None)
|
|
928
1048
|
)
|
|
929
1049
|
|
|
1050
|
+
if can_smooth:
|
|
1051
|
+
# Smooth mode: single bar with total in seconds
|
|
1052
|
+
est_transcribe_val = est_transcribe or 0
|
|
1053
|
+
est_extract_val = est_extract or 0
|
|
1054
|
+
total_est = est_transcribe_val + est_extract_val + est_encode
|
|
1055
|
+
|
|
1056
|
+
initial_desc = "Transcribing" if will_transcribe else "Processing"
|
|
1057
|
+
progress = tqdm(
|
|
1058
|
+
total=total_est,
|
|
1059
|
+
desc=initial_desc,
|
|
1060
|
+
unit="s",
|
|
1061
|
+
disable=False,
|
|
1062
|
+
bar_format='{l_bar}{bar}| {n:.0f}/{total:.0f}s [{elapsed}<{remaining}]',
|
|
1063
|
+
)
|
|
1064
|
+
|
|
1065
|
+
smooth_ticker = _SmoothProgressTicker(progress)
|
|
1066
|
+
# Ticker will be started inside CreateCleanMuteList for each step
|
|
1067
|
+
|
|
1068
|
+
# Pass context to CreateCleanMuteList
|
|
1069
|
+
self._smooth_ticker = smooth_ticker
|
|
1070
|
+
self._smooth_cumulative = 0.0
|
|
1071
|
+
self._smooth_transcribe_est = est_transcribe_val
|
|
1072
|
+
self._smooth_extract_est = est_extract_val
|
|
1073
|
+
self._step_timings = {}
|
|
1074
|
+
self._timing_log = timing_log
|
|
1075
|
+
self._timing_file_duration = file_duration
|
|
1076
|
+
self._will_transcribe = will_transcribe
|
|
1077
|
+
else:
|
|
1078
|
+
# Fallback: step-based bar (existing behavior)
|
|
1079
|
+
initial_desc = "Transcribing" if not self.inputTranscript else "Processing"
|
|
1080
|
+
progress = tqdm(
|
|
1081
|
+
total=1,
|
|
1082
|
+
desc=initial_desc,
|
|
1083
|
+
unit="step",
|
|
1084
|
+
disable=False,
|
|
1085
|
+
bar_format='{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}]',
|
|
1086
|
+
)
|
|
1087
|
+
|
|
1088
|
+
# Always pass timing context (even in step-based mode, for data collection)
|
|
1089
|
+
self._step_timings = step_timings
|
|
1090
|
+
self._timing_file_duration = file_duration
|
|
1091
|
+
self._will_transcribe = not self.inputTranscript
|
|
1092
|
+
|
|
930
1093
|
# Store progress reference for use in CreateCleanMuteList
|
|
931
1094
|
self._progress = progress
|
|
932
1095
|
|
|
933
1096
|
self.CreateCleanMuteList()
|
|
934
1097
|
|
|
935
|
-
# Update progress after CreateCleanMuteList
|
|
936
|
-
if progress:
|
|
1098
|
+
# Update progress after CreateCleanMuteList (step-based mode only)
|
|
1099
|
+
if progress and not smooth_ticker:
|
|
937
1100
|
did_extraction = (
|
|
938
1101
|
hasattr(self, 'autoGenerateMode') and
|
|
939
1102
|
self.autoGenerateMode and
|
|
@@ -942,23 +1105,21 @@ class Plugger(object):
|
|
|
942
1105
|
)
|
|
943
1106
|
|
|
944
1107
|
if not self.inputTranscript and not did_extraction:
|
|
945
|
-
# Transcription done inside CreateCleanMuteList, no extraction
|
|
946
1108
|
progress.update(1)
|
|
947
1109
|
progress.total = 2
|
|
948
1110
|
progress.set_description("Encoding")
|
|
949
1111
|
elif not self.inputTranscript and did_extraction:
|
|
950
|
-
# Both transcription and extraction handled inside CreateCleanMuteList
|
|
951
|
-
# Just set description to encoding
|
|
952
1112
|
progress.set_description("Encoding")
|
|
953
1113
|
elif self.inputTranscript and did_extraction:
|
|
954
|
-
# Extraction handled inside CreateCleanMuteList (no transcription update needed)
|
|
955
1114
|
progress.total = 2
|
|
956
1115
|
progress.set_description("Encoding")
|
|
957
1116
|
else:
|
|
958
|
-
# No transcription, no extraction - just encoding
|
|
959
1117
|
progress.total = 1
|
|
960
1118
|
progress.set_description("Encoding")
|
|
961
1119
|
|
|
1120
|
+
# Get cumulative position after CreateCleanMuteList (smooth mode)
|
|
1121
|
+
cumulative = getattr(self, '_smooth_cumulative', 0.0) if smooth_ticker else 0
|
|
1122
|
+
|
|
962
1123
|
# Handle instrumental mode differently
|
|
963
1124
|
if self.instrumentalMode:
|
|
964
1125
|
# Use instrumental splicing
|
|
@@ -1030,6 +1191,15 @@ class Plugger(object):
|
|
|
1030
1191
|
ffmpegCmd.extend(self.aParams)
|
|
1031
1192
|
ffmpegCmd.append(self.outputFileSpec)
|
|
1032
1193
|
|
|
1194
|
+
# Start encode step with timing
|
|
1195
|
+
if progress and smooth_ticker:
|
|
1196
|
+
est_encode = estimate_step_duration(timing_log, 'encode', file_duration) or 0
|
|
1197
|
+
progress.set_description("Encoding")
|
|
1198
|
+
smooth_ticker.start(cumulative, est_encode)
|
|
1199
|
+
elif progress:
|
|
1200
|
+
progress.set_description("Encoding")
|
|
1201
|
+
encode_start = time.time()
|
|
1202
|
+
|
|
1033
1203
|
ffmpegResult, ffmpegOutput = mmguero.run_process(ffmpegCmd, stdout=True, stderr=True, debug=self.debug)
|
|
1034
1204
|
if (ffmpegResult != 0) or (not os.path.isfile(self.outputFileSpec)):
|
|
1035
1205
|
mmguero.eprint(' '.join(mmguero.flatten(ffmpegCmd)))
|
|
@@ -1037,21 +1207,43 @@ class Plugger(object):
|
|
|
1037
1207
|
mmguero.eprint(ffmpegOutput)
|
|
1038
1208
|
raise ValueError(f"Could not process {self.inputFileSpec}")
|
|
1039
1209
|
|
|
1210
|
+
# Record encode timing and finalize
|
|
1211
|
+
actual_encode = time.time() - encode_start
|
|
1212
|
+
if smooth_ticker:
|
|
1213
|
+
smooth_ticker.stop()
|
|
1214
|
+
step_timings['encode'] = (actual_encode, file_duration)
|
|
1215
|
+
|
|
1040
1216
|
SetMonkeyplugTag(self.outputFileSpec, debug=self.debug)
|
|
1041
1217
|
|
|
1042
|
-
# Complete progress
|
|
1218
|
+
# Complete progress and save timing data
|
|
1043
1219
|
if progress:
|
|
1044
|
-
|
|
1220
|
+
if smooth_ticker:
|
|
1221
|
+
# Snap bar to total
|
|
1222
|
+
progress.n = progress.total
|
|
1223
|
+
progress.refresh()
|
|
1224
|
+
else:
|
|
1225
|
+
progress.update(1)
|
|
1045
1226
|
progress.close()
|
|
1046
1227
|
|
|
1228
|
+
# Update timing log with actual measurements (only on success)
|
|
1229
|
+
if timing_log is not None and file_duration > 0:
|
|
1230
|
+
for op, (wall_secs, audio_secs) in step_timings.items():
|
|
1231
|
+
update_timing_measurement(timing_log, op, wall_secs, audio_secs)
|
|
1232
|
+
save_timing_log(timing_log)
|
|
1233
|
+
|
|
1047
1234
|
else:
|
|
1048
1235
|
shutil.copyfile(self.inputFileSpec, self.outputFileSpec)
|
|
1049
1236
|
if progress:
|
|
1050
1237
|
progress.close()
|
|
1051
1238
|
|
|
1052
|
-
# Clean up progress
|
|
1239
|
+
# Clean up progress references
|
|
1053
1240
|
if hasattr(self, '_progress'):
|
|
1054
1241
|
delattr(self, '_progress')
|
|
1242
|
+
for attr in ('_smooth_ticker', '_smooth_cumulative', '_smooth_extract_est',
|
|
1243
|
+
'_smooth_transcribe_est', '_will_transcribe',
|
|
1244
|
+
'_step_timings', '_timing_log', '_timing_file_duration'):
|
|
1245
|
+
if hasattr(self, attr):
|
|
1246
|
+
delattr(self, attr)
|
|
1055
1247
|
|
|
1056
1248
|
return self.outputFileSpec
|
|
1057
1249
|
|
|
@@ -1970,6 +2162,7 @@ def expand_and_detect_vocals(input_pattern, output_pattern, args, skip_detection
|
|
|
1970
2162
|
# Config file loading
|
|
1971
2163
|
MONKEYPLUG_CACHE_DIR = os.path.join(os.path.expanduser('~'), '.cache', 'monkeyplug')
|
|
1972
2164
|
MONKEYPLUG_CONFIG_PATH = os.path.join(MONKEYPLUG_CACHE_DIR, 'config.json')
|
|
2165
|
+
MONKEYPLUG_TIMING_LOG_PATH = os.path.join(MONKEYPLUG_CACHE_DIR, 'timing_log.json')
|
|
1973
2166
|
|
|
1974
2167
|
DEFAULT_CONFIG = {
|
|
1975
2168
|
"pad_milliseconds": 10,
|
|
@@ -2029,6 +2222,69 @@ def load_config_settings(debug=False):
|
|
|
2029
2222
|
return dict(DEFAULT_CONFIG)
|
|
2030
2223
|
|
|
2031
2224
|
|
|
2225
|
+
###################################################################################################
|
|
2226
|
+
# Timing log for progress estimation
|
|
2227
|
+
def load_timing_log():
|
|
2228
|
+
"""Load historical timing data for progress bar estimation.
|
|
2229
|
+
|
|
2230
|
+
Returns:
|
|
2231
|
+
dict: Timing log with per-operation running averages, or {} if unavailable.
|
|
2232
|
+
"""
|
|
2233
|
+
if not os.path.isfile(MONKEYPLUG_TIMING_LOG_PATH):
|
|
2234
|
+
return {}
|
|
2235
|
+
try:
|
|
2236
|
+
with open(MONKEYPLUG_TIMING_LOG_PATH, 'r') as f:
|
|
2237
|
+
data = json.load(f)
|
|
2238
|
+
if isinstance(data, dict):
|
|
2239
|
+
return data
|
|
2240
|
+
except (json.JSONDecodeError, IOError, ValueError):
|
|
2241
|
+
pass
|
|
2242
|
+
return {}
|
|
2243
|
+
|
|
2244
|
+
|
|
2245
|
+
def save_timing_log(timing_log):
|
|
2246
|
+
"""Save timing log atomically to disk."""
|
|
2247
|
+
try:
|
|
2248
|
+
os.makedirs(os.path.dirname(MONKEYPLUG_TIMING_LOG_PATH), exist_ok=True)
|
|
2249
|
+
tmp_path = MONKEYPLUG_TIMING_LOG_PATH + '.tmp'
|
|
2250
|
+
with open(tmp_path, 'w') as f:
|
|
2251
|
+
json.dump(timing_log, f, indent=2)
|
|
2252
|
+
f.write('\n')
|
|
2253
|
+
os.replace(tmp_path, MONKEYPLUG_TIMING_LOG_PATH)
|
|
2254
|
+
except (IOError, OSError):
|
|
2255
|
+
pass # Best-effort
|
|
2256
|
+
|
|
2257
|
+
|
|
2258
|
+
def estimate_step_duration(timing_log, operation, audio_seconds):
|
|
2259
|
+
"""Estimate wall-clock seconds for an operation based on historical data.
|
|
2260
|
+
|
|
2261
|
+
Returns:
|
|
2262
|
+
float or None: Estimated seconds, or None if no data available.
|
|
2263
|
+
"""
|
|
2264
|
+
entry = timing_log.get(operation)
|
|
2265
|
+
if not entry or entry.get('run_count', 0) == 0:
|
|
2266
|
+
return None
|
|
2267
|
+
total_audio = entry.get('total_audio_seconds', 0)
|
|
2268
|
+
if total_audio <= 0:
|
|
2269
|
+
return None
|
|
2270
|
+
rate = entry['total_wall_seconds'] / total_audio
|
|
2271
|
+
return rate * audio_seconds
|
|
2272
|
+
|
|
2273
|
+
|
|
2274
|
+
def update_timing_measurement(timing_log, operation, wall_seconds, audio_seconds):
|
|
2275
|
+
"""Add a new timing measurement to the running averages."""
|
|
2276
|
+
if operation not in timing_log:
|
|
2277
|
+
timing_log[operation] = {
|
|
2278
|
+
'total_audio_seconds': 0.0,
|
|
2279
|
+
'total_wall_seconds': 0.0,
|
|
2280
|
+
'run_count': 0,
|
|
2281
|
+
}
|
|
2282
|
+
entry = timing_log[operation]
|
|
2283
|
+
entry['total_audio_seconds'] += audio_seconds
|
|
2284
|
+
entry['total_wall_seconds'] += wall_seconds
|
|
2285
|
+
entry['run_count'] += 1
|
|
2286
|
+
|
|
2287
|
+
|
|
2032
2288
|
###################################################################################################
|
|
2033
2289
|
# RunMonkeyPlug
|
|
2034
2290
|
def RunMonkeyPlug():
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{monkeyplug_enhanced-2.2.3 → monkeyplug_enhanced-2.2.4}/src/monkeyplug/data/profanity_list.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|