directory_manager = $directory_manager ?? new Audio_Directory_Manager(); } /** * Save audio content to a uniquely named file based on post ID. * * @param string $audio_content Binary audio content. * @param int $post_id Post ID for filename generation. * @return array Result with success status, URL, path, filename and size. */ public function save_audio_file( string $audio_content, int $post_id ): array { try { accessibility_audio_tts_debug_log( '[AudioFileWriter] Starting file save process for post ID: ' . $post_id ); $filename = $this->generate_final_filename_for_post( $post_id ); if ( class_exists( '\\Accessibility_Audio_TTS\\Core\\Audio_Access' ) ) { $relative_directory = gmdate( 'Y/m' ); \Accessibility_Audio_TTS\Core\Audio_Access::assert_storage_ready( $relative_directory ); $stage = \Accessibility_Audio_TTS\Core\Audio_Access::working_directory( get_post( $post_id ) ) . '/' . $filename . '.part'; try { $result = $this->write_file_to_disk( $stage, $audio_content ); if ( ! $result['success'] ) { return $result; } return \Accessibility_Audio_TTS\Core\Audio_Access::publish_file( $post_id, $stage, $relative_directory . '/' . $filename, 99999 === $post_id && ! get_post( $post_id ) ); } finally { if ( is_file( $stage ) ) { wp_delete_file( $stage ); } } } $upload_path = $this->directory_manager->get_upload_path(); if ( ! $this->directory_manager->ensure_directory_exists() ) { return array( 'success' => false, 'error' => 'Cannot create directory: ' . $upload_path ); } $filepath = $upload_path . '/' . $filename; $write_result = $this->write_file_to_disk( $filepath, $audio_content ); if ( ! $write_result['success'] ) { return $write_result; // Return error details from write_file_to_disk. } $this->set_file_permissions( $filepath ); $file_url = $this->directory_manager->get_upload_url() . '/' . $filename; accessibility_audio_tts_debug_log( '[AudioFileWriter] File saved successfully: ' . $filepath ); return array( 'success' => true, 'url' => $file_url, 'path' => $filepath, 'filename' => $filename, // Added filename to the return array. 'size' => $write_result['bytes_written'], ); } catch ( \Exception $e ) { accessibility_audio_tts_debug_log( '[AudioFileWriter] Exception in save_audio_file: ' . $e->getMessage() ); return array( 'success' => false, 'error' => $e->getMessage(), ); } } /** * Saves raw audio content to a specified full file path. * * @param string $audio_content Binary audio content. * @param string $full_file_path The absolute path where the file should be saved. * @return bool True on success, false on failure. */ public function save_raw_audio_content( string $audio_content, string $full_file_path ): bool { try { accessibility_audio_tts_debug_log( '[AudioFileWriter] Attempting to save raw audio to: ' . $full_file_path ); $directory_path = dirname( $full_file_path ); // The supplied destination can be private; public uploads are unrelated. if ( ( ! is_dir( $directory_path ) && ! wp_mkdir_p( $directory_path ) ) || ! wp_is_writable( $directory_path ) ) { accessibility_audio_tts_debug_log( '[AudioFileWriter] Failed to ensure directory exists: ' . $directory_path ); return false; } $write_result = $this->write_file_to_disk( $full_file_path, $audio_content ); if ( $write_result['success'] ) { $this->set_file_permissions( $full_file_path ); accessibility_audio_tts_debug_log( '[AudioFileWriter] Raw audio content saved successfully to: ' . $full_file_path ); return true; } // Error message is already logged by write_file_to_disk. return false; } catch ( \Exception $e ) { accessibility_audio_tts_debug_log( '[AudioFileWriter] Exception in save_raw_audio_content: ' . $e->getMessage() ); return false; } } /** * Generate unique final filename for an audio file associated with a post. * * @param int $post_id Post ID. * @param string $extension File extension (default: 'mp3'). * @return string The generated filename (e.g., accessibility_audio_tts_123_abcdef123456.mp3). */ public function generate_final_filename_for_post( int $post_id, string $extension = 'mp3' ): string { // Sanitize extension just in case. $extension = preg_replace( '/[^a-zA-Z0-9]/', '', $extension ); return 'accessibility_audio_tts_' . $post_id . '_' . uniqid( '', true ) . '.' . $extension; } /** * Write file content to disk. * * @param string $filepath Absolute file path. * @param string $audio_content Binary audio content. * @return array { success: bool, bytes_written?: int, error?: string }. */ private function write_file_to_disk( $filepath, $audio_content ) { accessibility_audio_tts_debug_log( '[AudioFileWriter] Writing audio file to: ' . $filepath ); global $wp_filesystem; if ( ! $wp_filesystem ) { require_once ABSPATH . 'wp-admin/includes/file.php'; WP_Filesystem(); } if ( $wp_filesystem ) { $bytes_written = $wp_filesystem->put_contents( $filepath, $audio_content ); if ( ! $bytes_written ) { accessibility_audio_tts_debug_log( '[AudioFileWriter] Failed to write file to: ' . $filepath . ' - Check permissions and disk space.' ); return array( 'success' => false, 'error' => 'Cannot save audio file. Check server permissions and available disk space.', ); } } else { // Fallback when WP_Filesystem is unavailable (local filesystem only). $bytes_written = file_put_contents( $filepath, $audio_content ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents if ( false === $bytes_written ) { accessibility_audio_tts_debug_log( '[AudioFileWriter] Failed to write file to: ' . $filepath . ' - Check permissions and disk space.' ); return array( 'success' => false, 'error' => 'Cannot save audio file. Check server permissions and available disk space.', ); } } accessibility_audio_tts_debug_log( '[AudioFileWriter] Successfully wrote ' . $bytes_written . ' bytes to file: ' . $filepath ); return array( 'success' => true, 'bytes_written' => (int) $bytes_written, ); } /** * Set appropriate file permissions. * * @param string $filepath Absolute file path. * @return void */ private function set_file_permissions( $filepath ) { File_Validation_Helper::set_file_permissions( $filepath, 0644 ); } }