fix ffmpeg
This commit is contained in:
146
README.md
146
README.md
@@ -27,6 +27,8 @@ Piattaforma completa per vendere videolezioni e lezioni live di Pilates. Svilupp
|
|||||||
- **Gestione utenti** (visualizza, blocca/sblocca, statistiche acquisti)
|
- **Gestione utenti** (visualizza, blocca/sblocca, statistiche acquisti)
|
||||||
- **Storico acquisti** con dettagli pagamenti PayPal e grafici
|
- **Storico acquisti** con dettagli pagamenti PayPal e grafici
|
||||||
- **Report e analisi** vendite con totali formattati
|
- **Report e analisi** vendite con totali formattati
|
||||||
|
- **Profilo amministratore** con cambio password e test invio email
|
||||||
|
- **Conversione video** automatica in formato web-compatible (MP4 H.264)
|
||||||
|
|
||||||
### Funzionalità Tecniche
|
### Funzionalità Tecniche
|
||||||
- ✅ **Sicurezza**: Password crittografate con bcrypt (PHP 8.1+ compatibile)
|
- ✅ **Sicurezza**: Password crittografate con bcrypt (PHP 8.1+ compatibile)
|
||||||
@@ -39,12 +41,17 @@ Piattaforma completa per vendere videolezioni e lezioni live di Pilates. Svilupp
|
|||||||
- ✅ **Soft delete**: I dati eliminati non sono rimossi definitivamente
|
- ✅ **Soft delete**: I dati eliminati non sono rimossi definitivamente
|
||||||
- ✅ **Gestione errori**: Controllo null values per compatibilità PHP 8.1+
|
- ✅ **Gestione errori**: Controllo null values per compatibilità PHP 8.1+
|
||||||
- ✅ **Flash messages**: Sistema di messaggi temporanei per feedback utente
|
- ✅ **Flash messages**: Sistema di messaggi temporanei per feedback utente
|
||||||
|
- ✅ **Email benvenuto**: Invio automatico email alla registrazione
|
||||||
---
|
- ✅ **Test email**: Strumento test SMTP nell'area admin
|
||||||
|
- ✅ **Video protetti**: Streaming sicuro con protezione download
|
||||||
## 🚀 Installazione
|
- ✅ **Conversione video**: Automatica in MP4 H.264 per compatibilità browser
|
||||||
|
|
||||||
### Requisiti
|
### Requisiti
|
||||||
|
- **PHP** 7.4 o superiore (8.0+ raccomandato)
|
||||||
|
- **MySQL** 5.7 o superiore (o MariaDB 10.3+)
|
||||||
|
- **Web Server** (Apache, Nginx, o PHP built-in server per sviluppo)
|
||||||
|
- **Account PayPal** Sandbox (per test) o Business (per produzione)
|
||||||
|
- **FFmpeg** (opzionale, per conversione automatica video in MP4)
|
||||||
- **PHP** 7.4 o superiore
|
- **PHP** 7.4 o superiore
|
||||||
- **MySQL** 5.7 o superiore (o MariaDB 10.3+)
|
- **MySQL** 5.7 o superiore (o MariaDB 10.3+)
|
||||||
- **Web Server** (Apache, Nginx, o PHP built-in server per sviluppo)
|
- **Web Server** (Apache, Nginx, o PHP built-in server per sviluppo)
|
||||||
@@ -194,11 +201,12 @@ Poi apri: `http://localhost:8000`
|
|||||||
## 📖 Guida all'Uso
|
## 📖 Guida all'Uso
|
||||||
|
|
||||||
### Per Amministratori
|
### Per Amministratori
|
||||||
|
|
||||||
#### Primo Accesso
|
#### Primo Accesso
|
||||||
1. Vai su `http://tuo-sito/login.php`
|
1. Vai su `http://tuo-sito/login.php`
|
||||||
2. Accedi con le credenziali di default
|
2. Accedi con le credenziali di default
|
||||||
3. Vai su "Area Admin" → "Profilo" e cambia la password
|
3. Vai su "Area Admin" → "Profilo" e cambia la password
|
||||||
|
4. Testa l'invio email usando il bottone "📨 Invia Email di Test" nel profilo admin
|
||||||
|
3. Vai su "Area Admin" → "Profilo" e cambia la password
|
||||||
|
|
||||||
#### Creare una Nuova Lezione
|
#### Creare una Nuova Lezione
|
||||||
1. **Area Admin** → **Gestione Lezioni** → **Nuova Lezione**
|
1. **Area Admin** → **Gestione Lezioni** → **Nuova Lezione**
|
||||||
@@ -332,16 +340,18 @@ define('USE_SMTP', false); // In config.php
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🎥 Hosting Video
|
### Opzione 1: File Locali (Upload Diretto) ⭐ **RACCOMANDATO**
|
||||||
|
|
||||||
### Opzione 1: File Locali (Upload Diretto) ⭐ **NUOVO**
|
|
||||||
1. Nella creazione/modifica lezione, seleziona **File Locale**
|
1. Nella creazione/modifica lezione, seleziona **File Locale**
|
||||||
2. Clicca su "Scegli file" e seleziona il video dal tuo PC
|
2. Clicca su "Scegli file" e seleziona il video dal tuo PC
|
||||||
3. Il sistema carica automaticamente il file in:
|
3. Il sistema carica automaticamente il file in:
|
||||||
- `uploads/lessons/demo/` per lezioni gratuite
|
- `uploads/lessons/demo/` per lezioni gratuite
|
||||||
- `uploads/lessons/pay/` per lezioni a pagamento
|
- `uploads/lessons/pay/` per lezioni a pagamento
|
||||||
4. Formati supportati: MP4, WebM, OGG, MOV
|
4. Formati supportati: MP4, WebM, OGG, MOV
|
||||||
5. ✅ **Vantaggi**: Upload automatico, nessun inserimento manuale del path
|
5. ✅ **Vantaggi**:
|
||||||
|
- Upload automatico
|
||||||
|
- Conversione automatica in MP4 se FFmpeg disponibile
|
||||||
|
- Streaming protetto con divieto download
|
||||||
|
- Controllo accessi basato su permessi utente
|
||||||
6. ⚠️ Limiti: Dimensione massima upload dipende da `php.ini` (default ~2MB)
|
6. ⚠️ Limiti: Dimensione massima upload dipende da `php.ini` (default ~2MB)
|
||||||
|
|
||||||
**Per aumentare il limite di upload:**
|
**Per aumentare il limite di upload:**
|
||||||
@@ -352,6 +362,27 @@ post_max_size = 500M
|
|||||||
max_execution_time = 300
|
max_execution_time = 300
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Conversione Automatica Video:**
|
||||||
|
Se carichi video in formato diverso da MP4, il sistema:
|
||||||
|
1. Verifica se FFmpeg è installato
|
||||||
|
2. Converte automaticamente il video in MP4 H.264 con codec AAC
|
||||||
|
3. Ottimizza per lo streaming web (fast start)
|
||||||
|
4. Sostituisce il file originale con quello convertito
|
||||||
|
|
||||||
|
**Per installare FFmpeg:**
|
||||||
|
```bash
|
||||||
|
# Ubuntu/Debian
|
||||||
|
sudo apt-get install ffmpeg
|
||||||
|
|
||||||
|
# CentOS/RHEL
|
||||||
|
sudo yum install ffmpeg
|
||||||
|
|
||||||
|
# Mac
|
||||||
|
brew install ffmpeg
|
||||||
|
```t_max_size = 500M
|
||||||
|
max_execution_time = 300
|
||||||
|
```
|
||||||
|
|
||||||
### Opzione 2: YouTube (Consigliato per Video Grandi)
|
### Opzione 2: YouTube (Consigliato per Video Grandi)
|
||||||
1. Carica video su YouTube come **Non in elenco**
|
1. Carica video su YouTube come **Non in elenco**
|
||||||
2. Copia l'URL (es: `https://youtube.com/watch?v=ABC123`)
|
2. Copia l'URL (es: `https://youtube.com/watch?v=ABC123`)
|
||||||
@@ -378,18 +409,23 @@ define('AWS_BUCKET', 'pilates-videos');
|
|||||||
4. Usa URL S3 nelle lezioni
|
4. Usa URL S3 nelle lezioni
|
||||||
|
|
||||||
---
|
---
|
||||||
|
### Checklist Produzione
|
||||||
|
Prima di mettere online:
|
||||||
|
|
||||||
## 💳 Configurare PayPal
|
- [ ] Cambia password admin di default
|
||||||
|
- [ ] Cambia `SECRET_KEY` in `config.php`
|
||||||
### Modalità Test (Sandbox)
|
- [ ] Imposta `DEBUG_MODE` a `false`
|
||||||
1. Vai su [developer.paypal.com](https://developer.paypal.com)
|
- [ ] Usa HTTPS (certificato SSL)
|
||||||
2. Crea un'app Sandbox
|
- [ ] Limita permessi cartelle (755 per cartelle, 644 per file)
|
||||||
3. Copia Client ID e Secret in `config.php`
|
- [ ] Backup regolari del database
|
||||||
4. Usa gli account test per simulare pagamenti
|
- [ ] Configura SMTP per email
|
||||||
|
- [ ] Testa invio email con bottone nel profilo admin
|
||||||
### Modalità Produzione (Reale)
|
- [ ] Testa email di benvenuto alla registrazione
|
||||||
1. Vai su [paypal.com/businessmanage](https://www.paypal.com/businessmanage)
|
- [ ] Converti tutti i video in MP4 H.264 (se necessario)
|
||||||
2. Crea app Live
|
- [ ] Testa streaming video protetto
|
||||||
|
- [ ] Testa tutti i flussi di pagamento
|
||||||
|
- [ ] Verifica che `includes/config.php` NON sia accessibile via web
|
||||||
|
- [ ] **ELIMINA** `test_password.php` e `test_email.php` (rischio sicurezza!)
|
||||||
3. Copia credenziali Live
|
3. Copia credenziali Live
|
||||||
4. In `config.php`:
|
4. In `config.php`:
|
||||||
```php
|
```php
|
||||||
@@ -417,12 +453,17 @@ Prima di mettere online:
|
|||||||
|
|
||||||
### File .htaccess (per Apache)
|
### File .htaccess (per Apache)
|
||||||
Crea `.htaccess` nella cartella `includes/`:
|
Crea `.htaccess` nella cartella `includes/`:
|
||||||
```apache
|
### I video non si vedono
|
||||||
# Nega accesso a config.php
|
- Verifica che l'URL sia corretto
|
||||||
<Files "config.php">
|
- Per YouTube/Vimeo: usa URL diretti al video
|
||||||
Require all denied
|
- Per file locali caricati: controlla che esistano in `uploads/lessons/demo/` o `pay/`
|
||||||
</Files>
|
- Verifica permessi cartella uploads (755)
|
||||||
```
|
- Per video grandi, verifica limiti upload in `php.ini`
|
||||||
|
- **Se vedi schermo nero ma senti audio**: il video ha codec incompatibile
|
||||||
|
- Vai su **Area Admin** → **🔄 Converti Video**
|
||||||
|
- Converti il video in MP4 H.264
|
||||||
|
- Oppure ricarica il video in formato MP4 già codificato correttamente
|
||||||
|
- Verifica nella console browser (F12) eventuali errori di caricamento
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -506,10 +547,12 @@ pilates-platform/
|
|||||||
│ └── js/
|
│ └── js/
|
||||||
│ └── main.js # JavaScript
|
│ └── main.js # JavaScript
|
||||||
│
|
│
|
||||||
├── database/
|
├── process_payment.php # Elabora pagamento PayPal
|
||||||
│ ├── schema.sql # Script creazione database
|
├── stream_video.php # Streaming protetto video (verifica permessi)
|
||||||
│ └── reset_admin_password.sql # Reset password admin
|
├── convert_videos.php # Conversione batch video in MP4 H.264
|
||||||
│
|
├── test_password.php # Utility test hash password (debug)
|
||||||
|
├── test_email.php # Utility test invio email SMTP (debug)
|
||||||
|
└── README.md # Questo file
|
||||||
├── includes/ # File PHP condivisi
|
├── includes/ # File PHP condivisi
|
||||||
│ ├── config.php # Configurazione
|
│ ├── config.php # Configurazione
|
||||||
│ ├── functions.php # Funzioni comuni
|
│ ├── functions.php # Funzioni comuni
|
||||||
@@ -570,25 +613,32 @@ MySQL è dove vengono salvati i dati:
|
|||||||
3. Utente paga
|
3. Utente paga
|
||||||
4. PayPal conferma il pagamento
|
4. PayPal conferma il pagamento
|
||||||
5. `process_payment.php` registra l'acquisto nel DB
|
5. `process_payment.php` registra l'acquisto nel DB
|
||||||
|
### Checklist Prossimi Passi
|
||||||
|
|
||||||
### Dove Modificare il Codice?
|
Dopo l'installazione:
|
||||||
- **Aspetto grafico**: `assets/css/style.css`
|
- [ ] Cambia password admin (profilo o reset_admin_password.sql)
|
||||||
- **Comportamento**: File `.php` specifici
|
- [ ] **Configura Gmail SMTP** (obbligatorio per recupero password)
|
||||||
- **Database**: Usa phpMyAdmin o client MySQL
|
- [ ] Genera password applicazione Gmail
|
||||||
- **Impostazioni**: `includes/config.php`
|
- [ ] Inserisci credenziali SMTP in config.php
|
||||||
|
- [ ] Testa invio email dal profilo admin
|
||||||
---
|
- [ ] Testa recupero password
|
||||||
|
- [ ] Testa email benvenuto registrazione
|
||||||
## 📞 Supporto e Manutenzione
|
- [ ] Installa FFmpeg (opzionale, per conversione video)
|
||||||
|
- [ ] Configura PayPal Sandbox
|
||||||
### Backup Database
|
- [ ] Carica almeno 3 lezioni demo con video
|
||||||
Esegui regolarmente (es: settimanalmente):
|
- [ ] Verifica che i video si vedano correttamente
|
||||||
```bash
|
- [ ] Testa registrazione utente
|
||||||
mysqldump -u root -p pilatesplatform > backup_$(date +%Y%m%d).sql
|
- [ ] Testa acquisto lezione (in sandbox)
|
||||||
```
|
- [ ] Verifica streaming protetto video
|
||||||
|
- [ ] Testa modifica lezioni esistenti
|
||||||
### Aggiornare PHP
|
- [ ] Personalizza colori e logo
|
||||||
Quando aggiorni PHP, testa la piattaforma in locale prima di aggiornare in produzione.
|
- [ ] Configura backup automatici database
|
||||||
|
- [ ] Passa a produzione PayPal
|
||||||
|
- [ ] Attiva HTTPS (certificato SSL)
|
||||||
|
- [ ] Converti tutti i video in MP4 se necessario
|
||||||
|
- [ ] **ELIMINA file di debug** (test_password.php, test_email.php)
|
||||||
|
- [ ] Testa tutti i flussi su produzione
|
||||||
|
- [ ] Lancia! 🚀PHP, testa la piattaforma in locale prima di aggiornare in produzione.
|
||||||
|
|
||||||
### Log Attività
|
### Log Attività
|
||||||
La tabella `activity_log` traccia:
|
La tabella `activity_log` traccia:
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ $top_lessons = $stmt->fetchAll();
|
|||||||
<li><a href="lessons.php">🎥 Gestione Lezioni</a></li>
|
<li><a href="lessons.php">🎥 Gestione Lezioni</a></li>
|
||||||
<li><a href="users.php">👥 Gestione Utenti</a></li>
|
<li><a href="users.php">👥 Gestione Utenti</a></li>
|
||||||
<li><a href="purchases.php">💰 Acquisti</a></li>
|
<li><a href="purchases.php">💰 Acquisti</a></li>
|
||||||
|
<li><a href="../convert_videos.php">🔄 Converti Video</a></li>
|
||||||
<li><a href="profile.php">👤 Profilo</a></li>
|
<li><a href="profile.php">👤 Profilo</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</aside>
|
</aside>
|
||||||
|
|||||||
@@ -52,6 +52,34 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$upload_path = $upload_dir . $file_name;
|
$upload_path = $upload_dir . $file_name;
|
||||||
|
|
||||||
if (move_uploaded_file($_FILES['video_file']['tmp_name'], $upload_path)) {
|
if (move_uploaded_file($_FILES['video_file']['tmp_name'], $upload_path)) {
|
||||||
|
// Se il video non è MP4, prova a convertirlo automaticamente
|
||||||
|
if ($file_extension !== 'mp4') {
|
||||||
|
$converted_path = $upload_dir . pathinfo($file_name, PATHINFO_FILENAME) . '.mp4';
|
||||||
|
|
||||||
|
// Verifica se FFmpeg è disponibile
|
||||||
|
$ffmpeg_check = [];
|
||||||
|
exec('ffmpeg -version 2>&1', $ffmpeg_check, $ffmpeg_return);
|
||||||
|
|
||||||
|
if ($ffmpeg_return === 0) {
|
||||||
|
// Converti il video in MP4
|
||||||
|
$convert_cmd = sprintf(
|
||||||
|
'ffmpeg -i %s -c:v libx264 -preset medium -crf 23 -c:a aac -b:a 128k -movflags +faststart -y %s 2>&1',
|
||||||
|
escapeshellarg($upload_path),
|
||||||
|
escapeshellarg($converted_path)
|
||||||
|
);
|
||||||
|
|
||||||
|
exec($convert_cmd, $convert_output, $convert_return);
|
||||||
|
|
||||||
|
if ($convert_return === 0 && file_exists($converted_path)) {
|
||||||
|
// Conversione riuscita, elimina il file originale e usa quello convertito
|
||||||
|
unlink($upload_path);
|
||||||
|
$file_name = pathinfo($file_name, PATHINFO_FILENAME) . '.mp4';
|
||||||
|
$upload_path = $converted_path;
|
||||||
|
}
|
||||||
|
// Se la conversione fallisce, usa comunque il file originale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Imposta il percorso relativo per il database (senza slash iniziale)
|
// Imposta il percorso relativo per il database (senza slash iniziale)
|
||||||
$video_url = 'uploads/lessons/' . ($is_demo ? 'demo' : 'pay') . '/' . $file_name;
|
$video_url = 'uploads/lessons/' . ($is_demo ? 'demo' : 'pay') . '/' . $file_name;
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,34 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$upload_path = $upload_dir . $file_name;
|
$upload_path = $upload_dir . $file_name;
|
||||||
|
|
||||||
if (move_uploaded_file($_FILES['video_file']['tmp_name'], $upload_path)) {
|
if (move_uploaded_file($_FILES['video_file']['tmp_name'], $upload_path)) {
|
||||||
|
// Se il video non è MP4, prova a convertirlo automaticamente
|
||||||
|
if ($file_extension !== 'mp4') {
|
||||||
|
$converted_path = $upload_dir . pathinfo($file_name, PATHINFO_FILENAME) . '.mp4';
|
||||||
|
|
||||||
|
// Verifica se FFmpeg è disponibile
|
||||||
|
$ffmpeg_check = [];
|
||||||
|
exec('ffmpeg -version 2>&1', $ffmpeg_check, $ffmpeg_return);
|
||||||
|
|
||||||
|
if ($ffmpeg_return === 0) {
|
||||||
|
// Converti il video in MP4
|
||||||
|
$convert_cmd = sprintf(
|
||||||
|
'ffmpeg -i %s -c:v libx264 -preset medium -crf 23 -c:a aac -b:a 128k -movflags +faststart -y %s 2>&1',
|
||||||
|
escapeshellarg($upload_path),
|
||||||
|
escapeshellarg($converted_path)
|
||||||
|
);
|
||||||
|
|
||||||
|
exec($convert_cmd, $convert_output, $convert_return);
|
||||||
|
|
||||||
|
if ($convert_return === 0 && file_exists($converted_path)) {
|
||||||
|
// Conversione riuscita, elimina il file originale e usa quello convertito
|
||||||
|
unlink($upload_path);
|
||||||
|
$file_name = pathinfo($file_name, PATHINFO_FILENAME) . '.mp4';
|
||||||
|
$upload_path = $converted_path;
|
||||||
|
}
|
||||||
|
// Se la conversione fallisce, usa comunque il file originale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Elimina il vecchio file se esisteva
|
// Elimina il vecchio file se esisteva
|
||||||
$old_video_path = ltrim($lesson['video_url'], '/');
|
$old_video_path = ltrim($lesson['video_url'], '/');
|
||||||
if (!empty($lesson['video_url']) && file_exists('../' . $old_video_path)) {
|
if (!empty($lesson['video_url']) && file_exists('../' . $old_video_path)) {
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ $lessons = $stmt->fetchAll();
|
|||||||
<li><a href="lessons.php" class="active">🎥 Gestione Lezioni</a></li>
|
<li><a href="lessons.php" class="active">🎥 Gestione Lezioni</a></li>
|
||||||
<li><a href="users.php">👥 Gestione Utenti</a></li>
|
<li><a href="users.php">👥 Gestione Utenti</a></li>
|
||||||
<li><a href="purchases.php">💰 Acquisti</a></li>
|
<li><a href="purchases.php">💰 Acquisti</a></li>
|
||||||
|
<li><a href="../convert_videos.php">🔄 Converti Video</a></li>
|
||||||
<li><a href="profile.php">👤 Profilo</a></li>
|
<li><a href="profile.php">👤 Profilo</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</aside>
|
</aside>
|
||||||
|
|||||||
@@ -183,6 +183,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['test_email'])) {
|
|||||||
<li><a href="lessons.php">🎥 Gestione Lezioni</a></li>
|
<li><a href="lessons.php">🎥 Gestione Lezioni</a></li>
|
||||||
<li><a href="users.php">👥 Gestione Utenti</a></li>
|
<li><a href="users.php">👥 Gestione Utenti</a></li>
|
||||||
<li><a href="purchases.php">💰 Acquisti</a></li>
|
<li><a href="purchases.php">💰 Acquisti</a></li>
|
||||||
|
<li><a href="../convert_videos.php">🔄 Converti Video</a></li>
|
||||||
<li><a href="profile.php" class="active">👤 Profilo</a></li>
|
<li><a href="profile.php" class="active">👤 Profilo</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</aside>
|
</aside>
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ $stats = $stmt->fetch();
|
|||||||
<li><a href="lessons.php">🎥 Gestione Lezioni</a></li>
|
<li><a href="lessons.php">🎥 Gestione Lezioni</a></li>
|
||||||
<li><a href="users.php">👥 Gestione Utenti</a></li>
|
<li><a href="users.php">👥 Gestione Utenti</a></li>
|
||||||
<li><a href="purchases.php" class="active">💰 Acquisti</a></li>
|
<li><a href="purchases.php" class="active">💰 Acquisti</a></li>
|
||||||
|
<li><a href="../convert_videos.php">🔄 Converti Video</a></li>
|
||||||
<li><a href="profile.php">👤 Profilo</a></li>
|
<li><a href="profile.php">👤 Profilo</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</aside>
|
</aside>
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ $users = $stmt->fetchAll();
|
|||||||
<li><a href="lessons.php">🎥 Gestione Lezioni</a></li>
|
<li><a href="lessons.php">🎥 Gestione Lezioni</a></li>
|
||||||
<li><a href="users.php" class="active">👥 Gestione Utenti</a></li>
|
<li><a href="users.php" class="active">👥 Gestione Utenti</a></li>
|
||||||
<li><a href="purchases.php">💰 Acquisti</a></li>
|
<li><a href="purchases.php">💰 Acquisti</a></li>
|
||||||
|
<li><a href="../convert_videos.php">🔄 Converti Video</a></li>
|
||||||
<li><a href="profile.php">👤 Profilo</a></li>
|
<li><a href="profile.php">👤 Profilo</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</aside>
|
</aside>
|
||||||
|
|||||||
270
convert_videos.php
Normal file
270
convert_videos.php
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Converti Video in Formato Web-Compatible
|
||||||
|
*
|
||||||
|
* Converte automaticamente i video caricati in formato MP4 con codec H.264 e AAC
|
||||||
|
* per garantire la massima compatibilità con tutti i browser
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once 'includes/config.php';
|
||||||
|
require_once 'includes/functions.php';
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
check_session_timeout();
|
||||||
|
require_admin();
|
||||||
|
|
||||||
|
$message = '';
|
||||||
|
$error = '';
|
||||||
|
$videos = [];
|
||||||
|
|
||||||
|
// Trova tutti i video che potrebbero aver bisogno di conversione
|
||||||
|
$pdo = get_db_connection();
|
||||||
|
$stmt = $pdo->query("
|
||||||
|
SELECT id, title, video_url, video_platform
|
||||||
|
FROM lessons
|
||||||
|
WHERE type = 'video'
|
||||||
|
AND video_platform = 'local'
|
||||||
|
AND video_url IS NOT NULL
|
||||||
|
AND deleted_at IS NULL
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
");
|
||||||
|
$videos = $stmt->fetchAll();
|
||||||
|
|
||||||
|
// Verifica se FFmpeg è disponibile
|
||||||
|
function check_ffmpeg() {
|
||||||
|
$output = [];
|
||||||
|
$return_var = 0;
|
||||||
|
exec('ffmpeg -version 2>&1', $output, $return_var);
|
||||||
|
return $return_var === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ffmpeg_available = check_ffmpeg();
|
||||||
|
|
||||||
|
// Funzione per convertire un video
|
||||||
|
function convert_video($input_path, $output_path) {
|
||||||
|
$command = sprintf(
|
||||||
|
'ffmpeg -i %s -c:v libx264 -preset medium -crf 23 -c:a aac -b:a 128k -movflags +faststart -y %s 2>&1',
|
||||||
|
escapeshellarg($input_path),
|
||||||
|
escapeshellarg($output_path)
|
||||||
|
);
|
||||||
|
|
||||||
|
$output = [];
|
||||||
|
$return_var = 0;
|
||||||
|
exec($command, $output, $return_var);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'success' => $return_var === 0,
|
||||||
|
'output' => implode("\n", $output)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processa conversione se richiesta
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['convert_video'])) {
|
||||||
|
$lesson_id = intval($_POST['lesson_id'] ?? 0);
|
||||||
|
|
||||||
|
if ($lesson_id > 0) {
|
||||||
|
$stmt = $pdo->prepare("SELECT video_url FROM lessons WHERE id = ?");
|
||||||
|
$stmt->execute([$lesson_id]);
|
||||||
|
$lesson = $stmt->fetch();
|
||||||
|
|
||||||
|
if ($lesson && $lesson['video_url']) {
|
||||||
|
$video_path = ltrim($lesson['video_url'], '/');
|
||||||
|
$input_file = __DIR__ . '/' . $video_path;
|
||||||
|
|
||||||
|
if (file_exists($input_file)) {
|
||||||
|
// Crea nome file di output
|
||||||
|
$path_info = pathinfo($input_file);
|
||||||
|
$output_file = $path_info['dirname'] . '/' . $path_info['filename'] . '_converted.mp4';
|
||||||
|
$backup_file = $input_file . '.backup';
|
||||||
|
|
||||||
|
// Backup del file originale
|
||||||
|
copy($input_file, $backup_file);
|
||||||
|
|
||||||
|
$result = convert_video($input_file, $output_file);
|
||||||
|
|
||||||
|
if ($result['success'] && file_exists($output_file)) {
|
||||||
|
// Sostituisci il file originale con quello convertito
|
||||||
|
unlink($input_file);
|
||||||
|
rename($output_file, $input_file);
|
||||||
|
|
||||||
|
// Aggiorna il database se l'estensione è cambiata
|
||||||
|
$new_extension = 'mp4';
|
||||||
|
$old_extension = strtolower($path_info['extension']);
|
||||||
|
|
||||||
|
if ($old_extension !== $new_extension) {
|
||||||
|
$new_video_url = str_replace('.' . $old_extension, '.' . $new_extension, $lesson['video_url']);
|
||||||
|
$new_file_path = str_replace('.' . $old_extension, '.' . $new_extension, $input_file);
|
||||||
|
rename($input_file, $new_file_path);
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("UPDATE lessons SET video_url = ? WHERE id = ?");
|
||||||
|
$stmt->execute([$new_video_url, $lesson_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$message = "✅ Video convertito con successo! Il file originale è stato salvato come backup.";
|
||||||
|
|
||||||
|
// Ricarica la lista
|
||||||
|
$stmt = $pdo->query("
|
||||||
|
SELECT id, title, video_url, video_platform
|
||||||
|
FROM lessons
|
||||||
|
WHERE type = 'video'
|
||||||
|
AND video_platform = 'local'
|
||||||
|
AND video_url IS NOT NULL
|
||||||
|
AND deleted_at IS NULL
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
");
|
||||||
|
$videos = $stmt->fetchAll();
|
||||||
|
} else {
|
||||||
|
$error = "❌ Errore durante la conversione: " . htmlspecialchars($result['output']);
|
||||||
|
// Ripristina il backup
|
||||||
|
if (file_exists($backup_file)) {
|
||||||
|
copy($backup_file, $input_file);
|
||||||
|
unlink($backup_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$error = "❌ File video non trovato: " . htmlspecialchars($video_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Converti Video - Pilates Platform</title>
|
||||||
|
<link rel="stylesheet" href="assets/css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="header">
|
||||||
|
<div class="container">
|
||||||
|
<div class="header-content">
|
||||||
|
<h1 class="logo">Pilates Studio - Conversione Video</h1>
|
||||||
|
<nav class="nav">
|
||||||
|
<a href="admin/dashboard.php" class="btn btn-outline">Torna alla Dashboard</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container" style="padding: 2rem 0;">
|
||||||
|
<div class="card">
|
||||||
|
<h2>🎬 Conversione Video in Formato Web-Compatible</h2>
|
||||||
|
|
||||||
|
<?php if ($message): ?>
|
||||||
|
<div class="alert alert-success"><?php echo $message; ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ($error): ?>
|
||||||
|
<div class="alert alert-error"><?php echo $error; ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="alert alert-info">
|
||||||
|
<h3>ℹ️ Informazioni</h3>
|
||||||
|
<p>Questo strumento converte i video in formato MP4 con codec H.264 e AAC per garantire la massima compatibilità con tutti i browser.</p>
|
||||||
|
<p><strong>Parametri di conversione:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>Codec Video: H.264 (libx264)</li>
|
||||||
|
<li>Codec Audio: AAC</li>
|
||||||
|
<li>Qualità: CRF 23 (bilanciamento qualità/dimensione)</li>
|
||||||
|
<li>Ottimizzazione: Fast start per streaming web</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (!$ffmpeg_available): ?>
|
||||||
|
<div class="alert alert-error">
|
||||||
|
<h3>⚠️ FFmpeg Non Disponibile</h3>
|
||||||
|
<p>FFmpeg non è installato sul server. Per utilizzare questa funzionalità, installa FFmpeg:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Ubuntu/Debian:</strong> <code>sudo apt-get install ffmpeg</code></li>
|
||||||
|
<li><strong>CentOS/RHEL:</strong> <code>sudo yum install ffmpeg</code></li>
|
||||||
|
<li><strong>Windows:</strong> Scarica da <a href="https://ffmpeg.org/download.html" target="_blank">ffmpeg.org</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="alert alert-success">
|
||||||
|
✅ FFmpeg è installato e disponibile
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<h3>Video Disponibili per Conversione</h3>
|
||||||
|
|
||||||
|
<?php if (empty($videos)): ?>
|
||||||
|
<p>Nessun video locale trovato.</p>
|
||||||
|
<?php else: ?>
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Titolo Lezione</th>
|
||||||
|
<th>Percorso Video</th>
|
||||||
|
<th>Formato</th>
|
||||||
|
<th>Dimensione</th>
|
||||||
|
<th>Azione</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($videos as $video): ?>
|
||||||
|
<?php
|
||||||
|
$video_path = ltrim($video['video_url'], '/');
|
||||||
|
$full_path = __DIR__ . '/' . $video_path;
|
||||||
|
$exists = file_exists($full_path);
|
||||||
|
$extension = strtolower(pathinfo($video['video_url'], PATHINFO_EXTENSION));
|
||||||
|
$file_size = $exists ? filesize($full_path) : 0;
|
||||||
|
$file_size_mb = round($file_size / (1024 * 1024), 2);
|
||||||
|
?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo $video['id']; ?></td>
|
||||||
|
<td><?php echo htmlspecialchars($video['title']); ?></td>
|
||||||
|
<td style="font-size: 0.85em; color: #666;">
|
||||||
|
<?php echo htmlspecialchars($video['video_url']); ?>
|
||||||
|
<?php if (!$exists): ?>
|
||||||
|
<br><span style="color: red;">⚠️ File non trovato</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="badge" style="background: <?php echo $extension === 'mp4' ? '#2ecc71' : '#e74c3c'; ?>;">
|
||||||
|
<?php echo strtoupper($extension); ?>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td><?php echo $file_size_mb; ?> MB</td>
|
||||||
|
<td>
|
||||||
|
<?php if ($exists && $ffmpeg_available): ?>
|
||||||
|
<form method="POST" style="display: inline;"
|
||||||
|
onsubmit="return confirm('Vuoi convertire questo video? Il processo potrebbe richiedere alcuni minuti.');">
|
||||||
|
<input type="hidden" name="lesson_id" value="<?php echo $video['id']; ?>">
|
||||||
|
<button type="submit" name="convert_video" class="btn btn-primary btn-sm">
|
||||||
|
🔄 Converti
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<?php else: ?>
|
||||||
|
<span style="color: #999;">Non disponibile</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div style="margin-top: 2rem; padding: 1rem; background: #f5f5f5; border-radius: 8px;">
|
||||||
|
<h4>💡 Suggerimenti</h4>
|
||||||
|
<ul>
|
||||||
|
<li>La conversione può richiedere diversi minuti per video di grandi dimensioni</li>
|
||||||
|
<li>Il file originale viene salvato come backup (.backup)</li>
|
||||||
|
<li>I video MP4 esistenti possono comunque essere riconvertiti per ottimizzazione</li>
|
||||||
|
<li>Per migliori risultati, carica video già in formato MP4 H.264</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="footer">
|
||||||
|
<div class="container">
|
||||||
|
<p>© <?php echo date('Y'); ?> Pilates Platform. Tutti i diritti riservati.</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
14
lesson.php
14
lesson.php
@@ -138,6 +138,16 @@ $can_view = $lesson['is_demo'] || $user_owns;
|
|||||||
<?php
|
<?php
|
||||||
// Usa lo stream protetto per i video locali
|
// Usa lo stream protetto per i video locali
|
||||||
$stream_url = SITE_URL . '/stream_video.php?lesson_id=' . $lesson_id;
|
$stream_url = SITE_URL . '/stream_video.php?lesson_id=' . $lesson_id;
|
||||||
|
|
||||||
|
// Determina il tipo MIME corretto dal file
|
||||||
|
$extension = strtolower(pathinfo($lesson['video_url'], PATHINFO_EXTENSION));
|
||||||
|
$mime_types = [
|
||||||
|
'mp4' => 'video/mp4',
|
||||||
|
'webm' => 'video/webm',
|
||||||
|
'ogg' => 'video/ogg',
|
||||||
|
'mov' => 'video/mp4' // MOV spesso funziona con il codec mp4
|
||||||
|
];
|
||||||
|
$video_mime = $mime_types[$extension] ?? 'video/mp4';
|
||||||
?>
|
?>
|
||||||
<video
|
<video
|
||||||
id="videoPlayer"
|
id="videoPlayer"
|
||||||
@@ -146,10 +156,10 @@ $can_view = $lesson['is_demo'] || $user_owns;
|
|||||||
width="100%"
|
width="100%"
|
||||||
height="100%"
|
height="100%"
|
||||||
style="border-radius: 8px; background: #000;"
|
style="border-radius: 8px; background: #000;"
|
||||||
preload="auto"
|
preload="metadata"
|
||||||
playsinline
|
playsinline
|
||||||
oncontextmenu="return false;">
|
oncontextmenu="return false;">
|
||||||
<source src="<?php echo htmlspecialchars($stream_url); ?>" type="video/mp4">
|
<source src="<?php echo htmlspecialchars($stream_url); ?>" type="<?php echo $video_mime; ?>">
|
||||||
<p style="color: white; padding: 20px;">
|
<p style="color: white; padding: 20px;">
|
||||||
Il tuo browser non supporta la riproduzione video.<br>
|
Il tuo browser non supporta la riproduzione video.<br>
|
||||||
Prova ad aggiornare il browser o usa Chrome/Firefox.
|
Prova ad aggiornare il browser o usa Chrome/Firefox.
|
||||||
|
|||||||
@@ -86,18 +86,25 @@ if (isset($_SERVER['HTTP_RANGE'])) {
|
|||||||
}
|
}
|
||||||
$length = $end - $start + 1;
|
$length = $end - $start + 1;
|
||||||
http_response_code(206); // Partial Content
|
http_response_code(206); // Partial Content
|
||||||
|
header("Content-Range: bytes $start-$end/$file_size");
|
||||||
|
} else {
|
||||||
|
http_response_code(200);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
http_response_code(200);
|
http_response_code(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pulisci output buffer per evitare corruzione
|
||||||
|
if (ob_get_level()) {
|
||||||
|
ob_end_clean();
|
||||||
|
}
|
||||||
|
|
||||||
// Invia header
|
// Invia header
|
||||||
header("Content-Type: $mime_type");
|
header("Content-Type: $mime_type");
|
||||||
header("Accept-Ranges: bytes");
|
header("Accept-Ranges: bytes");
|
||||||
header("Content-Length: $length");
|
header("Content-Length: $length");
|
||||||
header("Content-Range: bytes $start-$end/$file_size");
|
|
||||||
|
|
||||||
// Header per impedire il download e la cache aggressiva
|
// Header per impedire il download
|
||||||
header("Content-Disposition: inline; filename=\"video.{$extension}\"");
|
header("Content-Disposition: inline; filename=\"video.{$extension}\"");
|
||||||
header("X-Content-Type-Options: nosniff");
|
header("X-Content-Type-Options: nosniff");
|
||||||
|
|
||||||
@@ -105,16 +112,37 @@ header("X-Content-Type-Options: nosniff");
|
|||||||
header("Cache-Control: public, max-age=3600");
|
header("Cache-Control: public, max-age=3600");
|
||||||
header("Pragma: public");
|
header("Pragma: public");
|
||||||
|
|
||||||
|
// Disabilita compressione per lo streaming
|
||||||
|
header("Content-Encoding: none");
|
||||||
|
|
||||||
// Apri e leggi il file
|
// Apri e leggi il file
|
||||||
$fp = fopen($file, 'rb');
|
$fp = fopen($file, 'rb');
|
||||||
|
if ($fp === false) {
|
||||||
|
http_response_code(500);
|
||||||
|
exit('Impossibile aprire il file video');
|
||||||
|
}
|
||||||
|
|
||||||
fseek($fp, $start);
|
fseek($fp, $start);
|
||||||
|
|
||||||
$buffer = 1024 * 8; // 8KB buffer
|
// Buffer più grande per migliore performance
|
||||||
while (!feof($fp) && ($pos = ftell($fp)) <= $end) {
|
$buffer = 1024 * 256; // 256KB buffer
|
||||||
if ($pos + $buffer > $end) {
|
$bytes_sent = 0;
|
||||||
$buffer = $end - $pos + 1;
|
|
||||||
|
while (!feof($fp) && ($bytes_sent < $length) && connection_status() == 0) {
|
||||||
|
$bytes_to_read = min($buffer, $length - $bytes_sent);
|
||||||
|
$data = fread($fp, $bytes_to_read);
|
||||||
|
|
||||||
|
if ($data === false) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo $data;
|
||||||
|
$bytes_sent += strlen($data);
|
||||||
|
|
||||||
|
// Forza l'invio dei dati al browser
|
||||||
|
if (ob_get_level() > 0) {
|
||||||
|
ob_flush();
|
||||||
}
|
}
|
||||||
echo fread($fp, $buffer);
|
|
||||||
flush();
|
flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user