Update app.py
Browse files
app.py
CHANGED
@@ -21,7 +21,6 @@ Major updates:
|
|
21 |
import sys
|
22 |
import subprocess
|
23 |
from typing import Dict, Optional, Tuple, List
|
24 |
-
import os
|
25 |
|
26 |
def install(packages: List[str]):
|
27 |
for package in packages:
|
@@ -37,25 +36,6 @@ install([
|
|
37 |
"uuid", "datetime"
|
38 |
])
|
39 |
|
40 |
-
# Download VexFlow for local use if it doesn't exist
|
41 |
-
def download_vexflow():
|
42 |
-
static_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "static")
|
43 |
-
os.makedirs(static_dir, exist_ok=True)
|
44 |
-
vexflow_path = os.path.join(static_dir, "vexflow.js")
|
45 |
-
|
46 |
-
if not os.path.exists(vexflow_path):
|
47 |
-
try:
|
48 |
-
import requests
|
49 |
-
print("Downloading VexFlow for local use...")
|
50 |
-
response = requests.get("https://cdn.jsdelivr.net/npm/[email protected]/build/cjs/vexflow.js")
|
51 |
-
with open(vexflow_path, "wb") as f:
|
52 |
-
f.write(response.content)
|
53 |
-
print("VexFlow downloaded successfully.")
|
54 |
-
except Exception as e:
|
55 |
-
print(f"Error downloading VexFlow: {e}")
|
56 |
-
|
57 |
-
download_vexflow()
|
58 |
-
|
59 |
# -----------------------------------------------------------------------------
|
60 |
# 2. Static imports
|
61 |
# -----------------------------------------------------------------------------
|
@@ -811,27 +791,8 @@ def create_vexflow_notation(json_data, time_sig, key_sig):
|
|
811 |
<html>
|
812 |
<head>
|
813 |
<meta charset="utf-8">
|
814 |
-
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net https://unpkg.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' https://cdn.jsdelivr.net https://unpkg.com">
|
815 |
<title>Music Notation</title>
|
816 |
-
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/cjs/vexflow.js"
|
817 |
-
<script>
|
818 |
-
// Fallback if the CDN fails
|
819 |
-
window.addEventListener('error', function(e) {{
|
820 |
-
if (e.target.src && e.target.src.includes('vexflow')) {{
|
821 |
-
console.log('VexFlow CDN failed, loading local fallback');
|
822 |
-
var fallbackScript = document.createElement('script');
|
823 |
-
// Try local copy first, then unpkg as second fallback
|
824 |
-
fallbackScript.src = '/static/vexflow.js';
|
825 |
-
fallbackScript.onerror = function() {{
|
826 |
-
console.log('Local VexFlow failed, trying unpkg');
|
827 |
-
var unpkgScript = document.createElement('script');
|
828 |
-
unpkgScript.src = 'https://unpkg.com/[email protected]/build/cjs/vexflow.js';
|
829 |
-
document.head.appendChild(unpkgScript);
|
830 |
-
}};
|
831 |
-
document.head.appendChild(fallbackScript);
|
832 |
-
}}
|
833 |
-
}}, true);
|
834 |
-
</script>
|
835 |
<style>
|
836 |
#output {{width: 100%; overflow: auto;}}
|
837 |
body {{font-family: Arial, sans-serif;}}
|
@@ -839,18 +800,18 @@ def create_vexflow_notation(json_data, time_sig, key_sig):
|
|
839 |
</style>
|
840 |
</head>
|
841 |
<body>
|
842 |
-
<h2>Exercise in {
|
843 |
<div id="output"></div>
|
844 |
<script>
|
845 |
const {{Factory, EasyScore, System}} = Vex.Flow;
|
846 |
|
847 |
// Create VexFlow factory and context
|
848 |
-
const vf = new Factory({renderer: {elementId: 'output', width: 1200, height:
|
849 |
const score = vf.EasyScore();
|
850 |
const system = vf.System();
|
851 |
|
852 |
// Parse notes from JSON
|
853 |
-
const jsonData = {
|
854 |
|
855 |
// Convert to VexFlow notation
|
856 |
let vexNotes = [];
|
@@ -884,7 +845,7 @@ def create_vexflow_notation(json_data, time_sig, key_sig):
|
|
884 |
let vexNote;
|
885 |
|
886 |
if (isRest) {{
|
887 |
-
vexNote = `B4/${durationToVex(duration)}/r`;
|
888 |
}} else {{
|
889 |
// Convert scientific notation to VexFlow format
|
890 |
// VexFlow uses lowercase for note names
|
@@ -892,10 +853,10 @@ def create_vexflow_notation(json_data, time_sig, key_sig):
|
|
892 |
const match = noteName.match(noteRegex);
|
893 |
if (match) {{
|
894 |
const [_, pitch, octave] = match;
|
895 |
-
vexNote = `${pitch.toLowerCase()}${octave}/${durationToVex(duration)}`;
|
896 |
}} else {{
|
897 |
// Default if parsing fails
|
898 |
-
vexNote = `c4/${durationToVex(duration)}`;
|
899 |
}}
|
900 |
}}
|
901 |
|
@@ -919,65 +880,26 @@ def create_vexflow_notation(json_data, time_sig, key_sig):
|
|
919 |
const staves = [];
|
920 |
const measuresPerLine = 4;
|
921 |
|
922 |
-
// Create both treble and bass clef staves
|
923 |
for (let i = 0; i < vexNotes.length; i += measuresPerLine) {{
|
|
|
924 |
const lineNotes = vexNotes.slice(i, i + measuresPerLine);
|
925 |
|
926 |
-
// Create a new system for each line
|
927 |
-
const lineSystem = vf.System({{width: 1100
|
928 |
|
929 |
-
//
|
930 |
-
const trebleStave = lineSystem.addStave({{
|
931 |
-
voices: []
|
932 |
-
}}).addClef('treble');
|
933 |
-
|
934 |
-
// Create bass clef stave
|
935 |
-
const bassStave = lineSystem.addStave({{
|
936 |
-
voices: []
|
937 |
-
}}).addClef('bass');
|
938 |
-
|
939 |
-
// Add time signature and key to first system
|
940 |
-
if (i === 0) {{
|
941 |
-
trebleStave.addTimeSignature(timeSignature);
|
942 |
-
trebleStave.addKeySignature("{{key_sig.split()[0]}}");
|
943 |
-
bassStave.addTimeSignature(timeSignature);
|
944 |
-
bassStave.addKeySignature("{{key_sig.split()[0]}}");
|
945 |
-
}}
|
946 |
-
|
947 |
-
// Add notes to treble stave
|
948 |
-
lineNotes.forEach((measure, index) => {
|
949 |
-
trebleStave.addVoice(
|
950 |
-
score.voice(score.notes(measure.join(', ')))
|
951 |
-
);
|
952 |
-
});
|
953 |
-
|
954 |
-
// Add same notes to bass stave but an octave lower
|
955 |
lineNotes.forEach((measure, index) => {{
|
956 |
-
|
957 |
-
|
958 |
-
|
959 |
-
|
960 |
-
|
961 |
-
// Extract note and duration
|
962 |
-
const [notePart, durationPart] = note.split('/');
|
963 |
-
|
964 |
-
// Lower the octave for bass clef if possible
|
965 |
-
if (notePart.match(/[a-g][#b]?\d/)) {{
|
966 |
-
const noteChar = notePart.charAt(0);
|
967 |
-
const accidental = notePart.match(/[#b]/) ? notePart.match(/[#b]/)[0] : '';
|
968 |
-
const octave = parseInt(notePart.match(/\d/)[0]);
|
969 |
-
|
970 |
-
// Lower octave by 2 for bass clef
|
971 |
-
const newOctave = Math.max(2, octave - 2);
|
972 |
-
return `${noteChar}${accidental}${newOctave}/${durationPart}`;
|
973 |
-
}}
|
974 |
-
|
975 |
-
return note;
|
976 |
}});
|
977 |
|
978 |
-
|
979 |
-
|
980 |
-
|
|
|
|
|
981 |
}});
|
982 |
|
983 |
lineSystem.addConnector("singleRight");
|
@@ -1374,18 +1296,6 @@ def create_ui() -> gr.Blocks:
|
|
1374 |
# -----------------------------------------------------------------------------
|
1375 |
# 14. Entry point
|
1376 |
# -----------------------------------------------------------------------------
|
1377 |
-
def is_running_on_huggingface():
|
1378 |
-
"""Check if the app is running on Hugging Face Spaces"""
|
1379 |
-
return os.environ.get('SPACE_ID') is not None or os.environ.get('SYSTEM') == 'spaces'
|
1380 |
-
|
1381 |
if __name__ == "__main__":
|
1382 |
demo = create_ui()
|
1383 |
-
|
1384 |
-
# Configure based on environment
|
1385 |
-
if is_running_on_huggingface():
|
1386 |
-
print("Running on Hugging Face Spaces")
|
1387 |
-
# Configure for Hugging Face Spaces
|
1388 |
-
demo.launch(share=False, server_name="0.0.0.0", server_port=7860, allowed_paths=["static", "temp_audio"])
|
1389 |
-
else:
|
1390 |
-
# Local development
|
1391 |
-
demo.launch()
|
|
|
21 |
import sys
|
22 |
import subprocess
|
23 |
from typing import Dict, Optional, Tuple, List
|
|
|
24 |
|
25 |
def install(packages: List[str]):
|
26 |
for package in packages:
|
|
|
36 |
"uuid", "datetime"
|
37 |
])
|
38 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
# -----------------------------------------------------------------------------
|
40 |
# 2. Static imports
|
41 |
# -----------------------------------------------------------------------------
|
|
|
791 |
<html>
|
792 |
<head>
|
793 |
<meta charset="utf-8">
|
|
|
794 |
<title>Music Notation</title>
|
795 |
+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/cjs/vexflow.js"></script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
796 |
<style>
|
797 |
#output {{width: 100%; overflow: auto;}}
|
798 |
body {{font-family: Arial, sans-serif;}}
|
|
|
800 |
</style>
|
801 |
</head>
|
802 |
<body>
|
803 |
+
<h2>Exercise in {key_sig}, {time_sig}</h2>
|
804 |
<div id="output"></div>
|
805 |
<script>
|
806 |
const {{Factory, EasyScore, System}} = Vex.Flow;
|
807 |
|
808 |
// Create VexFlow factory and context
|
809 |
+
const vf = new Factory({{renderer: {{elementId: 'output', width: 1200, height: 200}}}});
|
810 |
const score = vf.EasyScore();
|
811 |
const system = vf.System();
|
812 |
|
813 |
// Parse notes from JSON
|
814 |
+
const jsonData = {json.dumps(parsed)};
|
815 |
|
816 |
// Convert to VexFlow notation
|
817 |
let vexNotes = [];
|
|
|
845 |
let vexNote;
|
846 |
|
847 |
if (isRest) {{
|
848 |
+
vexNote = `B4/${{durationToVex(duration)}}/r`;
|
849 |
}} else {{
|
850 |
// Convert scientific notation to VexFlow format
|
851 |
// VexFlow uses lowercase for note names
|
|
|
853 |
const match = noteName.match(noteRegex);
|
854 |
if (match) {{
|
855 |
const [_, pitch, octave] = match;
|
856 |
+
vexNote = `${{pitch.toLowerCase()}}${{octave}}/${{durationToVex(duration)}}`;
|
857 |
}} else {{
|
858 |
// Default if parsing fails
|
859 |
+
vexNote = `c4/${{durationToVex(duration)}}`;
|
860 |
}}
|
861 |
}}
|
862 |
|
|
|
880 |
const staves = [];
|
881 |
const measuresPerLine = 4;
|
882 |
|
|
|
883 |
for (let i = 0; i < vexNotes.length; i += measuresPerLine) {{
|
884 |
+
const lineStaves = [];
|
885 |
const lineNotes = vexNotes.slice(i, i + measuresPerLine);
|
886 |
|
887 |
+
// Create a new system for each line
|
888 |
+
const lineSystem = vf.System({{width: 1100}});
|
889 |
|
890 |
+
// Add staves for each measure in the line
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
891 |
lineNotes.forEach((measure, index) => {{
|
892 |
+
const stave = lineSystem.addStave({{
|
893 |
+
voices: [
|
894 |
+
score.voice(score.notes(measure.join(', ')))
|
895 |
+
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
896 |
}});
|
897 |
|
898 |
+
// Add time signature and key to first measure of first line
|
899 |
+
if (i === 0 && index === 0) {{
|
900 |
+
stave.addTimeSignature(timeSignature);
|
901 |
+
stave.addKeySignature("{key_sig.split()[0]}");
|
902 |
+
}}
|
903 |
}});
|
904 |
|
905 |
lineSystem.addConnector("singleRight");
|
|
|
1296 |
# -----------------------------------------------------------------------------
|
1297 |
# 14. Entry point
|
1298 |
# -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
1299 |
if __name__ == "__main__":
|
1300 |
demo = create_ui()
|
1301 |
+
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|