Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -4,9 +4,6 @@ Gradio app to convert one or more MIDI files into Geometry Dash .gmd files
|
|
| 4 |
(using midi_to_gd.py). For each uploaded MIDI the app will create a .gmd
|
| 5 |
file (plist wrapper with compressed k4 level string) and return a zip
|
| 6 |
containing all generated .gmd files.
|
| 7 |
-
|
| 8 |
-
Place this script in the same folder as midi_to_gd.py (or ensure the
|
| 9 |
-
module is importable via PYTHONPATH).
|
| 10 |
"""
|
| 11 |
import os
|
| 12 |
import sys
|
|
@@ -15,7 +12,6 @@ import zipfile
|
|
| 15 |
import traceback
|
| 16 |
from typing import List, Tuple
|
| 17 |
|
| 18 |
-
# Ensure the local module is importable
|
| 19 |
HERE = os.path.dirname(os.path.abspath(__file__))
|
| 20 |
if HERE not in sys.path:
|
| 21 |
sys.path.insert(0, HERE)
|
|
@@ -39,15 +35,24 @@ except Exception as e:
|
|
| 39 |
|
| 40 |
def _get_local_path_from_gr_file(f) -> str:
|
| 41 |
"""
|
| 42 |
-
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
| 44 |
"""
|
| 45 |
-
if
|
| 46 |
return ""
|
| 47 |
if isinstance(f, str):
|
| 48 |
return f
|
| 49 |
-
|
| 50 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
|
| 52 |
|
| 53 |
def convert_midis_to_zip(midi_files: List, beats_scale: float = 2.0, name_prefix: str = "") -> Tuple[str, str]:
|
|
@@ -65,23 +70,18 @@ def convert_midis_to_zip(midi_files: List, beats_scale: float = 2.0, name_prefix
|
|
| 65 |
out_dir = tempfile.mkdtemp(prefix="gd_midis_")
|
| 66 |
created_files = []
|
| 67 |
|
|
|
|
| 68 |
for idx, f in enumerate(midi_files, start=1):
|
| 69 |
midi_path = _get_local_path_from_gr_file(f)
|
| 70 |
-
if not os.path.exists(midi_path):
|
| 71 |
-
# Gradio might pass dicts in some versions; try to handle that
|
| 72 |
-
if isinstance(f, dict) and "name" in f:
|
| 73 |
-
midi_path = f["name"]
|
| 74 |
if not os.path.exists(midi_path):
|
| 75 |
return None, f"Uploaded file not found on disk: {midi_path}"
|
| 76 |
|
| 77 |
base = os.path.splitext(os.path.basename(midi_path))[0]
|
| 78 |
level_name = f"{name_prefix}{'_' if name_prefix else ''}{base}" if name_prefix else base
|
| 79 |
|
| 80 |
-
# Parse MIDI and generate object string
|
| 81 |
notes = parse_midi_notes(midi_path)
|
| 82 |
raw_objects = generate_gd_level_objects(notes, blocks_per_beat=beats_scale)
|
| 83 |
|
| 84 |
-
# Build compressed/encrypted k4 string and .gmd plist
|
| 85 |
full_level_data = build_full_level_string(raw_objects)
|
| 86 |
encrypted_k4 = encrypt_level_string(full_level_data)
|
| 87 |
|
|
@@ -101,7 +101,6 @@ def convert_midis_to_zip(midi_files: List, beats_scale: float = 2.0, name_prefix
|
|
| 101 |
wf.write(gmd_content)
|
| 102 |
created_files.append(out_path)
|
| 103 |
|
| 104 |
-
# Create a zip containing all .gmd files
|
| 105 |
zip_path = os.path.join(out_dir, "gd_levels.zip")
|
| 106 |
with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED) as zf:
|
| 107 |
for p in created_files:
|
|
@@ -128,7 +127,8 @@ def launch_ui():
|
|
| 128 |
)
|
| 129 |
|
| 130 |
with gr.Row():
|
| 131 |
-
|
|
|
|
| 132 |
settings_col = gr.Column(scale=1)
|
| 133 |
beats_scale = gr.Number(value=2.0, label="Blocks per beat (blocks/quarter-note)", precision=2)
|
| 134 |
name_prefix = gr.Textbox(label="Optional level name prefix", placeholder="Prefix (optional)")
|
|
@@ -139,7 +139,6 @@ def launch_ui():
|
|
| 139 |
|
| 140 |
def _on_click(files, beats, prefix):
|
| 141 |
zip_path, status_text = convert_midis_to_zip(files, beats, prefix)
|
| 142 |
-
# Gradio expects a file path to return for gr.File
|
| 143 |
return zip_path, status_text
|
| 144 |
|
| 145 |
convert_btn.click(fn=_on_click, inputs=[midi_input, beats_scale, name_prefix], outputs=[output_zip, status])
|
|
@@ -151,4 +150,6 @@ def launch_ui():
|
|
| 151 |
|
| 152 |
|
| 153 |
if __name__ == "__main__":
|
| 154 |
-
launch_ui()
|
|
|
|
|
|
|
|
|
| 4 |
(using midi_to_gd.py). For each uploaded MIDI the app will create a .gmd
|
| 5 |
file (plist wrapper with compressed k4 level string) and return a zip
|
| 6 |
containing all generated .gmd files.
|
|
|
|
|
|
|
|
|
|
| 7 |
"""
|
| 8 |
import os
|
| 9 |
import sys
|
|
|
|
| 12 |
import traceback
|
| 13 |
from typing import List, Tuple
|
| 14 |
|
|
|
|
| 15 |
HERE = os.path.dirname(os.path.abspath(__file__))
|
| 16 |
if HERE not in sys.path:
|
| 17 |
sys.path.insert(0, HERE)
|
|
|
|
| 35 |
|
| 36 |
def _get_local_path_from_gr_file(f) -> str:
|
| 37 |
"""
|
| 38 |
+
Accepts the various shapes Gradio may pass:
|
| 39 |
+
- a filesystem path string
|
| 40 |
+
- a dict like {'name': '/tmp/....'}
|
| 41 |
+
- a list of the above (we only call this per-file)
|
| 42 |
+
Returns a single filesystem path string or empty if not found.
|
| 43 |
"""
|
| 44 |
+
if not f:
|
| 45 |
return ""
|
| 46 |
if isinstance(f, str):
|
| 47 |
return f
|
| 48 |
+
if isinstance(f, (list, tuple)) and f:
|
| 49 |
+
# take first element
|
| 50 |
+
return _get_local_path_from_gr_file(f[0])
|
| 51 |
+
if isinstance(f, dict):
|
| 52 |
+
# Gradio sometimes returns {'name': '/tmp/...'}
|
| 53 |
+
return f.get("name") or f.get("file") or ""
|
| 54 |
+
# Fallback: try attribute .name
|
| 55 |
+
return getattr(f, "name", "")
|
| 56 |
|
| 57 |
|
| 58 |
def convert_midis_to_zip(midi_files: List, beats_scale: float = 2.0, name_prefix: str = "") -> Tuple[str, str]:
|
|
|
|
| 70 |
out_dir = tempfile.mkdtemp(prefix="gd_midis_")
|
| 71 |
created_files = []
|
| 72 |
|
| 73 |
+
# midi_files is expected to be a list of file paths (type="filepath")
|
| 74 |
for idx, f in enumerate(midi_files, start=1):
|
| 75 |
midi_path = _get_local_path_from_gr_file(f)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
if not os.path.exists(midi_path):
|
| 77 |
return None, f"Uploaded file not found on disk: {midi_path}"
|
| 78 |
|
| 79 |
base = os.path.splitext(os.path.basename(midi_path))[0]
|
| 80 |
level_name = f"{name_prefix}{'_' if name_prefix else ''}{base}" if name_prefix else base
|
| 81 |
|
|
|
|
| 82 |
notes = parse_midi_notes(midi_path)
|
| 83 |
raw_objects = generate_gd_level_objects(notes, blocks_per_beat=beats_scale)
|
| 84 |
|
|
|
|
| 85 |
full_level_data = build_full_level_string(raw_objects)
|
| 86 |
encrypted_k4 = encrypt_level_string(full_level_data)
|
| 87 |
|
|
|
|
| 101 |
wf.write(gmd_content)
|
| 102 |
created_files.append(out_path)
|
| 103 |
|
|
|
|
| 104 |
zip_path = os.path.join(out_dir, "gd_levels.zip")
|
| 105 |
with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED) as zf:
|
| 106 |
for p in created_files:
|
|
|
|
| 127 |
)
|
| 128 |
|
| 129 |
with gr.Row():
|
| 130 |
+
# NOTE: type="filepath" is the correct modern API. 'file' is invalid.
|
| 131 |
+
midi_input = gr.File(label="Upload MIDI files (.mid/.midi)", file_count="multiple", type="filepath")
|
| 132 |
settings_col = gr.Column(scale=1)
|
| 133 |
beats_scale = gr.Number(value=2.0, label="Blocks per beat (blocks/quarter-note)", precision=2)
|
| 134 |
name_prefix = gr.Textbox(label="Optional level name prefix", placeholder="Prefix (optional)")
|
|
|
|
| 139 |
|
| 140 |
def _on_click(files, beats, prefix):
|
| 141 |
zip_path, status_text = convert_midis_to_zip(files, beats, prefix)
|
|
|
|
| 142 |
return zip_path, status_text
|
| 143 |
|
| 144 |
convert_btn.click(fn=_on_click, inputs=[midi_input, beats_scale, name_prefix], outputs=[output_zip, status])
|
|
|
|
| 150 |
|
| 151 |
|
| 152 |
if __name__ == "__main__":
|
| 153 |
+
launch_ui()
|
| 154 |
+
|
| 155 |
+
|