2023-01-26 13:49:38 +00:00
|
|
|
using System.Security.Cryptography;
|
2022-01-25 23:39:51 +00:00
|
|
|
using VoidCat.Model;
|
|
|
|
using VoidCat.Model.Exceptions;
|
2022-02-16 16:33:00 +00:00
|
|
|
using VoidCat.Services.Abstractions;
|
2022-01-25 23:39:51 +00:00
|
|
|
|
2022-02-22 14:20:31 +00:00
|
|
|
namespace VoidCat.Services.Files;
|
2022-01-25 23:39:51 +00:00
|
|
|
|
2022-06-08 16:17:53 +00:00
|
|
|
/// <inheritdoc cref="IFileStore"/>
|
2022-03-01 11:32:41 +00:00
|
|
|
public class LocalDiskFileStore : StreamFileStore, IFileStore
|
2022-01-25 23:39:51 +00:00
|
|
|
{
|
|
|
|
private readonly VoidSettings _settings;
|
2023-01-30 18:35:36 +00:00
|
|
|
private readonly CompressContent _stripMetadata;
|
2022-02-08 23:52:01 +00:00
|
|
|
|
2023-01-30 18:35:36 +00:00
|
|
|
public LocalDiskFileStore(VoidSettings settings, IAggregateStatsCollector stats, CompressContent stripMetadata)
|
2022-06-08 16:17:53 +00:00
|
|
|
: base(stats)
|
2022-01-25 23:39:51 +00:00
|
|
|
{
|
|
|
|
_settings = settings;
|
2023-01-26 13:49:38 +00:00
|
|
|
_stripMetadata = stripMetadata;
|
2022-01-25 23:39:51 +00:00
|
|
|
}
|
|
|
|
|
2022-06-08 16:17:53 +00:00
|
|
|
/// <inheritdoc />
|
2022-02-16 16:33:00 +00:00
|
|
|
public async ValueTask Egress(EgressRequest request, Stream outStream, CancellationToken cts)
|
2022-01-25 23:39:51 +00:00
|
|
|
{
|
2022-03-07 13:38:53 +00:00
|
|
|
await using var fs = await Open(request, cts);
|
2022-03-01 11:32:41 +00:00
|
|
|
await EgressFromStream(fs, request, outStream, cts);
|
2022-01-25 23:39:51 +00:00
|
|
|
}
|
2023-01-26 13:49:38 +00:00
|
|
|
|
2022-07-25 18:46:14 +00:00
|
|
|
/// <inheritdoc />
|
|
|
|
public ValueTask<EgressResult> StartEgress(EgressRequest request)
|
|
|
|
{
|
|
|
|
return ValueTask.FromResult(new EgressResult());
|
|
|
|
}
|
2022-01-25 23:39:51 +00:00
|
|
|
|
2022-07-25 17:59:32 +00:00
|
|
|
/// <inheritdoc />
|
|
|
|
public string Key => "local-disk";
|
2023-01-26 13:49:38 +00:00
|
|
|
|
2023-08-24 09:51:07 +00:00
|
|
|
public ValueTask<bool> Exists(Guid id)
|
|
|
|
{
|
|
|
|
var path = MapPath(id);
|
|
|
|
return ValueTask.FromResult(File.Exists(path));
|
|
|
|
}
|
|
|
|
|
2023-05-09 13:56:57 +00:00
|
|
|
public async ValueTask<Database.File> Ingress(IngressPayload payload, CancellationToken cts)
|
2022-01-25 23:39:51 +00:00
|
|
|
{
|
2023-08-24 09:51:07 +00:00
|
|
|
var finalPath = MapCreatePath(payload.Id);
|
2023-01-26 13:49:38 +00:00
|
|
|
await using var fsTemp = new FileStream(finalPath,
|
|
|
|
payload.IsAppend ? FileMode.Append : FileMode.Create, FileAccess.ReadWrite);
|
|
|
|
|
|
|
|
var vf = await IngressToStream(fsTemp, payload, cts);
|
2023-03-04 16:44:13 +00:00
|
|
|
|
2023-01-26 13:49:38 +00:00
|
|
|
if (payload.ShouldStripMetadata && payload.Segment == payload.TotalSegments)
|
|
|
|
{
|
|
|
|
fsTemp.Close();
|
2023-05-09 13:56:57 +00:00
|
|
|
var ext = Path.GetExtension(vf.Name);
|
2023-01-26 13:49:38 +00:00
|
|
|
var srcPath = $"{finalPath}_orig{ext}";
|
|
|
|
File.Move(finalPath, srcPath);
|
2023-03-04 16:44:13 +00:00
|
|
|
|
2023-01-26 13:49:38 +00:00
|
|
|
var dstPath = $"{finalPath}_dst{ext}";
|
2023-01-30 18:35:36 +00:00
|
|
|
var res = await _stripMetadata.TryCompressMedia(srcPath, dstPath, cts);
|
|
|
|
if (res.Success)
|
2023-01-26 13:49:38 +00:00
|
|
|
{
|
2023-01-30 18:35:36 +00:00
|
|
|
File.Move(res.OutPath, finalPath);
|
2023-01-26 13:49:38 +00:00
|
|
|
File.Delete(srcPath);
|
2023-03-04 16:44:13 +00:00
|
|
|
|
2023-01-26 13:49:38 +00:00
|
|
|
// recompute metadata
|
|
|
|
var fInfo = new FileInfo(finalPath);
|
|
|
|
var hash = await SHA256.Create().ComputeHashAsync(fInfo.OpenRead(), cts);
|
|
|
|
vf = vf with
|
|
|
|
{
|
2023-05-09 13:56:57 +00:00
|
|
|
Size = (ulong)fInfo.Length,
|
|
|
|
Digest = hash.ToHex(),
|
|
|
|
MimeType = res.MimeType ?? vf.MimeType
|
2023-01-26 13:49:38 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-04-07 18:59:31 +00:00
|
|
|
File.Delete(srcPath);
|
|
|
|
throw new Exception("Failed to strip metadata, please try again");
|
2023-01-26 13:49:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-04 16:44:13 +00:00
|
|
|
if (payload.Segment == payload.TotalSegments)
|
|
|
|
{
|
2023-05-09 13:56:57 +00:00
|
|
|
var t = await vf.ToMeta(false).MakeTorrent(vf.Id,
|
2023-04-07 18:59:31 +00:00
|
|
|
new FileStream(finalPath, FileMode.Open),
|
|
|
|
_settings.SiteUrl,
|
2023-03-04 20:31:53 +00:00
|
|
|
_settings.TorrentTrackers);
|
|
|
|
|
|
|
|
var ub = new UriBuilder(_settings.SiteUrl);
|
|
|
|
ub.Path = $"/d/{vf.Id.ToBase58()}.torrent";
|
2023-04-07 18:59:31 +00:00
|
|
|
|
2023-05-09 13:56:57 +00:00
|
|
|
vf.MagnetLink = $"{t.GetMagnetLink()}&xs={Uri.EscapeDataString(ub.ToString())}";
|
2023-03-04 16:44:13 +00:00
|
|
|
}
|
|
|
|
|
2023-01-26 13:49:38 +00:00
|
|
|
return vf;
|
2022-02-08 18:20:59 +00:00
|
|
|
}
|
2022-02-08 23:52:01 +00:00
|
|
|
|
2022-06-06 21:51:25 +00:00
|
|
|
public ValueTask DeleteFile(Guid id)
|
2022-02-22 14:20:31 +00:00
|
|
|
{
|
|
|
|
var fp = MapPath(id);
|
|
|
|
if (File.Exists(fp))
|
|
|
|
{
|
|
|
|
File.Delete(fp);
|
|
|
|
}
|
2022-06-08 16:17:53 +00:00
|
|
|
|
2022-06-06 21:51:25 +00:00
|
|
|
return ValueTask.CompletedTask;
|
2022-02-22 14:20:31 +00:00
|
|
|
}
|
|
|
|
|
2022-03-07 13:38:53 +00:00
|
|
|
public ValueTask<Stream> Open(EgressRequest request, CancellationToken cts)
|
|
|
|
{
|
|
|
|
var path = MapPath(request.Id);
|
|
|
|
if (!File.Exists(path)) throw new VoidFileNotFoundException(request.Id);
|
|
|
|
|
|
|
|
return ValueTask.FromResult<Stream>(new FileStream(path, FileMode.Open, FileAccess.Read));
|
|
|
|
}
|
|
|
|
|
2023-08-24 09:51:07 +00:00
|
|
|
private string MapCreatePath(Guid id)
|
|
|
|
{
|
|
|
|
var path = MapPath(id);
|
|
|
|
var dir = Path.GetDirectoryName(path);
|
|
|
|
if (!Directory.Exists(dir))
|
|
|
|
{
|
|
|
|
Directory.CreateDirectory(dir!);
|
|
|
|
}
|
|
|
|
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2022-01-25 23:39:51 +00:00
|
|
|
private string MapPath(Guid id) =>
|
2023-08-24 09:51:07 +00:00
|
|
|
Path.Join(_settings.DataDirectory, "files-v2", id.ToString()[..2], id.ToString()[2..4], id.ToString());
|
2023-01-26 13:49:38 +00:00
|
|
|
}
|