Skip to content

Commit

Permalink
Fix getting length from libopenmpt as infinity, which could crash wit…
Browse files Browse the repository at this point in the history
…h Protracker modules (#1389)

* Handle infinity in get_display_time()

* Use question marks instead to follow the same format

* Small touchups

* Fix tag_scan() mishandling openmpt lengths and add some small touchups
  • Loading branch information
C0rn3j authored Jan 14, 2025
1 parent 3f13ca5 commit 837fe1d
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 25 deletions.
5 changes: 4 additions & 1 deletion src/tauon/t_modules/t_extra.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,11 @@ def rm_16(line: str) -> str:
return line


def get_display_time(seconds: str) -> str:
def get_display_time(seconds: float) -> str:
"""Returns a string from seconds to a compact time format, e.g 2h:23"""
if math.isinf(seconds) or math.isnan(seconds):
logging.error("Infinite/NaN time passed to get_display_time()!")
return "??:??"
result = divmod(int(seconds), 60)
if result[0] > 99:
result = divmod(result[0], 60)
Expand Down
41 changes: 17 additions & 24 deletions src/tauon/t_modules/t_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4325,7 +4325,6 @@ def tag_scan(nt: TrackClass) -> TrackClass | None:
nt.file_ext = os.path.splitext(os.path.basename(nt.fullpath))[1][1:].upper()

if nt.file_ext.lower() in GME_Formats and gme:

emu = ctypes.c_void_p()
track_info = ctypes.POINTER(GMETrackInfo)()
err = gme.gme_open_file(nt.fullpath.encode("utf-8"), ctypes.byref(emu), -1)
Expand Down Expand Up @@ -4374,21 +4373,19 @@ def tag_scan(nt: TrackClass) -> TrackClass | None:
break
if not nt.title:
nt.title = "Track " + str(nt.subtrack + 1)

elif nt.file_ext in ("MOD", "IT", "XM", "S3M", "MPTM") and mpt:
with Path(nt.fullpath).open("rb") as file:
data = file.read()
MOD1 = MOD.from_address(
mpt.openmpt_module_create_from_memory(
ctypes.c_char_p(data), ctypes.c_size_t(len(data)), None, None, None))
# The function may return infinity if the pattern data is too complex to evaluate
nt.length = mpt.openmpt_module_get_duration_seconds(byref(MOD1))
nt.title = mpt.openmpt_module_get_metadata(byref(MOD1), ctypes.c_char_p(b"title")).decode()
nt.artist = mpt.openmpt_module_get_metadata(byref(MOD1), ctypes.c_char_p(b"artist")).decode()
nt.comment = mpt.openmpt_module_get_metadata(byref(MOD1), ctypes.c_char_p(b"message_raw")).decode()

mpt.openmpt_module_destroy(byref(MOD1))
del MOD1

elif nt.file_ext == "FLAC":
with Flac(nt.fullpath) as audio:
audio.read()
Expand Down Expand Up @@ -4644,8 +4641,6 @@ def in_get(key, tags):
nt.misc["artists"] = []
if a not in nt.misc["artists"]:
nt.misc["artists"].append(a)


except Exception:
try:
if Exception is UnicodeDecodeError:
Expand All @@ -4655,10 +4650,13 @@ def in_get(key, tags):
except Exception:
logging.exception("Error printing error. Non utf8 not allowed:", nt.fullpath.encode("utf-8", "surrogateescape").decode("utf-8", "replace"), "\n")
return nt

# This check won't guarantee that all codepaths above are checked as some return early, but it's better than nothing
# And importantly it does catch openmpt which can actually return such
if math.isinf(nt.length) or math.isnan(nt.length):
logging.error(f"Infinite/NaN found(autocorrected to 0) when scanning tags in file: {vars(nt)}!")
nt.length = 0
return nt


def get_radio_art() -> None:
if radiobox.loaded_url in radiobox.websocket_source_urls:
return
Expand Down Expand Up @@ -24349,7 +24347,7 @@ def cue_scan(content: str, tn: TrackClass) -> int | None:
pctl.master_library[pctl.master_count] = nt

cued.append(pctl.master_count)
# loaded_pathes_cache[filepath.replace('\\', '/')] = pctl.master_count
# loaded_paths_cache[filepath.replace('\\', '/')] = pctl.master_count
# added.append(pctl.master_count)

pctl.master_count += 1
Expand Down Expand Up @@ -25707,7 +25705,7 @@ def worker1():
global to_get
global to_got

loaded_pathes_cache = {}
loaded_paths_cache = {}
loaded_cue_cache = {}
added = []

Expand Down Expand Up @@ -26000,7 +25998,7 @@ def add_from_cue(path):
pctl.master_library[track.index] = track
if track.fullpath not in cue_list:
cue_list.append(track.fullpath)
loaded_pathes_cache[track.fullpath] = track.index
loaded_paths_cache[track.fullpath] = track.index
added.append(track.index)

except Exception:
Expand Down Expand Up @@ -26152,8 +26150,8 @@ def add_file(path, force_scan: bool = False) -> int | None:

path = path.replace("\\", "/")

if path in loaded_pathes_cache:
de = loaded_pathes_cache[path]
if path in loaded_paths_cache:
de = loaded_paths_cache[path]

if pctl.master_library[de].fullpath in cue_list:
logging.info("File has an associated .cue file... Skipping")
Expand Down Expand Up @@ -26628,14 +26626,14 @@ def cache_paths():
if loaderCommand == LC_Folder:
to_get = 0
to_got = 0
loaded_pathes_cache, loaded_cue_cache = cache_paths()
loaded_paths_cache, loaded_cue_cache = cache_paths()
# pre_get(order.target)
if order.force_scan:
gets(order.target, force_scan=True)
else:
gets(order.target)
elif loaderCommand == LC_File:
loaded_pathes_cache, loaded_cue_cache = cache_paths()
loaded_paths_cache, loaded_cue_cache = cache_paths()
add_file(order.target)

if gui.im_cancel:
Expand Down Expand Up @@ -33867,9 +33865,7 @@ def line_render(n_track: TrackClass, p_track: TrackClass, y, this_line_playing,

star_x += round(70 * gui.scale)

if gui.star_mode == "star" and total > 0 and pctl.master_library[
index].length != 0:

if gui.star_mode == "star" and total > 0 and pctl.master_library[index].length != 0:
sx = width + start_x - 40 * gui.scale - offset_font_extra
sy = ry + (gui.playlist_row_height // 2) - (6 * gui.scale)
# if gui.scale == 1.25:
Expand Down Expand Up @@ -35103,7 +35099,7 @@ def full_render(self):
total += 2
ratio = total / (n_track.length - 1)

text = str(str(int(ratio)))
text = str(int(ratio))
if text == "0":
text = ""
colour = colours.index_text
Expand Down Expand Up @@ -43486,18 +43482,15 @@ def drop_file(target):
power += 400

if power < 500:

time.sleep(0.03)

if (
pctl.playing_state == 0 or pctl.playing_state == 2) and not load_orders and gui.update == 0 and not tauon.gall_ren.queue and not transcode_list and not gui.frame_callback_list:
if (pctl.playing_state == 0 or pctl.playing_state == 2) and not load_orders and gui.update == 0 \
and not tauon.gall_ren.queue and not transcode_list and not gui.frame_callback_list:
pass
else:
sleep_timer.set()
if sleep_timer.get() > 2:
SDL_WaitEventTimeout(None, 1000)
continue

else:
power = 0

Expand Down

0 comments on commit 837fe1d

Please sign in to comment.