155 lines
5.1 KiB
C#
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";
|
|
}
|
|
}
|
|
}
|