Files
2025-12-10 21:59:12 -05:00

155 lines
5.1 KiB
C#

namespace MarketAlly.Replicate.Maui.Services
{
/// <summary>
/// Default implementation of IFileSaveService.
/// </summary>
public class FileSaveService : IFileSaveService
{
private readonly HttpClient _httpClient;
public FileSaveService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<FileSaveResult> SaveFromUrlAsync(
string url,
TransformationType type,
string? savePath,
string filenamePattern,
string? predictionId = null,
CancellationToken cancellationToken = default)
{
try
{
// Determine save path
var basePath = savePath ?? FileSystem.CacheDirectory;
// Ensure directory exists
if (!Directory.Exists(basePath))
{
Directory.CreateDirectory(basePath);
}
// Download the file
var response = await _httpClient.GetAsync(url, cancellationToken);
response.EnsureSuccessStatusCode();
// Determine file extension from content type or URL
var extension = GetFileExtension(response.Content.Headers.ContentType?.MediaType, url, type);
// Generate filename from pattern
var filename = GenerateFilename(filenamePattern, predictionId, extension);
// Full path
var filePath = Path.Combine(basePath, filename);
// Save the file
var bytes = await response.Content.ReadAsByteArrayAsync(cancellationToken);
await File.WriteAllBytesAsync(filePath, bytes, cancellationToken);
return FileSaveResult.Succeeded(filePath, bytes.Length);
}
catch (Exception ex)
{
return FileSaveResult.Failed(ex);
}
}
public async Task<FileSaveResult> SaveBytesAsync(
byte[] bytes,
TransformationType type,
string? savePath,
string filenamePattern,
string extension,
string? predictionId = null)
{
try
{
// Determine save path
var basePath = savePath ?? FileSystem.CacheDirectory;
// Ensure directory exists
if (!Directory.Exists(basePath))
{
Directory.CreateDirectory(basePath);
}
// Generate filename from pattern
var filename = GenerateFilename(filenamePattern, predictionId, extension);
// Full path
var filePath = Path.Combine(basePath, filename);
// Save the file
await File.WriteAllBytesAsync(filePath, bytes);
return FileSaveResult.Succeeded(filePath, bytes.Length);
}
catch (Exception ex)
{
return FileSaveResult.Failed(ex);
}
}
public string GenerateFilename(string pattern, string? predictionId, string extension)
{
var now = DateTimeOffset.Now;
var result = pattern
.Replace("{timestamp}", now.ToUnixTimeSeconds().ToString())
.Replace("{datetime}", now.ToString("yyyyMMdd_HHmmss"))
.Replace("{id}", predictionId ?? Guid.NewGuid().ToString("N")[..8])
.Replace("{guid}", Guid.NewGuid().ToString("N"));
// Sanitize filename
foreach (var c in Path.GetInvalidFileNameChars())
{
result = result.Replace(c, '_');
}
// Add extension if not already present
if (!result.EndsWith(extension, StringComparison.OrdinalIgnoreCase))
{
result += extension;
}
return result;
}
private static string GetFileExtension(string? contentType, string url, TransformationType type)
{
// Try to get from content type
if (!string.IsNullOrEmpty(contentType))
{
var ext = contentType switch
{
"image/png" => ".png",
"image/jpeg" or "image/jpg" => ".jpg",
"image/webp" => ".webp",
"image/gif" => ".gif",
"video/mp4" => ".mp4",
"video/webm" => ".webm",
"video/quicktime" => ".mov",
_ => null
};
if (ext != null) return ext;
}
// Try to extract from URL
try
{
var uri = new Uri(url);
var path = uri.AbsolutePath;
var ext = Path.GetExtension(path);
if (!string.IsNullOrEmpty(ext))
return ext;
}
catch { }
// Default extensions
return type == TransformationType.Image ? ".png" : ".mp4";
}
}
}