Compare commits
No commits in common. "master" and "main" have entirely different histories.
10 changed files with 50 additions and 1638 deletions
50
.gitignore
vendored
Normal file
50
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# These are some examples of commonly ignored file patterns.
|
||||
# You should customize this list as applicable to your project.
|
||||
# Learn more about .gitignore:
|
||||
# https://www.atlassian.com/git/tutorials/saving-changes/gitignore
|
||||
|
||||
# Node artifact files
|
||||
node_modules/
|
||||
dist/
|
||||
|
||||
# Compiled Java class files
|
||||
*.class
|
||||
|
||||
# Compiled Python bytecode
|
||||
*.py[cod]
|
||||
|
||||
# Log files
|
||||
*.log
|
||||
|
||||
# Package files
|
||||
*.jar
|
||||
|
||||
# Maven
|
||||
target/
|
||||
dist/
|
||||
|
||||
# JetBrains IDE
|
||||
.idea/
|
||||
|
||||
# Unit test reports
|
||||
TEST*.xml
|
||||
|
||||
# Generated by MacOS
|
||||
.DS_Store
|
||||
|
||||
# Generated by Windows
|
||||
Thumbs.db
|
||||
|
||||
# Applications
|
||||
*.app
|
||||
*.exe
|
||||
*.war
|
||||
|
||||
# Large media files
|
||||
*.mp4
|
||||
*.tiff
|
||||
*.avi
|
||||
*.flv
|
||||
*.mov
|
||||
*.wmv
|
||||
|
||||
333
README.md
333
README.md
|
|
@ -1,333 +0,0 @@
|
|||
# 🎵 ElevenLabs Music Generator
|
||||
|
||||
A web-based application for generating AI music using ElevenLabs' Music Generation API. This application provides both simple prompt-based music generation and advanced composition planning features.
|
||||
|
||||
## 🌟 Features
|
||||
|
||||
### 🎼 Two Generation Modes
|
||||
- **Simple Mode**: Generate music directly from a text prompt
|
||||
- **Advanced Mode**: Create detailed composition plans first, then generate music from those plans
|
||||
|
||||
### 🎛️ Advanced Controls
|
||||
- **Duration Control**: Set music length from 10 seconds to 5 minutes (300 seconds)
|
||||
- **Composition Planning**: Generate structured plans with styles, sections, and arrangements
|
||||
- **Real-time Preview**: Built-in audio player with download capability
|
||||
- **Dark Mode**: Toggle between light and dark themes
|
||||
|
||||
### 🔒 Enterprise Features
|
||||
- **SSO Authentication**: Microsoft Azure AD integration (disabled for local development)
|
||||
- **Webhook Integration**: Automatic provenance tracking for generated content
|
||||
- **Auto-cleanup**: Generated files automatically deleted after 24 hours
|
||||
- **Error Handling**: Comprehensive error reporting and logging
|
||||
|
||||
## 📋 Requirements
|
||||
|
||||
### System Requirements
|
||||
- **PHP 7.4+** with cURL extension
|
||||
- **Web Server** (Apache, Nginx, or PHP built-in server)
|
||||
- **ElevenLabs API Key** with Music Generation access
|
||||
|
||||
### ElevenLabs Subscription Requirements
|
||||
- **Music Generation Access**: Requires special access and additional terms acceptance
|
||||
- **Recommended**: Creator or Pro tier subscription
|
||||
- **Contact**: Your ElevenLabs account team for music generation enablement
|
||||
|
||||
## 🚀 Installation
|
||||
|
||||
### 1. Clone the Repository
|
||||
```bash
|
||||
git clone https://bitbucket.org/zlalani/music-generation.git
|
||||
cd music-generation
|
||||
```
|
||||
|
||||
### 2. Configure API Key
|
||||
Edit `config.php` and replace the API key:
|
||||
```php
|
||||
return [
|
||||
'elevenlabs_api_key' => 'your-actual-api-key-here',
|
||||
'max_file_age_hours' => 24,
|
||||
'generated_files_dir' => 'generated/'
|
||||
];
|
||||
```
|
||||
|
||||
### 3. Set Permissions
|
||||
```bash
|
||||
# Create generated files directory
|
||||
mkdir -p generated/
|
||||
chmod 755 generated/
|
||||
|
||||
# Ensure PHP can write to the directory
|
||||
chown www-data:www-data generated/ # On Ubuntu/Debian
|
||||
# OR
|
||||
chown apache:apache generated/ # On CentOS/RHEL
|
||||
```
|
||||
|
||||
### 4. Local Development
|
||||
For quick local testing:
|
||||
```bash
|
||||
# Using PHP built-in server
|
||||
php -S localhost:8000
|
||||
|
||||
# Then visit: http://localhost:8000
|
||||
```
|
||||
|
||||
## 🎯 Usage Guide
|
||||
|
||||
### Simple Mode
|
||||
1. Select **"Simple Mode"** from the dropdown
|
||||
2. Enter your music description (e.g., "Upbeat electronic dance track with driving bass")
|
||||
3. Optionally set duration using the slider (0 = auto-duration)
|
||||
4. Click **"Generate Music"**
|
||||
5. Wait for generation (may take 30-60 seconds)
|
||||
6. Play and download your generated music
|
||||
|
||||
### Advanced Mode
|
||||
1. Select **"Advanced Mode (with Composition Plan)"**
|
||||
2. Enter your music description
|
||||
3. Click **"Generate Composition Plan"** first
|
||||
4. Review the generated plan (includes styles, sections, structure)
|
||||
5. Click **"Generate Music"** to create audio from the plan
|
||||
6. Play and download your generated music
|
||||
|
||||
### Example Prompts
|
||||
- **Electronic**: "Energetic EDM track with heavy bass drops and soaring synths"
|
||||
- **Classical**: "Peaceful piano melody with subtle string accompaniment"
|
||||
- **Ambient**: "Atmospheric soundscape with ethereal pads and gentle rain sounds"
|
||||
- **Rock**: "Driving rock anthem with powerful drums and electric guitar"
|
||||
|
||||
## 🔧 API Endpoints Used
|
||||
|
||||
### Music Generation
|
||||
- **Endpoint**: `POST https://api.elevenlabs.io/v1/music`
|
||||
- **Purpose**: Generate music from prompts or composition plans
|
||||
- **Parameters**:
|
||||
- `prompt`: Text description (max 2000 characters)
|
||||
- `music_length_ms`: Duration in milliseconds (10000-300000)
|
||||
- `composition_plan`: Advanced structured plan (optional)
|
||||
|
||||
### Composition Planning
|
||||
- **Endpoint**: `POST https://api.elevenlabs.io/v1/music/plan`
|
||||
- **Purpose**: Create detailed composition plans
|
||||
- **Parameters**:
|
||||
- `prompt`: Text description (max 2000 characters)
|
||||
- `music_length_ms`: Duration in milliseconds (optional)
|
||||
- **Note**: This endpoint doesn't consume credits but is rate-limited
|
||||
|
||||
## 🏗️ Architecture
|
||||
|
||||
### File Structure
|
||||
```
|
||||
music-generation/
|
||||
├── index.php # Main application interface
|
||||
├── config.php # Configuration settings
|
||||
├── generate_plan.php # Composition plan generation endpoint
|
||||
├── webhook_processor_audio.php # Webhook integration for provenance
|
||||
├── store_user_session.php # SSO user session storage
|
||||
├── clear_user_session.php # SSO user session cleanup
|
||||
├── generated/ # Auto-generated music files (auto-cleanup)
|
||||
└── README.md # This documentation
|
||||
```
|
||||
|
||||
### Key Components
|
||||
|
||||
#### Authentication System
|
||||
- **Microsoft SSO**: Azure AD integration for enterprise use
|
||||
- **Local Override**: Disabled for development/testing
|
||||
- **Session Management**: Token-based authentication
|
||||
|
||||
#### Webhook Integration
|
||||
- **Provenance Tracking**: Automatic logging of all generation events
|
||||
- **User Tracking**: Captures SSO user information (email, name, ID)
|
||||
- **Enhanced Metadata**: Generation mode, IP address, user agent
|
||||
- **Data Structure**: Standardized webhook payload format
|
||||
- **Error Handling**: Graceful fallback if webhook fails
|
||||
|
||||
#### File Management
|
||||
- **Auto-naming**: Timestamped filename generation
|
||||
- **Auto-cleanup**: Configurable file retention (default: 24 hours)
|
||||
- **Storage**: Local filesystem with organized structure
|
||||
|
||||
## 🛠️ Configuration Options
|
||||
|
||||
### config.php Settings
|
||||
```php
|
||||
return [
|
||||
// ElevenLabs API configuration
|
||||
'elevenlabs_api_key' => 'your-api-key',
|
||||
|
||||
// File management
|
||||
'max_file_age_hours' => 24, # Auto-delete after 24 hours
|
||||
'generated_files_dir' => 'generated/' # Storage directory
|
||||
];
|
||||
```
|
||||
|
||||
### Webhook Configuration
|
||||
The `AudioWebhookProcessor` class supports:
|
||||
- Custom webhook URLs
|
||||
- Configurable timeouts
|
||||
- SSL verification settings
|
||||
- Client/user identification
|
||||
- Deliverable tracking
|
||||
|
||||
## 🚨 Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### "Music generation requires additional access"
|
||||
- **Cause**: ElevenLabs Music API requires special access
|
||||
- **Solution**: Contact your ElevenLabs account team
|
||||
- **Links**: [Music Terms](https://elevenlabs.io/music-terms)
|
||||
|
||||
#### "Please configure your ElevenLabs API key"
|
||||
- **Cause**: Invalid or missing API key in config.php
|
||||
- **Solution**: Update config.php with valid API key
|
||||
|
||||
#### "API Error (403): limited_access"
|
||||
- **Cause**: API key doesn't have music generation permissions
|
||||
- **Solution**: Upgrade subscription and accept music terms
|
||||
|
||||
#### Files not generating
|
||||
- **Check**: PHP error logs for detailed error messages
|
||||
- **Verify**: Generated directory permissions (755)
|
||||
- **Ensure**: Sufficient disk space available
|
||||
|
||||
### Debug Mode
|
||||
Enable PHP error reporting for development:
|
||||
```php
|
||||
// Add to top of index.php for debugging
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
```
|
||||
|
||||
## 🔐 Security Considerations
|
||||
|
||||
### API Key Protection
|
||||
- Store API keys securely in config.php
|
||||
- Never commit API keys to version control
|
||||
- Use environment variables in production
|
||||
|
||||
### File Security
|
||||
- Generated files are automatically cleaned up
|
||||
- No sensitive data stored in generated files
|
||||
- Directory permissions properly configured
|
||||
|
||||
### SSO Integration
|
||||
- Microsoft Azure AD integration available
|
||||
- Token-based session management
|
||||
- Configurable for enterprise environments
|
||||
|
||||
## 🚀 Deployment
|
||||
|
||||
### Production Checklist
|
||||
- [ ] Configure proper web server (Apache/Nginx)
|
||||
- [ ] Set up HTTPS/SSL certificates
|
||||
- [ ] Configure proper file permissions
|
||||
- [ ] Enable SSO authentication
|
||||
- [ ] Set up monitoring and logging
|
||||
- [ ] Configure webhook endpoints
|
||||
- [ ] Test music generation access
|
||||
- [ ] Set up automated backups
|
||||
|
||||
### Environment Variables
|
||||
Consider using environment variables for sensitive config:
|
||||
```php
|
||||
// In config.php
|
||||
return [
|
||||
'elevenlabs_api_key' => $_ENV['ELEVENLABS_API_KEY'] ?? 'fallback-key',
|
||||
// ... other settings
|
||||
];
|
||||
```
|
||||
|
||||
## 📊 Monitoring & Analytics
|
||||
|
||||
### Webhook Tracking
|
||||
All generations are tracked via webhook with comprehensive metadata:
|
||||
|
||||
#### Standard Fields
|
||||
- **Generation timestamp**: When the music was created
|
||||
- **User identification**: SSO email, name, and user ID
|
||||
- **Prompt/settings used**: Full generation parameters
|
||||
- **File information**: Size, filename, duration
|
||||
- **Success/failure status**: Generation outcome
|
||||
|
||||
#### Enhanced Tracking (Music Specific)
|
||||
- **Content type**: "Music Generation"
|
||||
- **Generation mode**: "Simple Prompt" or "Advanced (with Composition Plan)"
|
||||
- **Client identification**: "Oliver Agency - Music Generation"
|
||||
- **Technical metadata**: User agent, IP address
|
||||
- **API endpoint**: "elevenlabs-music" for identification
|
||||
|
||||
#### Webhook Payload Example
|
||||
```json
|
||||
{
|
||||
"prompt": "Upbeat electronic dance track",
|
||||
"generation_type": "ElevenLabs Music Generation",
|
||||
"client": "Oliver Agency - Music Generation",
|
||||
"user_email": "user@oliver.agency",
|
||||
"user_name": "John Doe",
|
||||
"settings": {...},
|
||||
"additional_data": {
|
||||
"content_type": "Music Generation",
|
||||
"generation_mode": "Simple Prompt",
|
||||
"filename": "music_2023-08-19_14-30-45.mp3",
|
||||
"file_size": 2048576,
|
||||
"ip_address": "192.168.1.100",
|
||||
"user_agent": "Mozilla/5.0..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### File Management
|
||||
- Automatic cleanup prevents disk space issues
|
||||
- Configurable retention periods
|
||||
- File size and generation tracking
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
### Development Setup
|
||||
1. Fork the repository
|
||||
2. Create a feature branch
|
||||
3. Make your changes
|
||||
4. Test thoroughly
|
||||
5. Submit a pull request
|
||||
|
||||
### Code Standards
|
||||
- Follow PHP PSR standards
|
||||
- Comment complex functionality
|
||||
- Test with both generation modes
|
||||
- Verify webhook integration
|
||||
|
||||
## 📄 License
|
||||
|
||||
This project is proprietary software developed for Oliver Agency. All rights reserved.
|
||||
|
||||
## 🆘 Support
|
||||
|
||||
### For Technical Issues
|
||||
- Check the troubleshooting section above
|
||||
- Review PHP error logs
|
||||
- Test with the provided example prompts
|
||||
|
||||
### For ElevenLabs API Issues
|
||||
- Contact ElevenLabs support
|
||||
- Review your subscription tier
|
||||
- Check API key permissions
|
||||
|
||||
### For Feature Requests
|
||||
- Submit issues to the repository
|
||||
- Include detailed requirements
|
||||
- Provide use case examples
|
||||
|
||||
## 🔄 Version History
|
||||
|
||||
### v1.0.0 - Initial Release
|
||||
- Music generation with simple and advanced modes
|
||||
- Composition plan generation
|
||||
- Webhook integration for provenance tracking
|
||||
- Dark mode support
|
||||
- SSO authentication system
|
||||
- Automatic file cleanup
|
||||
|
||||
---
|
||||
|
||||
**Built with ❤️ using ElevenLabs Music Generation API**
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
<?php
|
||||
// Clear SSO user session data
|
||||
|
||||
session_start();
|
||||
|
||||
// Clear all SSO-related session data
|
||||
unset($_SESSION['sso_user_email']);
|
||||
unset($_SESSION['sso_user_name']);
|
||||
unset($_SESSION['sso_user_id']);
|
||||
unset($_SESSION['sso_login_timestamp']);
|
||||
|
||||
// Log session cleared
|
||||
error_log("SSO user session cleared");
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'User session cleared successfully'
|
||||
]);
|
||||
?>
|
||||
10
config.php
10
config.php
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
// Configuration file for ElevenLabs Music Generator
|
||||
// IMPORTANT: Replace the API key below with your new ElevenLabs Music API key
|
||||
|
||||
return [
|
||||
'elevenlabs_api_key' => 'sk_1295b1e2ed87f7cb811484c29cf295dcd80990020d658e02',
|
||||
'max_file_age_hours' => 24, // Auto-delete generated files older than this
|
||||
'generated_files_dir' => 'generated/'
|
||||
];
|
||||
?>
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
<?php
|
||||
// Composition Plan Generation Endpoint for ElevenLabs Music API
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Load configuration
|
||||
$config = require_once 'config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'error' => 'Method not allowed']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$prompt = $_POST['prompt'] ?? '';
|
||||
$musicLengthMs = (int)($_POST['music_length_ms'] ?? 0);
|
||||
$apiKey = $config['elevenlabs_api_key'];
|
||||
|
||||
if (empty($prompt)) {
|
||||
echo json_encode(['success' => false, 'error' => 'Prompt is required']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (empty($apiKey) || $apiKey === 'your-api-key-here') {
|
||||
echo json_encode(['success' => false, 'error' => 'ElevenLabs API key not configured']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Generate composition plan
|
||||
$planResult = generateCompositionPlan($prompt, $musicLengthMs, $apiKey);
|
||||
|
||||
if ($planResult['success']) {
|
||||
echo json_encode(['success' => true, 'plan' => $planResult['plan']]);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'error' => $planResult['error']]);
|
||||
}
|
||||
|
||||
function generateCompositionPlan($prompt, $musicLengthMs, $apiKey) {
|
||||
$url = 'https://api.elevenlabs.io/v1/music/plan';
|
||||
|
||||
$data = [
|
||||
'prompt' => $prompt,
|
||||
'model_id' => 'music_v1'
|
||||
];
|
||||
|
||||
// Add music length if specified (minimum 10 seconds)
|
||||
if ($musicLengthMs >= 10000) {
|
||||
$data['music_length_ms'] = $musicLengthMs;
|
||||
}
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'xi-api-key: ' . $apiKey,
|
||||
'Content-Type: application/json'
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
if (curl_error($ch)) {
|
||||
curl_close($ch);
|
||||
return ['success' => false, 'error' => 'Connection error: ' . curl_error($ch)];
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode === 200) {
|
||||
$planData = json_decode($response, true);
|
||||
if ($planData) {
|
||||
return ['success' => true, 'plan' => $planData];
|
||||
} else {
|
||||
return ['success' => false, 'error' => 'Invalid response format'];
|
||||
}
|
||||
} else {
|
||||
$errorResponse = json_decode($response, true);
|
||||
$errorMessage = $errorResponse['detail'] ?? 'Unknown error occurred';
|
||||
return ['success' => false, 'error' => 'API Error (' . $httpCode . '): ' . $errorMessage];
|
||||
}
|
||||
}
|
||||
?>
|
||||
Binary file not shown.
Binary file not shown.
805
index.php
805
index.php
|
|
@ -1,805 +0,0 @@
|
|||
<?php
|
||||
// Start session for SSO user tracking
|
||||
session_start();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ElevenLabs Music Generator</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<!-- Microsoft Authentication Library -->
|
||||
<script src="https://alcdn.msauth.net/browser/2.15.0/js/msal-browser.min.js" crossorigin="anonymous"></script>
|
||||
<style>
|
||||
:root {
|
||||
--primary-btn-color: #f3ae3e;
|
||||
--primary-btn-hover-color: #d4973b;
|
||||
--bg-color: #f5f5f5;
|
||||
--container-bg: white;
|
||||
--text-color: #333;
|
||||
--text-muted: #555;
|
||||
--border-color: #ddd;
|
||||
--input-bg: white;
|
||||
--success-bg: #d4edda;
|
||||
--success-border: #c3e6cb;
|
||||
--success-text: #155724;
|
||||
--error-bg: #f8d7da;
|
||||
--error-border: #f5c6cb;
|
||||
--error-text: #721c24;
|
||||
--help-text-color: #666;
|
||||
}
|
||||
body {
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
.container {
|
||||
background: var(--container-bg);
|
||||
padding: 30px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
h1 {
|
||||
color: var(--text-color);
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
input, textarea, select {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 5px;
|
||||
font-size: 16px;
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
font-weight: 300;
|
||||
background-color: var(--input-bg);
|
||||
color: var(--text-color);
|
||||
box-sizing: border-box;
|
||||
transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
|
||||
}
|
||||
textarea {
|
||||
height: 100px;
|
||||
resize: vertical;
|
||||
}
|
||||
button {
|
||||
background-color: var(--primary-btn-color);
|
||||
color: white;
|
||||
padding: 12px 30px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
button:hover {
|
||||
background-color: var(--primary-btn-hover-color);
|
||||
}
|
||||
button:disabled {
|
||||
background-color: #6c757d;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.help-text {
|
||||
font-size: 12px;
|
||||
color: var(--help-text-color);
|
||||
margin-top: 5px;
|
||||
}
|
||||
.result {
|
||||
margin-top: 30px;
|
||||
padding: 20px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.success {
|
||||
background-color: var(--success-bg);
|
||||
border: 1px solid var(--success-border);
|
||||
color: var(--success-text);
|
||||
}
|
||||
.error {
|
||||
background-color: var(--error-bg);
|
||||
border: 1px solid var(--error-border);
|
||||
color: var(--error-text);
|
||||
}
|
||||
.loading {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.audio-player {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
audio {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
}
|
||||
.slider-container {
|
||||
position: relative;
|
||||
margin: 10px 0;
|
||||
}
|
||||
.slider {
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
border-radius: 5px;
|
||||
background: var(--border-color);
|
||||
outline: none;
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.2s, background-color 0.3s ease;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
.slider:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
.slider::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background: var(--primary-btn-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
.slider::-moz-range-thumb {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background: var(--primary-btn-color);
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
}
|
||||
.slider-value {
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
font-weight: bold;
|
||||
color: var(--primary-btn-color);
|
||||
min-width: 60px;
|
||||
}
|
||||
.slider-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* Dark Mode Toggle Button */
|
||||
.dark-mode-toggle {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 1000;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
min-width: 48px;
|
||||
min-height: 48px;
|
||||
border-radius: 24px;
|
||||
background-color: var(--primary-btn-color);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
|
||||
color: #000;
|
||||
transition: background-color 0.3s ease;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.dark-mode-toggle:hover {
|
||||
background-color: var(--primary-btn-hover-color);
|
||||
}
|
||||
|
||||
/* Dark Mode Styles */
|
||||
.dark-mode {
|
||||
--bg-color: #1e1e1e;
|
||||
--container-bg: #2a2a2a;
|
||||
--text-color: #f5f5f5;
|
||||
--text-muted: #cccccc;
|
||||
--border-color: #444;
|
||||
--input-bg: #333;
|
||||
--success-bg: #1a4a1a;
|
||||
--success-border: #2d5a2d;
|
||||
--success-text: #90ee90;
|
||||
--error-bg: #4a1a1a;
|
||||
--error-border: #5a2d2d;
|
||||
--error-text: #ffb3b3;
|
||||
--help-text-color: #999;
|
||||
}
|
||||
|
||||
.dark-mode .dark-mode-toggle {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Authentication Styles */
|
||||
#login-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 80vh;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
background: var(--container-bg);
|
||||
border-radius: 10px;
|
||||
padding: 40px 20px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
#login-container h1 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 20px;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
#login-container p {
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 30px;
|
||||
color: var(--text-muted);
|
||||
max-width: 80%;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
#login-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 15px 40px;
|
||||
font-size: 1.1rem;
|
||||
background-color: var(--primary-btn-color);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 30px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||
font-weight: 600;
|
||||
transition: background-color 0.3s ease;
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
}
|
||||
|
||||
#login-button:hover {
|
||||
background-color: var(--primary-btn-hover-color);
|
||||
}
|
||||
|
||||
#logout-button {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 80px;
|
||||
padding: 8px 16px;
|
||||
font-size: 0.9rem;
|
||||
background-color: var(--container-bg);
|
||||
color: var(--text-muted);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
#logout-button:hover {
|
||||
background-color: var(--border-color);
|
||||
}
|
||||
|
||||
/* Hide protected content by default */
|
||||
#protected-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dark-mode #logout-button {
|
||||
background-color: var(--container-bg);
|
||||
color: var(--text-muted);
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Login Screen - Hidden for local testing -->
|
||||
<div id="login-container" style="display: none;">
|
||||
<h1>🎵 Music Generator</h1>
|
||||
<p>Please log in to access the music generator and create amazing compositions for your projects.</p>
|
||||
<button id="login-button" onclick="signIn()">
|
||||
Log In with Oliver SSO
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Logout Button - Hidden for local testing -->
|
||||
<button id="logout-button" onclick="signOut()" title="Log Out" style="display: none;">
|
||||
Log Out
|
||||
</button>
|
||||
|
||||
<!-- Dark Mode Toggle Button -->
|
||||
<button id="darkModeToggle" class="dark-mode-toggle" title="Toggle Dark Mode">
|
||||
<span id="lightModeIcon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M8 11a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0 1a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"/>
|
||||
</svg>
|
||||
</span>
|
||||
<span id="darkModeIcon" style="display: none;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<!-- Protected Content -->
|
||||
<div id="protected-content">
|
||||
<div class="container">
|
||||
<h1>🎵 Music Generator</h1>
|
||||
|
||||
<div class="result" style="background-color: #fff3cd; border: 1px solid #ffeaa7; color: #856404; margin-bottom: 20px;">
|
||||
<strong>Note:</strong> ElevenLabs Music Generation requires special access and additional terms acceptance.
|
||||
Contact your account team to enable music generation access.
|
||||
<a href="https://elevenlabs.io/music-terms" target="_blank">Learn more about music terms</a>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
// Load configuration
|
||||
$config = require_once 'config.php';
|
||||
|
||||
// Include webhook processor
|
||||
require_once 'webhook_processor_audio.php';
|
||||
|
||||
// Clean up old files on page load
|
||||
cleanOldFiles($config['generated_files_dir'], $config['max_file_age_hours']);
|
||||
|
||||
$result = '';
|
||||
$error = '';
|
||||
$audioFile = '';
|
||||
|
||||
function cleanOldFiles($directory, $maxAgeHours) {
|
||||
if (!is_dir($directory)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$files = glob($directory . '*.mp3');
|
||||
$cutoffTime = time() - ($maxAgeHours * 3600);
|
||||
|
||||
foreach ($files as $file) {
|
||||
if (is_file($file) && filemtime($file) < $cutoffTime) {
|
||||
unlink($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$text = $_POST['text'] ?? '';
|
||||
$mode = $_POST['mode'] ?? 'simple';
|
||||
$musicLengthMs = ($_POST['music_length_ms'] ?? 0) * 1000; // Convert seconds to milliseconds
|
||||
$compositionPlan = $_POST['composition_plan'] ?? null;
|
||||
$apiKey = $config['elevenlabs_api_key'];
|
||||
|
||||
if (empty($text)) {
|
||||
$error = 'Please enter a description for your music.';
|
||||
} elseif (empty($apiKey) || $apiKey === 'your-api-key-here') {
|
||||
$error = 'Please configure your ElevenLabs API key in config.php';
|
||||
} else {
|
||||
if ($mode === 'advanced' && !empty($compositionPlan)) {
|
||||
$audioFile = generateMusicWithPlan($compositionPlan, $apiKey, $error);
|
||||
} else {
|
||||
$audioFile = generateMusic($text, $musicLengthMs, $apiKey, $error);
|
||||
}
|
||||
if ($audioFile) {
|
||||
$result = 'Music generated successfully!';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function generateMusic($prompt, $musicLengthMs, $apiKey, &$error) {
|
||||
$url = 'https://api.elevenlabs.io/v1/music';
|
||||
|
||||
$data = [
|
||||
'prompt' => $prompt
|
||||
];
|
||||
|
||||
// Only add music_length_ms if it's specified and valid (minimum 10 seconds)
|
||||
if (!empty($musicLengthMs) && is_numeric($musicLengthMs) && $musicLengthMs >= 10000) {
|
||||
$data['music_length_ms'] = (int)$musicLengthMs;
|
||||
}
|
||||
|
||||
// Don't include model_id as it defaults to music_v1
|
||||
|
||||
return generateMusicRequest($url, $data, $apiKey, $error, 'ElevenLabs Music Generation', $prompt);
|
||||
}
|
||||
|
||||
function generateMusicWithPlan($compositionPlan, $apiKey, &$error) {
|
||||
$url = 'https://api.elevenlabs.io/v1/music';
|
||||
|
||||
$data = [
|
||||
'composition_plan' => json_decode($compositionPlan, true),
|
||||
'model_id' => 'music_v1'
|
||||
];
|
||||
|
||||
return generateMusicRequest($url, $data, $apiKey, $error, 'ElevenLabs Music Generation (Advanced)', 'Advanced composition');
|
||||
}
|
||||
|
||||
function generateMusicRequest($url, $data, $apiKey, &$error, $generationType, $prompt) {
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'xi-api-key: ' . $apiKey,
|
||||
'Content-Type: application/json'
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Increased timeout for music generation
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
// Log the request for debugging
|
||||
error_log("Music API Request - URL: $url, Data: " . json_encode($data) . ", HTTP Code: $httpCode, Response: " . substr($response, 0, 500));
|
||||
|
||||
if (curl_error($ch)) {
|
||||
$error = 'Connection error: ' . curl_error($ch);
|
||||
curl_close($ch);
|
||||
return false;
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode === 200) {
|
||||
$filename = 'music_' . date('Y-m-d_H-i-s') . '.mp3';
|
||||
$filepath = __DIR__ . '/generated/' . $filename;
|
||||
|
||||
if (!is_dir(__DIR__ . '/generated')) {
|
||||
mkdir(__DIR__ . '/generated', 0755, true);
|
||||
}
|
||||
|
||||
if (file_put_contents($filepath, $response)) {
|
||||
// Send webhook for provenance tracking
|
||||
try {
|
||||
$webhookProcessor = new AudioWebhookProcessor();
|
||||
|
||||
// Get user info from SSO session if available
|
||||
$userEmail = 'music-generation@oliver.agency'; // Default
|
||||
$userName = 'Anonymous User'; // Default
|
||||
|
||||
// Check for SSO session data (when SSO is enabled)
|
||||
if (isset($_SESSION['sso_user_email'])) {
|
||||
$userEmail = $_SESSION['sso_user_email'];
|
||||
}
|
||||
if (isset($_SESSION['sso_user_name'])) {
|
||||
$userName = $_SESSION['sso_user_name'];
|
||||
}
|
||||
|
||||
$webhookData = [
|
||||
'prompt' => $prompt,
|
||||
'generation_type' => $generationType,
|
||||
'settings' => $data,
|
||||
'audio_data' => $response,
|
||||
'client' => 'Oliver Agency - Music Generation',
|
||||
'user_email' => $userEmail,
|
||||
'user_name' => $userName,
|
||||
'deliverable_number' => '1000000',
|
||||
'additional_data' => [
|
||||
'filename' => $filename,
|
||||
'file_size' => strlen($response),
|
||||
'generation_timestamp' => date('Y-m-d H:i:s'),
|
||||
'api_endpoint' => 'elevenlabs-music',
|
||||
'content_type' => 'Music Generation',
|
||||
'generation_mode' => isset($data['composition_plan']) ? 'Advanced (with Composition Plan)' : 'Simple Prompt',
|
||||
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown',
|
||||
'ip_address' => $_SERVER['REMOTE_ADDR'] ?? 'Unknown'
|
||||
]
|
||||
];
|
||||
|
||||
$webhookSuccess = $webhookProcessor->sendGenerationData($webhookData);
|
||||
|
||||
if (!$webhookSuccess) {
|
||||
error_log("Webhook failed for music generation: " . $filename);
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Webhook error: " . $e->getMessage());
|
||||
}
|
||||
|
||||
return 'generated/' . $filename;
|
||||
} else {
|
||||
$error = 'Failed to save the generated music file.';
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Better error handling
|
||||
$errorResponse = json_decode($response, true);
|
||||
if (json_last_error() === JSON_ERROR_NONE && is_array($errorResponse)) {
|
||||
// Successfully parsed JSON response
|
||||
if (isset($errorResponse['detail'])) {
|
||||
$errorDetail = is_array($errorResponse['detail']) ? json_encode($errorResponse['detail']) : $errorResponse['detail'];
|
||||
} elseif (isset($errorResponse['message'])) {
|
||||
$errorDetail = is_array($errorResponse['message']) ? json_encode($errorResponse['message']) : $errorResponse['message'];
|
||||
} elseif (isset($errorResponse['error'])) {
|
||||
$errorDetail = is_array($errorResponse['error']) ? json_encode($errorResponse['error']) : $errorResponse['error'];
|
||||
} else {
|
||||
$errorDetail = json_encode($errorResponse);
|
||||
}
|
||||
} else {
|
||||
// Raw response if JSON parsing failed
|
||||
$errorDetail = substr($response, 0, 500);
|
||||
}
|
||||
$error = 'API Error (' . $httpCode . '): ' . $errorDetail;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<form method="POST" id="musicForm">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="mode">Generation Mode:</label>
|
||||
<select id="mode" name="mode" onchange="toggleMode()">
|
||||
<option value="simple" <?php echo ($_POST['mode'] ?? 'simple') === 'simple' ? 'selected' : ''; ?>>Simple Mode</option>
|
||||
<option value="advanced" <?php echo ($_POST['mode'] ?? 'simple') === 'advanced' ? 'selected' : ''; ?>>Advanced Mode (with Composition Plan)</option>
|
||||
</select>
|
||||
<div class="help-text">Simple mode uses just a prompt, Advanced mode creates a detailed composition plan first</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="text">Music Description:</label>
|
||||
<textarea id="text" name="text" placeholder="e.g., Upbeat electronic dance track with driving bass and soaring synths" required><?php echo htmlspecialchars($_POST['text'] ?? ''); ?></textarea>
|
||||
<div class="help-text">Describe the music you want to generate (max 2000 characters)</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="slider-label">
|
||||
<label for="music_length_ms">Duration (seconds):</label>
|
||||
<span class="slider-value" id="duration_value">Auto</span>
|
||||
</div>
|
||||
<div class="slider-container">
|
||||
<input type="range" id="music_length_ms" name="music_length_ms" class="slider" min="0" max="300" step="1" value="<?php echo htmlspecialchars(($_POST['music_length_ms'] ?? 0) / 1000); ?>">
|
||||
</div>
|
||||
<div class="help-text">0 = Auto duration, 10-300 seconds for manual control</div>
|
||||
</div>
|
||||
|
||||
<div id="advanced-options" style="display: none;">
|
||||
<div class="form-group">
|
||||
<label>Step 1: Generate Composition Plan</label>
|
||||
<button type="button" id="generatePlanBtn" onclick="generateCompositionPlan()">Generate Composition Plan</button>
|
||||
<div class="help-text">Creates a detailed plan including styles, sections, and structure</div>
|
||||
</div>
|
||||
|
||||
<div id="composition-plan-result" style="display: none;">
|
||||
<div class="form-group">
|
||||
<label for="composition_plan">Generated Composition Plan:</label>
|
||||
<textarea id="composition_plan" name="composition_plan" readonly style="height: 200px;"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" id="generateBtn">Generate Music</button>
|
||||
</form>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="result error">
|
||||
<strong>Error:</strong> <?php echo htmlspecialchars($error); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($result && $audioFile): ?>
|
||||
<div class="result success">
|
||||
<strong><?php echo htmlspecialchars($result); ?></strong>
|
||||
<div class="audio-player">
|
||||
<audio controls>
|
||||
<source src="<?php echo htmlspecialchars($audioFile); ?>" type="audio/mpeg">
|
||||
Your browser does not support the audio element.
|
||||
</audio>
|
||||
<br><br>
|
||||
<a href="<?php echo htmlspecialchars($audioFile); ?>" download>Download Music Track</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div> <!-- End protected content -->
|
||||
|
||||
<script>
|
||||
// Microsoft Authentication Configuration
|
||||
const msalConfig = {
|
||||
auth: {
|
||||
clientId: "9079054c-9620-4757-a256-23413042f1ef",
|
||||
authority: "https://login.microsoftonline.com/e519c2e6-bc6d-4fdf-8d9c-923c2f002385",
|
||||
redirectUri: "https://ai-sandbox.oliver.solutions/format"
|
||||
},
|
||||
cache: {
|
||||
cacheLocation: "sessionStorage",
|
||||
storeAuthStateInCookie: true,
|
||||
}
|
||||
};
|
||||
|
||||
const loginRequest = {
|
||||
scopes: ["user.read"]
|
||||
};
|
||||
|
||||
const myMSALObj = new msal.PublicClientApplication(msalConfig);
|
||||
|
||||
// Authentication Functions
|
||||
function checkAuthenticationStatus() {
|
||||
const accessToken = sessionStorage.getItem('accessToken');
|
||||
if (accessToken) {
|
||||
showProtectedContent();
|
||||
} else {
|
||||
document.getElementById('login-container').style.display = 'flex';
|
||||
document.getElementById('protected-content').style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function signIn() {
|
||||
myMSALObj.loginPopup(loginRequest)
|
||||
.then(loginResponse => {
|
||||
console.log("User logged in:", loginResponse.account.username);
|
||||
sessionStorage.setItem('accessToken', loginResponse.accessToken);
|
||||
|
||||
// Store user information for webhook tracking
|
||||
const userInfo = {
|
||||
email: loginResponse.account.username,
|
||||
name: loginResponse.account.name || loginResponse.account.username,
|
||||
userId: loginResponse.account.localAccountId
|
||||
};
|
||||
|
||||
// Send user info to server for session storage
|
||||
fetch('store_user_session.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(userInfo)
|
||||
});
|
||||
|
||||
showProtectedContent();
|
||||
}).catch(error => {
|
||||
console.error("Error during login:", error);
|
||||
document.getElementById('login-container').style.display = 'flex';
|
||||
document.getElementById('protected-content').style.display = 'none';
|
||||
});
|
||||
}
|
||||
|
||||
function signOut() {
|
||||
sessionStorage.removeItem('accessToken');
|
||||
console.log("User logged out.");
|
||||
|
||||
// Clear server-side session
|
||||
fetch('clear_user_session.php', { method: 'POST' });
|
||||
|
||||
document.getElementById('login-container').style.display = 'flex';
|
||||
document.getElementById('protected-content').style.display = 'none';
|
||||
document.getElementById('logout-button').style.display = 'none';
|
||||
}
|
||||
|
||||
function showProtectedContent() {
|
||||
const accessToken = sessionStorage.getItem('accessToken');
|
||||
if (accessToken) {
|
||||
document.getElementById('login-container').style.display = 'none';
|
||||
document.getElementById('protected-content').style.display = 'block';
|
||||
document.getElementById('logout-button').style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize authentication on page load
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Disable SSO for local testing - directly show protected content
|
||||
document.getElementById('login-container').style.display = 'none';
|
||||
document.getElementById('protected-content').style.display = 'block';
|
||||
document.getElementById('logout-button').style.display = 'none';
|
||||
|
||||
// Dark mode toggle functionality
|
||||
// Check for saved dark mode preference
|
||||
const darkModeEnabled = localStorage.getItem('darkMode') === 'enabled';
|
||||
|
||||
if (darkModeEnabled) {
|
||||
document.body.classList.add('dark-mode');
|
||||
document.getElementById('lightModeIcon').style.display = 'none';
|
||||
document.getElementById('darkModeIcon').style.display = 'block';
|
||||
}
|
||||
|
||||
// Toggle dark mode when button is clicked
|
||||
document.getElementById('darkModeToggle').addEventListener('click', function() {
|
||||
document.body.classList.toggle('dark-mode');
|
||||
|
||||
// Save preference and toggle icons
|
||||
if (document.body.classList.contains('dark-mode')) {
|
||||
localStorage.setItem('darkMode', 'enabled');
|
||||
document.getElementById('lightModeIcon').style.display = 'none';
|
||||
document.getElementById('darkModeIcon').style.display = 'block';
|
||||
} else {
|
||||
localStorage.setItem('darkMode', 'disabled');
|
||||
document.getElementById('lightModeIcon').style.display = 'block';
|
||||
document.getElementById('darkModeIcon').style.display = 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Update slider values in real-time
|
||||
const durationSlider = document.getElementById('music_length_ms');
|
||||
const durationValue = document.getElementById('duration_value');
|
||||
|
||||
function updateDurationValue() {
|
||||
const value = parseFloat(durationSlider.value);
|
||||
durationValue.textContent = value === 0 ? 'Auto' : value + 's';
|
||||
}
|
||||
|
||||
function toggleMode() {
|
||||
const mode = document.getElementById('mode').value;
|
||||
const advancedOptions = document.getElementById('advanced-options');
|
||||
|
||||
if (mode === 'advanced') {
|
||||
advancedOptions.style.display = 'block';
|
||||
} else {
|
||||
advancedOptions.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function generateCompositionPlan() {
|
||||
const prompt = document.getElementById('text').value;
|
||||
const musicLengthMs = document.getElementById('music_length_ms').value * 1000;
|
||||
const generatePlanBtn = document.getElementById('generatePlanBtn');
|
||||
|
||||
if (!prompt.trim()) {
|
||||
alert('Please enter a music description first.');
|
||||
return;
|
||||
}
|
||||
|
||||
generatePlanBtn.disabled = true;
|
||||
generatePlanBtn.textContent = 'Generating Plan...';
|
||||
|
||||
// Make AJAX request to generate composition plan
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', 'generate_plan.php', true);
|
||||
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
|
||||
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState === 4) {
|
||||
generatePlanBtn.disabled = false;
|
||||
generatePlanBtn.textContent = 'Generate Composition Plan';
|
||||
|
||||
if (xhr.status === 200) {
|
||||
try {
|
||||
const response = JSON.parse(xhr.responseText);
|
||||
if (response.success) {
|
||||
document.getElementById('composition_plan').value = JSON.stringify(response.plan, null, 2);
|
||||
document.getElementById('composition-plan-result').style.display = 'block';
|
||||
} else {
|
||||
alert('Error generating plan: ' + response.error);
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Error parsing response');
|
||||
}
|
||||
} else {
|
||||
alert('Error generating composition plan');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const params = 'prompt=' + encodeURIComponent(prompt) +
|
||||
'&music_length_ms=' + encodeURIComponent(musicLengthMs);
|
||||
xhr.send(params);
|
||||
}
|
||||
|
||||
durationSlider.addEventListener('input', updateDurationValue);
|
||||
|
||||
// Initialize values on page load
|
||||
updateDurationValue();
|
||||
toggleMode(); // Initialize mode display
|
||||
|
||||
document.getElementById('musicForm').addEventListener('submit', function() {
|
||||
const btn = document.getElementById('generateBtn');
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Generating Music... Please wait';
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
<?php
|
||||
// Store SSO user information in PHP session for webhook tracking
|
||||
|
||||
session_start();
|
||||
|
||||
// Only accept POST requests
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'error' => 'Method not allowed']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Get JSON input
|
||||
$input = file_get_contents('php://input');
|
||||
$userData = json_decode($input, true);
|
||||
|
||||
if (!$userData) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'error' => 'Invalid JSON data']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Store user information in session for webhook usage
|
||||
if (isset($userData['email'])) {
|
||||
$_SESSION['sso_user_email'] = $userData['email'];
|
||||
}
|
||||
|
||||
if (isset($userData['name'])) {
|
||||
$_SESSION['sso_user_name'] = $userData['name'];
|
||||
}
|
||||
|
||||
if (isset($userData['userId'])) {
|
||||
$_SESSION['sso_user_id'] = $userData['userId'];
|
||||
}
|
||||
|
||||
// Store timestamp of login
|
||||
$_SESSION['sso_login_timestamp'] = time();
|
||||
|
||||
// Log successful session storage
|
||||
error_log("SSO user session stored: " . $userData['email'] ?? 'Unknown');
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'User session stored successfully'
|
||||
]);
|
||||
?>
|
||||
|
|
@ -1,339 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Audio Webhook Processor for AI Sound Generation Applications
|
||||
*
|
||||
* Adapted from the standardized WebhookProcessor for audio files.
|
||||
* This class provides a standardized way to integrate webhook functionality
|
||||
* into any application that generates AI audio/sound effects.
|
||||
*
|
||||
* Usage:
|
||||
* require_once 'webhook_processor_audio.php';
|
||||
* $processor = new AudioWebhookProcessor(['webhook_url' => 'https://your-hook.com']);
|
||||
* $processor->sendGenerationData($data);
|
||||
*/
|
||||
|
||||
class AudioWebhookProcessor {
|
||||
private $config;
|
||||
private $defaultWebhookUrl = 'https://hook.us1.make.celonis.com/sbhcpk0athbdbxxmgijxc5sbwtjsg33h';
|
||||
|
||||
/**
|
||||
* Initialize webhook processor with configuration
|
||||
*
|
||||
* @param array $config Configuration options:
|
||||
* - webhook_url: Target webhook URL (optional)
|
||||
* - client: Default client name (optional)
|
||||
* - user_email: Default user email (optional)
|
||||
* - deliverable_number: Default deliverable number (optional)
|
||||
* - timeout: cURL timeout in seconds (default: 30)
|
||||
* - verify_ssl: Whether to verify SSL certificates (default: true)
|
||||
*/
|
||||
public function __construct($config = []) {
|
||||
$this->config = array_merge([
|
||||
'webhook_url' => $this->defaultWebhookUrl,
|
||||
'client' => 'Auto-Approved',
|
||||
'user_email' => 'ai-comp-setup@oliver.agency',
|
||||
'deliverable_number' => '1000000',
|
||||
'timeout' => 30,
|
||||
'verify_ssl' => true
|
||||
], $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send AI sound generation data to webhook
|
||||
*
|
||||
* @param array $data Generation data:
|
||||
* - prompt: The user's text prompt (required)
|
||||
* - generation_type: Type of AI generation (ElevenLabs, etc.)
|
||||
* - settings: Generation settings array (duration, prompt_influence, etc.)
|
||||
* - audio_data: Raw audio binary data OR audio URL OR file path
|
||||
* - audio_url: Alternative to audio_data - URL to download audio
|
||||
* - audio_file: Alternative to audio_data - file path to read audio
|
||||
* - client: Client name (overrides default)
|
||||
* - user_email: User email (overrides default)
|
||||
* - deliverable_number: Deliverable number (overrides default)
|
||||
* - additional_data: Extra data to include in webhook
|
||||
* @param string $webhookUrl Optional webhook URL override
|
||||
* @return bool True on success, false on failure
|
||||
*/
|
||||
public function sendGenerationData($data, $webhookUrl = null) {
|
||||
$webhookUrl = $webhookUrl ?: $this->config['webhook_url'];
|
||||
|
||||
// Build standardized webhook data
|
||||
$webhookData = $this->buildWebhookData($data);
|
||||
|
||||
// Send the webhook
|
||||
return $this->sendWebhook($webhookData, $webhookUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build standardized webhook data structure for audio
|
||||
*
|
||||
* @param array $data Input data
|
||||
* @return array Standardized webhook data
|
||||
*/
|
||||
private function buildWebhookData($data) {
|
||||
$webhookData = [
|
||||
'client' => $data['client'] ?? $this->config['client'],
|
||||
'deliverableNumber' => $data['deliverable_number'] ?? $this->config['deliverable_number'],
|
||||
'userEmail' => $data['user_email'] ?? $this->config['user_email'],
|
||||
'generationType' => $data['generation_type'] ?? 'ElevenLabs Sound Effects',
|
||||
'settings' => $data['settings'] ?? [],
|
||||
'timestamp' => time(),
|
||||
];
|
||||
|
||||
// Add prompt to settings if provided
|
||||
if (isset($data['prompt'])) {
|
||||
$webhookData['settings']['prompt'] = $data['prompt'];
|
||||
}
|
||||
|
||||
// Process audio data if provided
|
||||
if (isset($data['audio_data'])) {
|
||||
$normalized = $this->normalizeAudioData($data['audio_data']);
|
||||
if ($normalized) {
|
||||
$webhookData['audioFile'] = $normalized['base64'];
|
||||
$webhookData['audioMimeType'] = $normalized['mime_type'];
|
||||
}
|
||||
} elseif (isset($data['audio_url'])) {
|
||||
$normalized = $this->processAudioUrl($data['audio_url']);
|
||||
if ($normalized) {
|
||||
$webhookData['audioFile'] = $normalized['base64'];
|
||||
$webhookData['audioMimeType'] = $normalized['mime_type'];
|
||||
}
|
||||
} elseif (isset($data['audio_file'])) {
|
||||
$normalized = $this->processAudioFile($data['audio_file']);
|
||||
if ($normalized) {
|
||||
$webhookData['audioFile'] = $normalized['base64'];
|
||||
$webhookData['audioMimeType'] = $normalized['mime_type'];
|
||||
}
|
||||
}
|
||||
|
||||
// Add any additional data
|
||||
if (isset($data['additional_data']) && is_array($data['additional_data'])) {
|
||||
$webhookData = array_merge($webhookData, $data['additional_data']);
|
||||
}
|
||||
|
||||
return $webhookData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize audio data to standard format
|
||||
*
|
||||
* @param mixed $audioData Raw binary data, base64 string, or data URI
|
||||
* @return array|null Normalized audio data or null on failure
|
||||
*/
|
||||
private function normalizeAudioData($audioData) {
|
||||
// If already a data URI, extract the data
|
||||
if (is_string($audioData) && strpos($audioData, 'data:') === 0) {
|
||||
$parts = explode(',', $audioData, 2);
|
||||
if (count($parts) === 2) {
|
||||
$mimeType = explode(';', explode(':', $parts[0])[1])[0];
|
||||
$audioData = base64_decode($parts[1]);
|
||||
}
|
||||
}
|
||||
// If base64 encoded string, decode it
|
||||
elseif (is_string($audioData) && base64_decode($audioData, true) !== false) {
|
||||
$audioData = base64_decode($audioData);
|
||||
}
|
||||
|
||||
// Detect mime type
|
||||
$finfo = new finfo(FILEINFO_MIME_TYPE);
|
||||
$mimeType = $finfo->buffer($audioData);
|
||||
|
||||
// Validate that it's an audio file
|
||||
if (strpos($mimeType, 'audio/') !== 0) {
|
||||
// Try common audio extensions based on magic bytes
|
||||
$audioTypes = [
|
||||
'audio/mpeg' => ["\xFF\xFB", "\xFF\xF3", "\xFF\xF2"], // MP3
|
||||
'audio/wav' => ["RIFF"], // WAV
|
||||
'audio/ogg' => ["OggS"], // OGG
|
||||
'audio/mp4' => ["\x00\x00\x00"], // M4A/MP4
|
||||
];
|
||||
|
||||
foreach ($audioTypes as $type => $signatures) {
|
||||
foreach ($signatures as $signature) {
|
||||
if (strpos($audioData, $signature) === 0) {
|
||||
$mimeType = $type;
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'mime_type' => $mimeType,
|
||||
'base64' => 'data:' . $mimeType . ';base64,' . base64_encode($audioData),
|
||||
'data' => $audioData,
|
||||
'size' => strlen($audioData)
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Process audio from URL
|
||||
*
|
||||
* @param string $audioUrl URL to download
|
||||
* @return array|null Normalized audio data or null on failure
|
||||
*/
|
||||
private function processAudioUrl($audioUrl) {
|
||||
$context = stream_context_create([
|
||||
'http' => [
|
||||
'timeout' => $this->config['timeout'],
|
||||
'user_agent' => 'AudioWebhookProcessor/1.0'
|
||||
]
|
||||
]);
|
||||
|
||||
$audioData = @file_get_contents($audioUrl, false, $context);
|
||||
|
||||
if ($audioData === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->normalizeAudioData($audioData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process audio from file path
|
||||
*
|
||||
* @param string $filePath Path to audio file
|
||||
* @return array|null Normalized audio data or null on failure
|
||||
*/
|
||||
private function processAudioFile($filePath) {
|
||||
if (!file_exists($filePath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$audioData = file_get_contents($filePath);
|
||||
|
||||
if ($audioData === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->normalizeAudioData($audioData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send webhook using cURL
|
||||
*
|
||||
* @param array $data Data to send
|
||||
* @param string $webhookUrl Target URL
|
||||
* @return bool True on success, false on failure
|
||||
*/
|
||||
private function sendWebhook($data, $webhookUrl) {
|
||||
$ch = curl_init($webhookUrl);
|
||||
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_POSTFIELDS => json_encode($data),
|
||||
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_SSL_VERIFYPEER => $this->config['verify_ssl'],
|
||||
CURLOPT_TIMEOUT => $this->config['timeout'],
|
||||
CURLOPT_USERAGENT => 'AudioWebhookProcessor/1.0'
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curlError = curl_error($ch);
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
// Log errors if debugging is enabled
|
||||
if ($httpCode < 200 || $httpCode >= 300 || !empty($curlError)) {
|
||||
error_log("Audio Webhook failed: HTTP {$httpCode}, Error: {$curlError}, URL: {$webhookUrl}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test webhook endpoint connectivity
|
||||
*
|
||||
* @param string $webhookUrl Optional URL to test (uses default if not provided)
|
||||
* @return array Test results with status, http_code, and response_time
|
||||
*/
|
||||
public function testWebhook($webhookUrl = null) {
|
||||
$webhookUrl = $webhookUrl ?: $this->config['webhook_url'];
|
||||
|
||||
$testData = [
|
||||
'test' => true,
|
||||
'timestamp' => time(),
|
||||
'client' => 'AudioWebhookProcessor Test',
|
||||
'generationType' => 'ElevenLabs Sound Effects Test',
|
||||
'settings' => [
|
||||
'prompt' => 'Test sound generation',
|
||||
'duration_seconds' => 5.0,
|
||||
'prompt_influence' => 0.3
|
||||
]
|
||||
];
|
||||
|
||||
$startTime = microtime(true);
|
||||
$ch = curl_init($webhookUrl);
|
||||
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_POSTFIELDS => json_encode($testData),
|
||||
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_SSL_VERIFYPEER => $this->config['verify_ssl'],
|
||||
CURLOPT_TIMEOUT => $this->config['timeout'],
|
||||
CURLOPT_USERAGENT => 'AudioWebhookProcessor/1.0 Test'
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curlError = curl_error($ch);
|
||||
$responseTime = round((microtime(true) - $startTime) * 1000, 2);
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
return [
|
||||
'success' => ($httpCode >= 200 && $httpCode < 300 && empty($curlError)),
|
||||
'http_code' => $httpCode,
|
||||
'response_time_ms' => $responseTime,
|
||||
'error' => $curlError,
|
||||
'response' => $response
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current configuration
|
||||
*
|
||||
* @return array Current configuration (sensitive values masked)
|
||||
*/
|
||||
public function getConfig() {
|
||||
$config = $this->config;
|
||||
// Mask sensitive webhook URL
|
||||
if (isset($config['webhook_url'])) {
|
||||
$config['webhook_url'] = substr($config['webhook_url'], 0, 30) . '...';
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update configuration
|
||||
*
|
||||
* @param array $newConfig New configuration values
|
||||
* @return void
|
||||
*/
|
||||
public function updateConfig($newConfig) {
|
||||
$this->config = array_merge($this->config, $newConfig);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function for quick audio webhook sending
|
||||
*
|
||||
* @param array $data Generation data
|
||||
* @param string $webhookUrl Webhook URL
|
||||
* @param array $config Optional processor configuration
|
||||
* @return bool Success status
|
||||
*/
|
||||
function sendAudioWebhook($data, $webhookUrl = null, $config = []) {
|
||||
if ($webhookUrl) {
|
||||
$config['webhook_url'] = $webhookUrl;
|
||||
}
|
||||
|
||||
$processor = new AudioWebhookProcessor($config);
|
||||
return $processor->sendGenerationData($data);
|
||||
}
|
||||
?>
|
||||
Loading…
Add table
Reference in a new issue