using System.Security.Cryptography; using VoidCat.Model; using VoidCat.Model.Exceptions; using VoidCat.Services.Abstractions; namespace VoidCat.Services.Files; /// public class LocalDiskFileStore : StreamFileStore, IFileStore { private readonly VoidSettings _settings; private readonly CompressContent _stripMetadata; public LocalDiskFileStore(VoidSettings settings, IAggregateStatsCollector stats, CompressContent stripMetadata) : base(stats) { _settings = settings; _stripMetadata = stripMetadata; } /// public async ValueTask Egress(EgressRequest request, Stream outStream, CancellationToken cts) { await using var fs = await Open(request, cts); await EgressFromStream(fs, request, outStream, cts); } /// public ValueTask StartEgress(EgressRequest request) { return ValueTask.FromResult(new EgressResult()); } /// public string Key => "local-disk"; public ValueTask Exists(Guid id) { var path = MapPath(id); return ValueTask.FromResult(File.Exists(path)); } public async ValueTask Ingress(IngressPayload payload, CancellationToken cts) { var finalPath = MapCreatePath(payload.Id); await using var fsTemp = new FileStream(finalPath, payload.IsAppend ? FileMode.Append : FileMode.Create, FileAccess.ReadWrite); var vf = await IngressToStream(fsTemp, payload, cts); if (payload.ShouldStripMetadata && payload.Segment == payload.TotalSegments) { fsTemp.Close(); var ext = Path.GetExtension(vf.Name); var srcPath = $"{finalPath}_orig{ext}"; File.Move(finalPath, srcPath); var dstPath = $"{finalPath}_dst{ext}"; var res = await _stripMetadata.TryCompressMedia(srcPath, dstPath, cts); if (res.Success) { File.Move(res.OutPath, finalPath); File.Delete(srcPath); // recompute metadata var fInfo = new FileInfo(finalPath); var hash = await SHA256.Create().ComputeHashAsync(fInfo.OpenRead(), cts); vf = vf with { Size = (ulong)fInfo.Length, Digest = hash.ToHex(), MimeType = res.MimeType ?? vf.MimeType }; } else { File.Delete(srcPath); throw new Exception("Failed to strip metadata, please try again"); } } if (payload.Segment == payload.TotalSegments) { var t = await vf.ToMeta(false).MakeTorrent(vf.Id, new FileStream(finalPath, FileMode.Open), _settings.SiteUrl, _settings.TorrentTrackers); var ub = new UriBuilder(_settings.SiteUrl); ub.Path = $"/d/{vf.Id.ToBase58()}.torrent"; vf.MagnetLink = $"{t.GetMagnetLink()}&xs={Uri.EscapeDataString(ub.ToString())}"; } return vf; } public ValueTask DeleteFile(Guid id) { var fp = MapPath(id); if (File.Exists(fp)) { File.Delete(fp); } return ValueTask.CompletedTask; } public ValueTask Open(EgressRequest request, CancellationToken cts) { var path = MapPath(request.Id); if (!File.Exists(path)) throw new VoidFileNotFoundException(request.Id); return ValueTask.FromResult(new FileStream(path, FileMode.Open, FileAccess.Read)); } private string MapCreatePath(Guid id) { var path = MapPath(id); var dir = Path.GetDirectoryName(path); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir!); } return path; } private string MapPath(Guid id) => Path.Join(_settings.DataDirectory, "files-v2", id.ToString()[..2], id.ToString()[2..4], id.ToString()); }