Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -27,7 +27,6 @@ from wordpress_xmlrpc.methods import media
|
|
27 |
import smtplib
|
28 |
from email.mime.text import MIMEText
|
29 |
from email.mime.multipart import MIMEMultipart
|
30 |
-
import math
|
31 |
|
32 |
# Add enhanced CSS to block download buttons in audio players
|
33 |
st.markdown("""
|
@@ -397,7 +396,7 @@ def show_sidebar():
|
|
397 |
# Format for premium users - CORRECTED LIMITS PER TIER
|
398 |
daily_limit = 0
|
399 |
download_limit = 0
|
400 |
-
duration_limit =
|
401 |
|
402 |
plan_id = subscription_status.get('subscription_plan_id')
|
403 |
if isinstance(plan_id, int):
|
@@ -1328,258 +1327,7 @@ def load_and_play_generated_audio(response, genre=None, energy_level=None, tempo
|
|
1328 |
st.error(f"Failed to process generated audio: {e}")
|
1329 |
return None, None
|
1330 |
|
1331 |
-
def generate_long_audio_chunked(genre, energy_level, tempo, description, total_duration):
|
1332 |
-
"""
|
1333 |
-
Generate long audio by making multiple sequential API calls and stitching the results
|
1334 |
-
with improved error handling and robustness for different audio formats
|
1335 |
-
"""
|
1336 |
-
# Maximum duration for a single request that works reliably
|
1337 |
-
max_chunk_duration = 120 # Based on your working threshold
|
1338 |
-
|
1339 |
-
# Calculate how many chunks we need
|
1340 |
-
num_chunks = math.ceil(total_duration / max_chunk_duration)
|
1341 |
-
|
1342 |
-
# Prepare to collect audio pieces
|
1343 |
-
audio_chunks = []
|
1344 |
-
sample_rate = None
|
1345 |
-
|
1346 |
-
# Process each chunk
|
1347 |
-
overall_progress = st.progress(0)
|
1348 |
-
status_text = st.empty()
|
1349 |
-
|
1350 |
-
try:
|
1351 |
-
for i in range(num_chunks):
|
1352 |
-
# Calculate duration for this chunk
|
1353 |
-
chunk_duration = min(max_chunk_duration, total_duration - (i * max_chunk_duration))
|
1354 |
-
|
1355 |
-
# Update the description for continuity
|
1356 |
-
chunk_description = description
|
1357 |
-
if i > 0:
|
1358 |
-
chunk_description += f" (continuation part {i+1})"
|
1359 |
-
|
1360 |
-
# Update status
|
1361 |
-
status_text.markdown(f"π΅ **Generating part {i+1} of {num_chunks}** ({chunk_duration}s)...")
|
1362 |
-
|
1363 |
-
# Prepare and send the request
|
1364 |
-
prompt = f"Genre: {genre}, Energy Level: {energy_level}, Tempo: {tempo}, Description: {chunk_description}"
|
1365 |
-
payload = {"inputs": {"prompt": prompt, "duration": chunk_duration}}
|
1366 |
-
api_headers = get_api_headers()
|
1367 |
-
|
1368 |
-
# Make the request with appropriate timeout
|
1369 |
-
response = time_post_request(
|
1370 |
-
API_URL,
|
1371 |
-
headers=api_headers,
|
1372 |
-
payload=payload,
|
1373 |
-
timeout=1200 # Match the endpoint timeout
|
1374 |
-
)
|
1375 |
-
|
1376 |
-
if not response or response.status_code != 200:
|
1377 |
-
error_code = response.status_code if response else "No response"
|
1378 |
-
status_text.error(f"β Error generating part {i+1}: HTTP {error_code}")
|
1379 |
-
return None, None
|
1380 |
-
|
1381 |
-
# Parse the response
|
1382 |
-
try:
|
1383 |
-
response_data = json.loads(response.content)
|
1384 |
-
|
1385 |
-
# Check for error in response
|
1386 |
-
if isinstance(response_data, dict) and 'error' in response_data:
|
1387 |
-
status_text.error(f"β API error in part {i+1}: {response_data['error']}")
|
1388 |
-
return None, None
|
1389 |
-
|
1390 |
-
# Extract audio data from first item in the list
|
1391 |
-
if isinstance(response_data, list) and len(response_data) > 0:
|
1392 |
-
chunk_data = response_data[0]
|
1393 |
-
|
1394 |
-
# Extract audio data
|
1395 |
-
if "generated_audio" in chunk_data:
|
1396 |
-
chunk_audio = np.array(chunk_data["generated_audio"])
|
1397 |
-
|
1398 |
-
# Log shape for debugging
|
1399 |
-
print(f"Chunk {i+1} audio shape: {chunk_audio.shape}")
|
1400 |
-
|
1401 |
-
# Check for inconsistent shapes
|
1402 |
-
if len(audio_chunks) > 0:
|
1403 |
-
existing_shape = audio_chunks[0].shape
|
1404 |
-
if len(existing_shape) != len(chunk_audio.shape):
|
1405 |
-
print(f"WARNING: Shape mismatch between chunks. Existing: {existing_shape}, New: {chunk_audio.shape}")
|
1406 |
-
|
1407 |
-
# Store sample rate from first chunk
|
1408 |
-
if sample_rate is None and "sample_rate" in chunk_data:
|
1409 |
-
sample_rate = chunk_data["sample_rate"]
|
1410 |
-
print(f"Using sample rate: {sample_rate}")
|
1411 |
-
|
1412 |
-
# Add to our collection
|
1413 |
-
audio_chunks.append(chunk_audio)
|
1414 |
-
else:
|
1415 |
-
raise ValueError(f"No 'generated_audio' found in chunk {i+1}")
|
1416 |
-
else:
|
1417 |
-
raise ValueError(f"Unexpected response format for chunk {i+1}")
|
1418 |
-
|
1419 |
-
# Update progress
|
1420 |
-
overall_progress.progress((i + 1) / num_chunks)
|
1421 |
-
|
1422 |
-
except Exception as e:
|
1423 |
-
import traceback
|
1424 |
-
print(f"Error processing chunk {i+1}:")
|
1425 |
-
print(traceback.format_exc())
|
1426 |
-
status_text.error(f"β Error processing response for part {i+1}: {str(e)}")
|
1427 |
-
return None, None
|
1428 |
-
|
1429 |
-
# Handle the case when we couldn't get any valid chunks
|
1430 |
-
if not audio_chunks:
|
1431 |
-
status_text.error("β No valid audio chunks were generated.")
|
1432 |
-
return None, None
|
1433 |
-
|
1434 |
-
# Check if we at least have a sample rate
|
1435 |
-
if sample_rate is None:
|
1436 |
-
sample_rate = 44100 # Default to standard sample rate if not provided
|
1437 |
-
print("WARNING: No sample rate provided, using default 44100 Hz")
|
1438 |
-
|
1439 |
-
# Combine audio chunks with crossfading
|
1440 |
-
status_text.markdown("π Stitching audio chunks together...")
|
1441 |
-
try:
|
1442 |
-
# First check if we only have one chunk (no stitching needed)
|
1443 |
-
if len(audio_chunks) == 1:
|
1444 |
-
final_audio = audio_chunks[0]
|
1445 |
-
else:
|
1446 |
-
final_audio = combine_audio_with_crossfade(audio_chunks, sample_rate)
|
1447 |
-
|
1448 |
-
# Final check for valid audio
|
1449 |
-
if final_audio is None or final_audio.size == 0:
|
1450 |
-
status_text.error("β Failed to create valid audio output.")
|
1451 |
-
return None, None
|
1452 |
-
|
1453 |
-
status_text.success("β
Full audio track generated successfully!")
|
1454 |
-
overall_progress.progress(1.0)
|
1455 |
-
|
1456 |
-
return final_audio, sample_rate
|
1457 |
-
|
1458 |
-
except Exception as e:
|
1459 |
-
import traceback
|
1460 |
-
print(f"Error combining audio chunks:")
|
1461 |
-
print(traceback.format_exc())
|
1462 |
-
status_text.error(f"β Error combining audio chunks: {str(e)}")
|
1463 |
-
return None, None
|
1464 |
-
|
1465 |
-
except Exception as e:
|
1466 |
-
import traceback
|
1467 |
-
print(f"Unexpected error in generation process:")
|
1468 |
-
print(traceback.format_exc())
|
1469 |
-
status_text.error(f"β Unexpected error in generation process: {str(e)}")
|
1470 |
-
return None, None
|
1471 |
|
1472 |
-
|
1473 |
-
def combine_audio_with_crossfade(audio_chunks, sample_rate, crossfade_duration=2.0):
|
1474 |
-
"""
|
1475 |
-
Combine multiple audio chunks with crossfading between them with improved
|
1476 |
-
robustness for handling different array shapes and potential edge cases.
|
1477 |
-
|
1478 |
-
Parameters:
|
1479 |
-
audio_chunks: List of numpy arrays containing audio data
|
1480 |
-
sample_rate: Sample rate of the audio
|
1481 |
-
crossfade_duration: Crossfade duration in seconds
|
1482 |
-
|
1483 |
-
Returns:
|
1484 |
-
numpy.ndarray: Combined audio with crossfading
|
1485 |
-
"""
|
1486 |
-
if not audio_chunks:
|
1487 |
-
return None
|
1488 |
-
|
1489 |
-
if len(audio_chunks) == 1:
|
1490 |
-
return audio_chunks[0]
|
1491 |
-
|
1492 |
-
# Print debugging info about chunks
|
1493 |
-
print(f"Combining {len(audio_chunks)} audio chunks")
|
1494 |
-
for i, chunk in enumerate(audio_chunks):
|
1495 |
-
print(f"Chunk {i} shape: {chunk.shape}")
|
1496 |
-
|
1497 |
-
# Calculate crossfade samples
|
1498 |
-
crossfade_samples = int(crossfade_duration * sample_rate)
|
1499 |
-
print(f"Crossfade samples: {crossfade_samples}")
|
1500 |
-
|
1501 |
-
# Function to ensure consistent array dimensions
|
1502 |
-
def normalize_audio_shape(audio):
|
1503 |
-
"""Make sure audio is 2D with shape (channels, samples)"""
|
1504 |
-
if audio is None or audio.size == 0:
|
1505 |
-
return np.zeros((1, 0)) # Return empty mono audio
|
1506 |
-
|
1507 |
-
# Convert to 2D if needed
|
1508 |
-
if audio.ndim == 1:
|
1509 |
-
return audio.reshape(1, -1) # Convert mono to shape (1, samples)
|
1510 |
-
elif audio.ndim == 2:
|
1511 |
-
# If shape is (samples, channels), transpose to (channels, samples)
|
1512 |
-
if audio.shape[0] > 2 and (audio.shape[1] == 1 or audio.shape[1] == 2):
|
1513 |
-
return audio.T
|
1514 |
-
return audio
|
1515 |
-
else:
|
1516 |
-
# Unexpected shape - flatten to mono
|
1517 |
-
print(f"WARNING: Unexpected audio shape {audio.shape}, flattening to mono")
|
1518 |
-
flattened = audio.flatten()
|
1519 |
-
return flattened.reshape(1, -1)
|
1520 |
-
|
1521 |
-
# Normalize all chunks to ensure consistent shapes
|
1522 |
-
normalized_chunks = [normalize_audio_shape(chunk) for chunk in audio_chunks]
|
1523 |
-
|
1524 |
-
# Initialize with first chunk
|
1525 |
-
combined_audio = normalized_chunks[0]
|
1526 |
-
|
1527 |
-
# Add each subsequent chunk with crossfading
|
1528 |
-
for i, next_chunk in enumerate(normalized_chunks[1:], 1):
|
1529 |
-
print(f"Combining chunk {i} with combined audio")
|
1530 |
-
print(f"Combined audio shape: {combined_audio.shape}, next chunk shape: {next_chunk.shape}")
|
1531 |
-
|
1532 |
-
# Ensure both audio chunks have the same number of channels
|
1533 |
-
if combined_audio.shape[0] != next_chunk.shape[0]:
|
1534 |
-
# Handle different channel counts by converting to mono if needed
|
1535 |
-
channels_to_use = min(combined_audio.shape[0], next_chunk.shape[0])
|
1536 |
-
if channels_to_use == 1:
|
1537 |
-
# Convert both to mono
|
1538 |
-
if combined_audio.shape[0] > 1:
|
1539 |
-
combined_audio = np.mean(combined_audio, axis=0, keepdims=True)
|
1540 |
-
if next_chunk.shape[0] > 1:
|
1541 |
-
next_chunk = np.mean(next_chunk, axis=0, keepdims=True)
|
1542 |
-
else:
|
1543 |
-
# Use only the first channel from each
|
1544 |
-
combined_audio = combined_audio[:channels_to_use]
|
1545 |
-
next_chunk = next_chunk[:channels_to_use]
|
1546 |
-
|
1547 |
-
# Check if we have enough samples for crossfading
|
1548 |
-
if (combined_audio.shape[1] >= crossfade_samples and
|
1549 |
-
next_chunk.shape[1] >= crossfade_samples):
|
1550 |
-
|
1551 |
-
# Create fade curves
|
1552 |
-
fade_out = np.linspace(1, 0, crossfade_samples)
|
1553 |
-
fade_in = np.linspace(0, 1, crossfade_samples)
|
1554 |
-
|
1555 |
-
# Create temporary copies to avoid modifying originals
|
1556 |
-
combined_end = combined_audio[:, -crossfade_samples:].copy()
|
1557 |
-
next_start = next_chunk[:, :crossfade_samples].copy()
|
1558 |
-
|
1559 |
-
# Apply crossfade
|
1560 |
-
for channel in range(combined_audio.shape[0]):
|
1561 |
-
combined_end[channel] *= fade_out
|
1562 |
-
next_start[channel] *= fade_in
|
1563 |
-
|
1564 |
-
# Overlap and add
|
1565 |
-
overlap_region = combined_end + next_start
|
1566 |
-
|
1567 |
-
# Concatenate
|
1568 |
-
combined_audio = np.concatenate([
|
1569 |
-
combined_audio[:, :-crossfade_samples],
|
1570 |
-
overlap_region,
|
1571 |
-
next_chunk[:, crossfade_samples:]
|
1572 |
-
], axis=1)
|
1573 |
-
else:
|
1574 |
-
# Not enough samples for crossfade, just concatenate
|
1575 |
-
print("WARNING: Not enough samples for crossfade, simple concatenation used")
|
1576 |
-
combined_audio = np.concatenate([combined_audio, next_chunk], axis=1)
|
1577 |
-
|
1578 |
-
print(f"Final combined audio shape: {combined_audio.shape}")
|
1579 |
-
return combined_audio
|
1580 |
-
|
1581 |
-
|
1582 |
-
# Modify your existing generate_audio function to use chunking for long durations
|
1583 |
def generate_audio(genre, energy_level, tempo, description, duration):
|
1584 |
# Create placeholders
|
1585 |
status_placeholder = st.empty()
|
@@ -1620,7 +1368,12 @@ def generate_audio(genre, energy_level, tempo, description, duration):
|
|
1620 |
if is_free_tier:
|
1621 |
if duration > 30:
|
1622 |
st.warning("β οΈ Free tier users are limited to 30-second generations.")
|
1623 |
-
st.info("π‘ Upgrade to a premium plan to generate longer tracks
|
|
|
|
|
|
|
|
|
|
|
1624 |
duration = 30 # Force duration to 30 seconds for free tier
|
1625 |
|
1626 |
# Check if server is cold
|
@@ -1629,7 +1382,8 @@ def generate_audio(genre, energy_level, tempo, description, duration):
|
|
1629 |
"π **Server is currently starting up.**\n\n"
|
1630 |
"Our AI model needs some time to load and prepare when the server has been inactive.\n"
|
1631 |
"This can take up to **7 minutes** for free users.\n\n"
|
1632 |
-
"π‘ **Upgrade to a premium plan to reduce or eliminate wait times
|
|
|
1633 |
)
|
1634 |
|
1635 |
# Wait for server to warm up with progress display
|
@@ -1643,154 +1397,78 @@ def generate_audio(genre, energy_level, tempo, description, duration):
|
|
1643 |
if not server_ready:
|
1644 |
st.error(
|
1645 |
"β Server initialization timed out.\n\n"
|
1646 |
-
"Consider upgrading to premium for reliable, fast access
|
|
|
1647 |
)
|
1648 |
return None
|
1649 |
|
1650 |
# Clear warmup messages
|
1651 |
for placeholder in [status_placeholder, progress_placeholder, subscription_placeholder, stage_placeholder]:
|
1652 |
placeholder.empty()
|
1653 |
-
|
1654 |
-
# IMPORTANT: Choose between normal and chunked generation based on duration
|
1655 |
-
if duration <= 120: # For shorter durations that work reliably
|
1656 |
-
# Prepare standard generation request
|
1657 |
-
prompt = f"Genre: {genre}, Energy Level: {energy_level}, Tempo: {tempo}, Description: {description}"
|
1658 |
-
payload = {"inputs": {"prompt": prompt, "duration": duration}}
|
1659 |
-
api_headers = get_api_headers()
|
1660 |
-
|
1661 |
-
# Make the generation request
|
1662 |
-
with st.spinner("π΅ Generating your music... This may take a few moments."):
|
1663 |
-
response = time_post_request(API_URL, headers=api_headers, payload=payload, timeout=1200)
|
1664 |
|
1665 |
-
|
1666 |
-
|
1667 |
-
|
1668 |
-
|
1669 |
-
if 'error' in response_data:
|
1670 |
-
error_msg = response_data['error']
|
1671 |
-
if 'CUDA error' in error_msg:
|
1672 |
-
st.error("β The AI server is currently experiencing GPU resource issues.")
|
1673 |
-
st.info("Please try again in a few minutes. This is a temporary server issue.")
|
1674 |
-
return None
|
1675 |
-
else:
|
1676 |
-
st.error(f"β Server error: {error_msg}")
|
1677 |
-
return None
|
1678 |
-
|
1679 |
-
# If we get here, response is valid
|
1680 |
-
st.success("β¨ Music generated successfully!")
|
1681 |
-
except Exception as parse_error:
|
1682 |
-
print(f"Error parsing response to check for errors: {parse_error}")
|
1683 |
-
st.success("β¨ Music generated successfully!")
|
1684 |
-
|
1685 |
-
# Load and play the generated audio
|
1686 |
-
result = load_and_play_generated_audio(
|
1687 |
-
response=response,
|
1688 |
-
genre=genre,
|
1689 |
-
energy_level=energy_level,
|
1690 |
-
tempo=tempo,
|
1691 |
-
description=description,
|
1692 |
-
duration=duration
|
1693 |
-
)
|
1694 |
-
|
1695 |
-
# Only update count if audio was successfully generated
|
1696 |
-
if result[0] is not None:
|
1697 |
-
update_generation_url = "https://songlabai.com/wp-json/custom-api/v1/update-generation-count"
|
1698 |
-
requests.post(update_generation_url, headers=auth_headers)
|
1699 |
-
|
1700 |
-
return response # Return the response for future use
|
1701 |
-
else:
|
1702 |
-
error_code = response.status_code if response else "No response"
|
1703 |
-
st.error(f"β Failed to generate audio (Error {error_code}).")
|
1704 |
-
return None
|
1705 |
|
1706 |
-
|
1707 |
-
|
1708 |
-
|
1709 |
-
|
1710 |
-
# Generate audio in chunks and stitch together
|
1711 |
-
audio_array, sample_rate = generate_long_audio_chunked(
|
1712 |
-
genre, energy_level, tempo, description, duration
|
1713 |
-
)
|
1714 |
|
1715 |
-
if
|
1716 |
-
#
|
1717 |
-
|
1718 |
-
|
1719 |
-
|
1720 |
-
|
1721 |
-
|
1722 |
-
|
1723 |
-
|
1724 |
-
|
1725 |
-
|
1726 |
-
|
1727 |
-
|
1728 |
-
|
1729 |
-
|
1730 |
-
# Ensure it's the right data type
|
1731 |
-
if audio_array_to_save.dtype != np.float32:
|
1732 |
-
audio_array_to_save = audio_array_to_save.astype(np.float32)
|
1733 |
-
|
1734 |
-
print(f"Writing audio with shape {audio_array_to_save.shape} to WAV file")
|
1735 |
-
sf.write(audio_buffer, audio_array_to_save, sample_rate, format='WAV', subtype='PCM_16')
|
1736 |
-
audio_buffer.seek(0)
|
1737 |
-
|
1738 |
-
# Convert to AudioSegment for compatibility with your existing code
|
1739 |
-
try:
|
1740 |
-
audio_segment = AudioSegment.from_file(audio_buffer, format="wav")
|
1741 |
-
except Exception as e:
|
1742 |
-
print(f"Error converting to AudioSegment: {e}")
|
1743 |
-
# Alternative approach if the first method fails
|
1744 |
-
audio_buffer.seek(0)
|
1745 |
-
with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as temp_file:
|
1746 |
-
temp_filename = temp_file.name
|
1747 |
-
temp_file.write(audio_buffer.read())
|
1748 |
-
|
1749 |
-
# Now load from the temporary file
|
1750 |
-
audio_segment = AudioSegment.from_file(temp_filename, format="wav")
|
1751 |
-
# Clean up temp file
|
1752 |
-
os.unlink(temp_filename)
|
1753 |
-
|
1754 |
-
# Store in session state (matching your existing patterns)
|
1755 |
-
st_state.audio_pydub = audio_segment
|
1756 |
-
st_state.audio_sample_rate = sample_rate
|
1757 |
-
st_state.augmented_audio_pydub = audio_segment
|
1758 |
-
st_state.augmented_audio_sample_rate = sample_rate
|
1759 |
-
|
1760 |
-
# Set bytes for player
|
1761 |
-
audio_buffer.seek(0)
|
1762 |
-
st.session_state.audio_bytes = audio_buffer.getvalue()
|
1763 |
-
st.session_state.playing_state = 'generated'
|
1764 |
|
1765 |
-
#
|
1766 |
-
st.
|
1767 |
-
|
1768 |
-
|
1769 |
-
|
1770 |
-
|
1771 |
-
'duration': duration,
|
1772 |
-
'generated_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
1773 |
-
}
|
1774 |
|
1775 |
-
#
|
1776 |
-
|
1777 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
1778 |
|
1779 |
-
#
|
1780 |
-
|
1781 |
-
|
|
|
|
|
1782 |
|
1783 |
-
return
|
1784 |
else:
|
1785 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
1786 |
return None
|
1787 |
-
|
1788 |
except Exception as e:
|
1789 |
import traceback
|
1790 |
print(f"Detailed error in generate_audio: {traceback.format_exc()}")
|
1791 |
st.error(f"An unexpected error occurred: {e}")
|
1792 |
return None
|
1793 |
-
|
1794 |
def update_generate_button(genre, energy_level, tempo, description, duration):
|
1795 |
"""Create or update the generate button based on current state"""
|
1796 |
generate_btn_col, login_prompt_col = st.columns([1, 2])
|
@@ -1934,7 +1612,7 @@ with duration_col:
|
|
1934 |
duration = st.slider(
|
1935 |
"Duration (in seconds):",
|
1936 |
min_value=15,
|
1937 |
-
max_value=
|
1938 |
value=30,
|
1939 |
step=1
|
1940 |
)
|
|
|
27 |
import smtplib
|
28 |
from email.mime.text import MIMEText
|
29 |
from email.mime.multipart import MIMEMultipart
|
|
|
30 |
|
31 |
# Add enhanced CSS to block download buttons in audio players
|
32 |
st.markdown("""
|
|
|
396 |
# Format for premium users - CORRECTED LIMITS PER TIER
|
397 |
daily_limit = 0
|
398 |
download_limit = 0
|
399 |
+
duration_limit = 140
|
400 |
|
401 |
plan_id = subscription_status.get('subscription_plan_id')
|
402 |
if isinstance(plan_id, int):
|
|
|
1327 |
st.error(f"Failed to process generated audio: {e}")
|
1328 |
return None, None
|
1329 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1330 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1331 |
def generate_audio(genre, energy_level, tempo, description, duration):
|
1332 |
# Create placeholders
|
1333 |
status_placeholder = st.empty()
|
|
|
1368 |
if is_free_tier:
|
1369 |
if duration > 30:
|
1370 |
st.warning("β οΈ Free tier users are limited to 30-second generations.")
|
1371 |
+
st.info("π‘ Upgrade to a premium plan to generate longer tracks!\n\n" +
|
1372 |
+
"**Available Plans:**\n" +
|
1373 |
+
"- **$24.99/month:** Up to 3 generations per day, 1 download per month\n" +
|
1374 |
+
"- **$99.99/month:** Up to 5 generations per day, 5 downloads per month\n" +
|
1375 |
+
"- **$269.97/month:** Up to 20 generations per day, 15 downloads per month\n\n" +
|
1376 |
+
"π [Upgrade Now](https://songlabai.com/subscribe/)")
|
1377 |
duration = 30 # Force duration to 30 seconds for free tier
|
1378 |
|
1379 |
# Check if server is cold
|
|
|
1382 |
"π **Server is currently starting up.**\n\n"
|
1383 |
"Our AI model needs some time to load and prepare when the server has been inactive.\n"
|
1384 |
"This can take up to **7 minutes** for free users.\n\n"
|
1385 |
+
"π‘ **Upgrade to a premium plan to reduce or eliminate wait times!**\n"
|
1386 |
+
"π [Upgrade Now](https://songlabai.com/subscribe/)"
|
1387 |
)
|
1388 |
|
1389 |
# Wait for server to warm up with progress display
|
|
|
1397 |
if not server_ready:
|
1398 |
st.error(
|
1399 |
"β Server initialization timed out.\n\n"
|
1400 |
+
"Consider upgrading to premium for reliable, fast access.\n\n"
|
1401 |
+
"π [Upgrade Now](https://songlabai.com/subscribe/)"
|
1402 |
)
|
1403 |
return None
|
1404 |
|
1405 |
# Clear warmup messages
|
1406 |
for placeholder in [status_placeholder, progress_placeholder, subscription_placeholder, stage_placeholder]:
|
1407 |
placeholder.empty()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1408 |
|
1409 |
+
# Prepare generation request
|
1410 |
+
prompt = f"Genre: {genre}, Energy Level: {energy_level}, Tempo: {tempo}, Description: {description}"
|
1411 |
+
payload = {"inputs": {"prompt": prompt, "duration": duration}}
|
1412 |
+
api_headers = get_api_headers()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1413 |
|
1414 |
+
# Make the generation request
|
1415 |
+
with st.spinner("π΅ Generating your music... This may take a few moments."):
|
1416 |
+
response = time_post_request(API_URL, headers=api_headers, payload=payload, timeout=1200)
|
|
|
|
|
|
|
|
|
|
|
1417 |
|
1418 |
+
if response and response.status_code == 200:
|
1419 |
+
# Check for error in response before showing success message
|
1420 |
+
try:
|
1421 |
+
response_data = json.loads(response.content)
|
1422 |
+
if 'error' in response_data:
|
1423 |
+
error_msg = response_data['error']
|
1424 |
+
if 'CUDA error' in error_msg:
|
1425 |
+
st.error("β The AI server is currently experiencing GPU resource issues.")
|
1426 |
+
st.info("Please try again in a few minutes. This is a temporary server issue.")
|
1427 |
+
print(f"CUDA error from server: {error_msg}")
|
1428 |
+
return None
|
1429 |
+
else:
|
1430 |
+
st.error(f"β Server error: {error_msg}")
|
1431 |
+
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1432 |
|
1433 |
+
# If we get here, response is valid
|
1434 |
+
st.success("β¨ Music generated successfully!")
|
1435 |
+
except Exception as parse_error:
|
1436 |
+
print(f"Error parsing response to check for errors: {parse_error}")
|
1437 |
+
# Continue with normal flow - will be caught in load_and_play if there's an issue
|
1438 |
+
st.success("β¨ Music generated successfully!")
|
|
|
|
|
|
|
1439 |
|
1440 |
+
# Load and play the generated audio
|
1441 |
+
result = load_and_play_generated_audio(
|
1442 |
+
response=response,
|
1443 |
+
genre=genre,
|
1444 |
+
energy_level=energy_level,
|
1445 |
+
tempo=tempo,
|
1446 |
+
description=description,
|
1447 |
+
duration=duration
|
1448 |
+
)
|
1449 |
|
1450 |
+
# Only update count if audio was successfully generated
|
1451 |
+
if result[0] is not None:
|
1452 |
+
# Update generation count on WordPress
|
1453 |
+
update_generation_url = "https://songlabai.com/wp-json/custom-api/v1/update-generation-count"
|
1454 |
+
requests.post(update_generation_url, headers=auth_headers)
|
1455 |
|
1456 |
+
return response # Return the response for future use
|
1457 |
else:
|
1458 |
+
error_code = response.status_code if response else "No response"
|
1459 |
+
st.error(
|
1460 |
+
f"β Failed to generate audio (Error {error_code}).\n\n"
|
1461 |
+
"This might be due to high server load or an error.\n\n"
|
1462 |
+
"π‘ **Tip:** Premium users experience fewer failures and faster generation times.\n\n"
|
1463 |
+
"π [Upgrade Now](https://songlabai.com/subscribe/)"
|
1464 |
+
)
|
1465 |
return None
|
|
|
1466 |
except Exception as e:
|
1467 |
import traceback
|
1468 |
print(f"Detailed error in generate_audio: {traceback.format_exc()}")
|
1469 |
st.error(f"An unexpected error occurred: {e}")
|
1470 |
return None
|
1471 |
+
|
1472 |
def update_generate_button(genre, energy_level, tempo, description, duration):
|
1473 |
"""Create or update the generate button based on current state"""
|
1474 |
generate_btn_col, login_prompt_col = st.columns([1, 2])
|
|
|
1612 |
duration = st.slider(
|
1613 |
"Duration (in seconds):",
|
1614 |
min_value=15,
|
1615 |
+
max_value=140,
|
1616 |
value=30,
|
1617 |
step=1
|
1618 |
)
|