uploaded_url)) { return $media->uploaded_url; } // Fallback to storage disk URL (file-based media) return Storage::disk($media->disk)->url($media->file_path.$media->file_name); } public static function loadMediaToLocalTemp($uuid) { $media = self::getMediaByUuid($uuid); $result = [ 'media' => $media, 'uuid' => $uuid, 'temp_path' => null, 'temp' => null, ]; if (! $media) { return $result; } $tempPath = 'temp_'.$media->file_name; // Handle URL-based media if (! empty($media->uploaded_url)) { if (! Storage::disk(self::LOCAL_TEMP_DISK)->exists($tempPath)) { $fileContent = @file_get_contents($media->uploaded_url); if ($fileContent !== false) { if (Storage::disk(self::LOCAL_TEMP_DISK)->put($tempPath, $fileContent)) { $result['temp_path'] = $tempPath; $result['temp'] = Storage::disk(self::LOCAL_TEMP_DISK)->path($tempPath); } } } else { $result['temp_path'] = $tempPath; $result['temp'] = Storage::disk(self::LOCAL_TEMP_DISK)->path($tempPath); } return $result; } // Handle file-based media (existing behavior) if (! Storage::disk($media->disk)->exists($media->file_path.$media->file_name)) { return $result; } $fileContent = Storage::disk($media->disk)->get($media->file_path.$media->file_name); if (! Storage::disk(self::LOCAL_TEMP_DISK)->exists($tempPath)) { if (Storage::disk(self::LOCAL_TEMP_DISK)->put($tempPath, $fileContent)) { $result['temp_path'] = $tempPath; $result['temp'] = Storage::disk(self::LOCAL_TEMP_DISK)->path($tempPath); } } else { $result['temp_path'] = $tempPath; $result['temp'] = Storage::disk(self::LOCAL_TEMP_DISK)->path($tempPath); } return $result; } public static function loadMediasToLocalTemp(array $uuids) { $medias = self::getMediaByUuids($uuids); $result = []; foreach ($medias as $media) { if (! $media) { continue; // Skip invalid media instead of returning early } $tempPath = 'temp_'.$media->file_name; $singleResult = [ 'media' => $media, 'uuid' => $media->uuid, 'temp_path' => null, 'temp' => null, 'cloud_url' => self::getMediaCloudUrl($media), ]; // Handle URL-based media if (! empty($media->uploaded_url)) { if (Storage::disk(self::LOCAL_TEMP_DISK)->exists($tempPath)) { // File already exists in temp, just set the paths $singleResult['temp_path'] = $tempPath; $singleResult['temp'] = Storage::disk(self::LOCAL_TEMP_DISK)->path($tempPath); } else { // File needs to be downloaded to temp $fileContent = @file_get_contents($media->uploaded_url); if ($fileContent !== false) { if (Storage::disk(self::LOCAL_TEMP_DISK)->put($tempPath, $fileContent)) { $singleResult['temp_path'] = $tempPath; $singleResult['temp'] = Storage::disk(self::LOCAL_TEMP_DISK)->path($tempPath); } } } $result[] = $singleResult; continue; } // Handle file-based media (existing behavior) // Check if file exists in cloud storage if (! Storage::disk($media->disk)->exists($media->file_path.$media->file_name)) { $result[] = $singleResult; // Add to results but with null temp paths continue; } // Check if already loaded in local temp if (Storage::disk(self::LOCAL_TEMP_DISK)->exists($tempPath)) { // File already exists in temp, just set the paths $singleResult['temp_path'] = $tempPath; $singleResult['temp'] = Storage::disk(self::LOCAL_TEMP_DISK)->path($tempPath); } else { // File needs to be loaded to temp $fileContent = Storage::disk($media->disk)->get($media->file_path.$media->file_name); if (Storage::disk(self::LOCAL_TEMP_DISK)->put($tempPath, $fileContent)) { $singleResult['temp_path'] = $tempPath; $singleResult['temp'] = Storage::disk(self::LOCAL_TEMP_DISK)->path($tempPath); } } $result[] = $singleResult; } return $result; } public static function deleteMediaFromLocalTemp($uuid) { $result = self::loadMediaToLocalTemp($uuid); if ($result['temp_path']) { Storage::disk(self::LOCAL_TEMP_DISK)->delete($result['temp_path']); } return [ 'media' => $result['media'], 'uuid' => $uuid, 'temp_path' => null, 'temp' => null, ]; } public static function deleteMediasFromLocalTemp(array $uuids) { $results = self::loadMediasToLocalTemp($uuids); $deletedResults = []; foreach ($results as $result) { if ($result['temp_path']) { Storage::disk(self::LOCAL_TEMP_DISK)->delete($result['temp_path']); } $deletedResults[] = [ 'media' => $result['media'], 'uuid' => $result['uuid'], 'temp_path' => null, 'temp' => null, ]; } return $deletedResults; } /** * Add a new media file to the specified media collection. * * Example: $newMedia = MediaEngine::addMedia( * 'bgm_collection', * 'bgm', * 'user_upload', * 'web_app', * $fileContent, // File content or null * $url, // URL or null * 'save_file', // Mode: 'save_file'|'download'|'save_url' * 'background_music.mp3', // Optional - auto-generated if null * 'r2', * 'name of file', * $user_id, * $mimeType // Optional MIME type * ); */ public static function addMedia( string $mediaCollectionKey, string $mediaType, string $mediaSource, string $mediaProvider, ?string $fileContent = null, ?string $url = null, ?string $mode = 'save_file', ?string $fileName = null, string $disk = 'r2', ?string $name = null, ?int $userId = null, ?string $mimeType = null ) { $mediaCollection = MediaCollection::where('key', $mediaCollectionKey)->first(); if (! $mediaCollection) { throw new \InvalidArgumentException("Media collection with key '{$mediaCollectionKey}' not found."); } $config = config("platform.media.{$mediaCollectionKey}"); // Smart mode detection for backward compatibility if ($mode === 'save_file') { // If mode is default but we have URL and no fileContent, switch to save_url for backward compatibility if (! empty($url) && empty($fileContent)) { $mode = 'save_url'; } } // Validate mode parameter $validModes = ['save_file', 'download', 'save_url']; if (! in_array($mode, $validModes)) { throw new \InvalidArgumentException("Invalid mode '{$mode}'. Valid modes are: ".implode(', ', $validModes)); } // Validate required parameters based on mode if ($mode === 'save_file' && empty($fileContent)) { throw new \InvalidArgumentException("fileContent is required when mode is 'save_file'."); } if (($mode === 'download' || $mode === 'save_url') && empty($url)) { throw new \InvalidArgumentException("url is required when mode is '{$mode}'."); } // Auto-generate fileName if not provided if (empty($fileName)) { if (! empty($url)) { // Extract filename from URL $pathInfo = pathinfo(parse_url($url, PHP_URL_PATH)); $fileName = $pathInfo['basename'] ?? null; // If no filename in URL, generate one if (empty($fileName) || $fileName === '/') { $extension = ! empty($config['extension']) ? $config['extension'] : 'file'; $fileName = $mediaType.'_'.epoch_now_timestamp().'.'.$extension; } } else { // Generate filename for file-based media $extension = ! empty($config['extension']) ? $config['extension'] : 'file'; $fileName = $mediaType.'_'.epoch_now_timestamp().'.'.$extension; } } // Handle different modes if ($mode === 'save_url') { // URL-based media handling - just save URL reference $adjustedFileName = $config['prefix'].epoch_now_timestamp().'-'.$fileName; // Auto-detect MIME type from URL if not provided if (empty($mimeType)) { $fileDetails = self::getFileDetailsbyUrl($url); $mimeType = $fileDetails->mimetype; // Fallback to config mime if detection fails if (empty($mimeType) || $mimeType === 'application/octet-stream') { $mimeType = $config['mime']; } } $media = new Media([ 'uuid' => Str::uuid(), 'media_collection_id' => $mediaCollection->id, 'user_id' => $userId, 'media_type' => $mediaType, 'media_source' => $mediaSource, 'media_provider' => $mediaProvider, 'mime_type' => $mimeType, 'file_name' => $adjustedFileName, 'file_path' => $config['location'], // Keep for consistency 'disk' => $disk, 'name' => $name, 'uploaded_url' => $url, ]); $media->save(); return $media; } // For 'save_file' and 'download' modes, we need to store file content if ($mode === 'download') { // Download content from URL $fileContent = @file_get_contents($url); if ($fileContent === false) { throw new \RuntimeException("Failed to download file from URL: {$url}"); } } // File-based media handling (for both 'save_file' and 'download' modes) if (empty($fileContent)) { throw new \InvalidArgumentException('fileContent is required for file storage.'); } // Adjust fileName with prefix, postfix, and ensure extension $adjustedFileName = $config['prefix'].epoch_now_timestamp().'-'.$fileName; // Construct file path $filePath = $config['location']; // Store the file $stored = Storage::disk($disk)->put($filePath.$adjustedFileName, $fileContent); if (! $stored) { throw new \RuntimeException("Failed to store file: {$filePath}"); } // Get filetype $detectedMimeType = null; $tempFile = tempnam(sys_get_temp_dir(), 'mime_'); try { file_put_contents($tempFile, $fileContent); $finfo = finfo_open(FILEINFO_MIME_TYPE); $detectedMimeType = finfo_file($finfo, $tempFile); finfo_close($finfo); } finally { // This ensures the file is deleted even if an exception occurs if (file_exists($tempFile)) { unlink($tempFile); } } if (is_empty($detectedMimeType)) { $detectedMimeType = $config['mime']; } $media = new Media([ 'uuid' => Str::uuid(), 'media_collection_id' => $mediaCollection->id, 'user_id' => $userId, 'media_type' => $mediaType, 'media_source' => $mediaSource, 'media_provider' => $mediaProvider, 'mime_type' => $detectedMimeType, 'file_name' => $adjustedFileName, 'file_path' => $filePath, 'disk' => $disk, 'name' => $name, 'uploaded_url' => null, ]); $media->save(); return $media; } private static function ensureUniqueFileName($fileName, $disk, $location) { $uniqueFileName = $fileName; $counter = 1; while (Storage::disk($disk)->exists($location.$uniqueFileName)) { $info = pathinfo($fileName); $uniqueFileName = $info['filename'].'-'.$counter.'.'.$info['extension']; $counter++; } return $uniqueFileName; } public static function getMediaByUuid($uuid) { return Media::where('uuid', $uuid)->first(); } public static function getMediaByUuids(array $uuids) { return Media::whereIn('uuid', $uuids)->get(); } public static function getMediasByCollectionKey($key) { $collection = MediaCollection::where('key', $key)->first(); if (! $collection) { return collect(); } return $collection->media; } public static function getCollectionKeyByOwnerMediaType($owner_type, $media_type) { $mediaConfig = config('platform.media'); foreach ($mediaConfig as $key => $item) { if ($item['owner_type'] == $owner_type && $item['media_type'] == $media_type) { return $key; } } } /** * Get file details from a URL including filename, extension and MIME type * * @param string $url The URL of the file * @return object Object containing filename, extension and MIME type */ public static function getFileDetailsbyUrl($url) { // Create an empty result object $result = new \stdClass; // Parse the URL to extract the filename $pathInfo = pathinfo(parse_url($url, PHP_URL_PATH)); // Set the filename and extension $result->filename = $pathInfo['filename'] ?? ''; $result->extension = $pathInfo['extension'] ?? ''; // Initialize the MIME type as unknown $result->mimetype = 'application/octet-stream'; // Try to get the real MIME type using fileinfo try { // Create a temporary file $tempFile = tempnam(sys_get_temp_dir(), 'file_'); // Get the file content from URL $fileContent = @file_get_contents($url); if ($fileContent !== false) { // Write the content to the temporary file file_put_contents($tempFile, $fileContent); // Create a finfo object $finfo = new \finfo(FILEINFO_MIME_TYPE); // Get the MIME type $result->mimetype = $finfo->file($tempFile); // Clean up the temporary file @unlink($tempFile); } } catch (\Exception $e) { // If there's an error, try to determine MIME type from extension $commonMimeTypes = [ // Images 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'png' => 'image/png', 'gif' => 'image/gif', 'webp' => 'image/webp', 'svg' => 'image/svg+xml', // Audio 'mp3' => 'audio/mpeg', 'wav' => 'audio/wav', 'ogg' => 'audio/ogg', 'm4a' => 'audio/mp4', 'flac' => 'audio/flac', // Video 'mp4' => 'video/mp4', 'webm' => 'video/webm', 'avi' => 'video/x-msvideo', 'mov' => 'video/quicktime', 'mkv' => 'video/x-matroska', ]; if (! empty($result->extension) && isset($commonMimeTypes[strtolower($result->extension)])) { $result->mimetype = $commonMimeTypes[strtolower($result->extension)]; } } return $result; } }