: /** * Handle video download with progress */ add_action('wp_ajax_fsvp_download_video', 'fsvp_handle_download_video'); function fsvp_handle_download_video() { // Verifica nonce if (!wp_verify_nonce($_POST['nonce'], 'fsvp_admin_nonce')) { wp_die('Security check failed'); } // Verifica permessi if (!current_user_can('manage_options')) { wp_die('Insufficient permissions'); } $video_id = intval($_POST['video_id']); // Get video info global $wpdb; $video = $wpdb->get_row($wpdb->prepare( "SELECT * FROM {$wpdb->prefix}fsvp_videos WHERE video_id = %d", $video_id )); if (!$video) { wp_die('Video not found'); } $file_path = FSVP_PROTECTED_DIR . $video->filename; if (!file_exists($file_path)) { wp_die('File not found'); } // Chunked download fsvp_stream_file_download($file_path, $video->description . '.mp4'); } /** * Stream file for download with progress support */ function fsvp_stream_file_download($file_path, $download_name) { $file_size = filesize($file_path); $chunk_size = 8192; // 8KB chunks // Headers per download header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . $download_name . '"'); header('Content-Length: ' . $file_size); header('Accept-Ranges: bytes'); // Range support per progress bar $range_start = 0; $range_end = $file_size - 1; if (isset($_SERVER['HTTP_RANGE'])) { $ranges = explode('=', $_SERVER['HTTP_RANGE']); $range = explode('-', $ranges[1]); $range_start = intval($range[0]); if (!empty($range[1])) { $range_end = intval($range[1]); } header('HTTP/1.1 206 Partial Content'); header('Content-Range: bytes ' . $range_start . '-' . $range_end . '/' . $file_size); header('Content-Length: ' . ($range_end - $range_start + 1)); } // Stream file $handle = fopen($file_path, 'rb'); fseek($handle, $range_start); $bytes_left = $range_end - $range_start + 1; while ($bytes_left > 0 && !feof($handle)) { $bytes_to_read = min($chunk_size, $bytes_left); echo fread($handle, $bytes_to_read); flush(); $bytes_left -= $bytes_to_read; } fclose($handle); exit; } ?>