mirror of
https://git.v0l.io/Kieran/void.cat.git
synced 2025-04-09 07:38:01 +02:00
Build torrents with web seed
This commit is contained in:
parent
0fd723847f
commit
8bb7064963
@ -47,7 +47,19 @@ public class DownloadController : Controller
|
||||
var voidFile = await SetupDownload(gid);
|
||||
if (voidFile == default) return;
|
||||
|
||||
var egressReq = new EgressRequest(gid, GetRanges(Request, (long) voidFile!.Metadata!.Size));
|
||||
if (id.EndsWith(".torrent"))
|
||||
{
|
||||
var t = await voidFile.Metadata!.MakeTorrent(
|
||||
await _storage.Open(new(gid, Enumerable.Empty<RangeRequest>()), CancellationToken.None),
|
||||
_settings.SiteUrl);
|
||||
|
||||
Response.Headers.ContentDisposition = $"inline; filename=\"{id}\"";
|
||||
Response.ContentType = "application/x-bittorent";
|
||||
await t.EncodeToAsync(Response.Body);
|
||||
return;
|
||||
}
|
||||
|
||||
var egressReq = new EgressRequest(gid, GetRanges(Request, (long)voidFile!.Metadata!.Size));
|
||||
if (egressReq.Ranges.Count() > 1)
|
||||
{
|
||||
_logger.LogWarning("Multi-range request not supported!");
|
||||
@ -59,10 +71,10 @@ public class DownloadController : Controller
|
||||
}
|
||||
else if (egressReq.Ranges.Count() == 1)
|
||||
{
|
||||
Response.StatusCode = (int) HttpStatusCode.PartialContent;
|
||||
Response.StatusCode = (int)HttpStatusCode.PartialContent;
|
||||
if (egressReq.Ranges.Sum(a => a.Size) == 0)
|
||||
{
|
||||
Response.StatusCode = (int) HttpStatusCode.RequestedRangeNotSatisfiable;
|
||||
Response.StatusCode = (int)HttpStatusCode.RequestedRangeNotSatisfiable;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -80,7 +92,7 @@ public class DownloadController : Controller
|
||||
var preResult = await _storage.StartEgress(egressReq);
|
||||
if (preResult.Redirect != null)
|
||||
{
|
||||
Response.StatusCode = (int) HttpStatusCode.Redirect;
|
||||
Response.StatusCode = (int)HttpStatusCode.Redirect;
|
||||
Response.Headers.Location = preResult.Redirect.ToString();
|
||||
Response.ContentLength = 0;
|
||||
return;
|
||||
@ -107,7 +119,7 @@ public class DownloadController : Controller
|
||||
var orderId = Request.Headers.GetHeader("V-OrderId") ?? Request.Query["orderId"];
|
||||
if (!await IsOrderPaid(orderId))
|
||||
{
|
||||
Response.StatusCode = (int) HttpStatusCode.PaymentRequired;
|
||||
Response.StatusCode = (int)HttpStatusCode.PaymentRequired;
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@ -115,10 +127,11 @@ public class DownloadController : Controller
|
||||
// prevent hot-linking viruses
|
||||
var referer = Request.Headers.Referer.Count > 0 ? new Uri(Request.Headers.Referer.First()) : null;
|
||||
var hasCorrectReferer = referer?.Host.Equals(_settings.SiteUrl.Host, StringComparison.InvariantCultureIgnoreCase) ??
|
||||
false;
|
||||
false;
|
||||
|
||||
if (meta.VirusScan?.IsVirus == true && !hasCorrectReferer)
|
||||
{
|
||||
Response.StatusCode = (int) HttpStatusCode.Redirect;
|
||||
Response.StatusCode = (int)HttpStatusCode.Redirect;
|
||||
Response.Headers.Location = $"/{id.ToBase58()}";
|
||||
return default;
|
||||
}
|
||||
@ -159,4 +172,4 @@ public class DownloadController : Controller
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ using System.Text;
|
||||
using Amazon;
|
||||
using Amazon.Runtime;
|
||||
using Amazon.S3;
|
||||
using BencodeNET.Objects;
|
||||
using BencodeNET.Torrents;
|
||||
using VoidCat.Model.Exceptions;
|
||||
using VoidCat.Model.User;
|
||||
|
||||
@ -272,4 +274,48 @@ public static class Extensions
|
||||
|
||||
public static bool HasGoogle(this VoidSettings settings)
|
||||
=> settings.Google != null;
|
||||
|
||||
public static async Task<Torrent> MakeTorrent(this FileMeta meta, Stream fileStream, Uri baseAddress)
|
||||
{
|
||||
const int pieceSize = 16_384;
|
||||
var webSeed = new UriBuilder(baseAddress)
|
||||
{
|
||||
Path = $"/d/{meta.Id.ToBase58()}"
|
||||
};
|
||||
|
||||
async Task<byte[]> BuildPieces()
|
||||
{
|
||||
fileStream.Seek(0, SeekOrigin.Begin);
|
||||
var hashes = new List<byte[]>();
|
||||
var chunk = new byte[pieceSize];
|
||||
for (var x = 0; x < (int)Math.Ceiling(meta.Size / (decimal)pieceSize); x++)
|
||||
{
|
||||
var rLen = await fileStream.ReadAsync(chunk, 0, chunk.Length);
|
||||
hashes.Add(SHA1.HashData(chunk.AsSpan(0, rLen)));
|
||||
}
|
||||
|
||||
return hashes.SelectMany(a => a).ToArray();
|
||||
}
|
||||
|
||||
// build magnet link
|
||||
var t = new Torrent()
|
||||
{
|
||||
File = new()
|
||||
{
|
||||
FileName = meta.Name,
|
||||
FileSize = (long)meta.Size
|
||||
},
|
||||
Comment = meta.Description,
|
||||
CreationDate = meta.Uploaded.UtcDateTime,
|
||||
IsPrivate = false,
|
||||
PieceSize = pieceSize,
|
||||
Pieces = await BuildPieces(),
|
||||
ExtraFields = new BDictionary
|
||||
{
|
||||
{"url-list", webSeed.ToString()}
|
||||
}
|
||||
};
|
||||
|
||||
return t;
|
||||
}
|
||||
}
|
@ -80,6 +80,11 @@ public record FileMeta : IFileMeta
|
||||
/// Encryption params as JSON string
|
||||
/// </summary>
|
||||
public string? EncryptionParams { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Magnet link for downloads
|
||||
/// </summary>
|
||||
public string? MagnetLink { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -49,21 +49,21 @@ public class LocalDiskFileStore : StreamFileStore, IFileStore
|
||||
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.Metadata!.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);
|
||||
@ -84,6 +84,12 @@ public class LocalDiskFileStore : StreamFileStore, IFileStore
|
||||
}
|
||||
}
|
||||
|
||||
if (payload.Segment == payload.TotalSegments)
|
||||
{
|
||||
var t = await vf.Metadata!.MakeTorrent(new FileStream(finalPath, FileMode.Open), _settings.SiteUrl);
|
||||
vf.Metadata!.MagnetLink = t.GetMagnetLink();
|
||||
}
|
||||
|
||||
return vf;
|
||||
}
|
||||
|
||||
|
@ -35,8 +35,8 @@ public class PostgresFileMetadataStore : IFileMetadataStore
|
||||
await using var conn = await _connection.Get();
|
||||
await conn.ExecuteAsync(
|
||||
@"insert into
|
||||
""Files""(""Id"", ""Name"", ""Size"", ""Uploaded"", ""Description"", ""MimeType"", ""Digest"", ""EditSecret"", ""Expires"", ""Storage"", ""EncryptionParams"")
|
||||
values(:id, :name, :size, :uploaded, :description, :mimeType, :digest, :editSecret, :expires, :store, :encryptionParams)
|
||||
""Files""(""Id"", ""Name"", ""Size"", ""Uploaded"", ""Description"", ""MimeType"", ""Digest"", ""EditSecret"", ""Expires"", ""Storage"", ""EncryptionParams"", ""MagnetLink"")
|
||||
values(:id, :name, :size, :uploaded, :description, :mimeType, :digest, :editSecret, :expires, :store, :encryptionParams, :magnetLink)
|
||||
on conflict (""Id"") do update set
|
||||
""Name"" = :name,
|
||||
""Size"" = :size,
|
||||
@ -44,7 +44,8 @@ on conflict (""Id"") do update set
|
||||
""MimeType"" = :mimeType,
|
||||
""Expires"" = :expires,
|
||||
""Storage"" = :store,
|
||||
""EncryptionParams"" = :encryptionParams",
|
||||
""EncryptionParams"" = :encryptionParams,
|
||||
""MagnetLink"" = :magnetLink",
|
||||
new
|
||||
{
|
||||
id,
|
||||
@ -57,7 +58,8 @@ on conflict (""Id"") do update set
|
||||
editSecret = obj.EditSecret,
|
||||
expires = obj.Expires?.ToUniversalTime(),
|
||||
store = obj.Storage,
|
||||
encryptionParams = obj.EncryptionParams
|
||||
encryptionParams = obj.EncryptionParams,
|
||||
magnetLink = obj.MagnetLink,
|
||||
});
|
||||
}
|
||||
|
||||
|
20
VoidCat/Services/Migrations/Database/07-MagnetLink.cs
Normal file
20
VoidCat/Services/Migrations/Database/07-MagnetLink.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using FluentMigrator;
|
||||
|
||||
namespace VoidCat.Services.Migrations.Database;
|
||||
|
||||
[Migration(20230304_1509)]
|
||||
public class MagnetLink : Migration {
|
||||
public override void Up()
|
||||
{
|
||||
Create.Column("MagnetLink")
|
||||
.OnTable("Files")
|
||||
.AsString()
|
||||
.Nullable();
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
Delete.Column("MagnetLink")
|
||||
.FromTable("Files");
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AWSSDK.S3" Version="3.7.9.30" />
|
||||
<PackageReference Include="BencodeNET" Version="4.0.0" />
|
||||
<PackageReference Include="Dapper" Version="2.0.123" />
|
||||
<PackageReference Include="FFMpegCore" Version="4.8.0" />
|
||||
<PackageReference Include="FluentMigrator" Version="3.3.2" />
|
||||
|
@ -9,7 +9,8 @@
|
||||
},
|
||||
"Settings": {
|
||||
"CorsOrigins": [
|
||||
"http://localhost:8001"
|
||||
"http://localhost:8001",
|
||||
"http://localhost:3000"
|
||||
],
|
||||
"VirusScanner": {
|
||||
"ClamAV": {
|
||||
|
@ -7,6 +7,7 @@
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"Settings": {
|
||||
"SiteUrl": "http://localhost:7195",
|
||||
"DataDirectory": "./data"
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,2 @@
|
||||
BROWSER=none
|
||||
HTTPS=true
|
||||
API_HOST=https://localhost:7195
|
||||
BROWSER=none
|
||||
API_HOST=http://localhost:7195
|
@ -2,7 +2,7 @@
|
||||
"name": "spa",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"proxy": "https://localhost:7195",
|
||||
"proxy": "http://localhost:7195",
|
||||
"dependencies": {
|
||||
"@hcaptcha/react-hcaptcha": "^1.1.1",
|
||||
"@reduxjs/toolkit": "^1.7.2",
|
||||
|
@ -273,15 +273,6 @@ export function FileUpload(props) {
|
||||
return <div dangerouslySetInnerHTML={{__html: elm.outerHTML}}/>;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
let chromeVersion = getChromeVersion();
|
||||
if (chromeVersion >= 105) {
|
||||
//doStreamUpload().catch(console.error);
|
||||
} else {
|
||||
doXHRUpload().catch(console.error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="upload">
|
||||
<div className="info">
|
||||
|
Loading…
x
Reference in New Issue
Block a user