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()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|