Compare commits
8 Commits
v1.1.28
...
3dca408356
| Author | SHA1 | Date | |
|---|---|---|---|
| 3dca408356 | |||
| 1655e342b7 | |||
| 7223c35658 | |||
| b85bcd1c7a | |||
| 8536d335bc | |||
| b34f7436c2 | |||
| 8b5803c085 | |||
| 5e2a7b53c9 |
@@ -2,6 +2,8 @@
|
||||
@import "colors";
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
scrollbar-color: $accentColor transparent;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
:root {
|
||||
@@ -18,6 +20,7 @@
|
||||
.stickyTop {
|
||||
top: 0;
|
||||
position: sticky;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
body {
|
||||
@@ -34,6 +37,8 @@ body {
|
||||
#content {
|
||||
grid-area: Content;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
height: 100dvh;
|
||||
padding: 10px;
|
||||
/* margin-left: $navBarSize; */
|
||||
}
|
||||
|
||||
@@ -14,8 +14,13 @@ pub fn MediaItem(props: MediaItemProps) -> Element {
|
||||
let filename = item.file_name;
|
||||
let id = item.id.unwrap().value;
|
||||
let thumb = item.thumb_url;
|
||||
let url = item.media_url;
|
||||
return rsx! {
|
||||
a { class: "mediaItem", href: "{HOST}/m/{id}", target: "_blank",
|
||||
a {
|
||||
class: "mediaItem",
|
||||
href: "{HOST}/{url}",
|
||||
target: "_blank",
|
||||
"data-id" : id,
|
||||
img { src: "{HOST}{thumb}" }
|
||||
span { class: "info",
|
||||
span { class: "name", "{filename}" }
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FFMpegCore" Version="5.2.0" />
|
||||
<PackageReference Include="Isopoh.Cryptography.Argon2" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.7" />
|
||||
<PackageReference Include="MaybeError" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.6" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="3.4.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.7" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="3.4.1" />
|
||||
<PackageReference Include="MongoDB.Driver.Core.Extensions.DiagnosticSources" Version="2.1.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.6" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.12.1" />
|
||||
<PackageReference Include="ZLinq" Version="1.4.12" />
|
||||
<PackageReference Include="ZLinq" Version="1.5.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
|
||||
using SixLabors.ImageSharp;
|
||||
|
||||
namespace AobaCore.Models;
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
@@ -16,23 +18,27 @@ public class Media
|
||||
public ObjectId Owner { get; set; }
|
||||
public DateTime UploadDate { get; set; }
|
||||
public string[] Tags { get; set; } = [];
|
||||
public Size? Dimensions { get; set; }
|
||||
public Dictionary<ThumbnailSize, ObjectId> Thumbnails { get; set; } = [];
|
||||
|
||||
|
||||
public static readonly Dictionary<string, MediaType> KnownTypes = new()
|
||||
{
|
||||
{ ".jpg", MediaType.Image },
|
||||
{ ".avif", MediaType.Image },
|
||||
{ ".jpeg", MediaType.Image },
|
||||
{ ".jxr", MediaType.Image },
|
||||
{ ".avif", MediaType.Image },
|
||||
{ ".png", MediaType.Image },
|
||||
{ ".apng", MediaType.Image },
|
||||
{ ".webp", MediaType.Image },
|
||||
{ ".qoi", MediaType.Image },
|
||||
{ ".ico", MediaType.Image },
|
||||
{ ".gif", MediaType.Image },
|
||||
{ ".mp3", MediaType.Audio },
|
||||
{ ".flac", MediaType.Audio },
|
||||
{ ".alac", MediaType.Audio },
|
||||
{ ".mp4", MediaType.Video },
|
||||
{ ".m4v", MediaType.Video },
|
||||
{ ".webm", MediaType.Video },
|
||||
{ ".mov", MediaType.Video },
|
||||
{ ".avi", MediaType.Video },
|
||||
@@ -50,6 +56,10 @@ public class Media
|
||||
{ ".xml", MediaType.Code },
|
||||
{ ".json", MediaType.Code },
|
||||
{ ".py", MediaType.Code },
|
||||
{ ".rs", MediaType.Code },
|
||||
{ ".zed", MediaType.Code },
|
||||
{ ".ts", MediaType.Code },
|
||||
{ ".astro", MediaType.Code },
|
||||
};
|
||||
|
||||
[BsonConstructor]
|
||||
@@ -75,7 +85,7 @@ public class Media
|
||||
{
|
||||
return this switch
|
||||
{
|
||||
//Media { MediaType: MediaType.Raw or MediaType.Text or MediaType.Code} => $"/i/dl/{MediaId}/{Filename}",
|
||||
Media { MediaType: MediaType.Raw or MediaType.Text or MediaType.Code } => $"/m/{MediaId}/{Uri.EscapeDataString(Filename)}",
|
||||
_ => $"/m/{MediaId}"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ using AobaCore.Models;
|
||||
|
||||
using MaybeError.Errors;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Driver.GridFS;
|
||||
@@ -69,6 +71,7 @@ public class AobaService(IMongoDatabase db)
|
||||
{
|
||||
var fileId = await _gridFs.UploadFromStreamAsync(filename, data, cancellationToken: cancellationToken);
|
||||
var media = new Media(fileId, filename, owner);
|
||||
|
||||
await AddMediaAsync(media, cancellationToken);
|
||||
return media;
|
||||
}
|
||||
@@ -76,6 +79,10 @@ public class AobaService(IMongoDatabase db)
|
||||
{
|
||||
return ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
data.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<MaybeEx<GridFSDownloadStream, GridFSException>> GetFileStreamAsync(ObjectId mediaId, bool seekable = false, CancellationToken cancellationToken = default)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using FFMpegCore;
|
||||
using FFMpegCore.Pipes;
|
||||
|
||||
|
||||
using MaybeError.Errors;
|
||||
|
||||
using MongoDB.Bson;
|
||||
@@ -10,6 +11,7 @@ using MongoDB.Driver;
|
||||
using MongoDB.Driver.GridFS;
|
||||
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Formats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
|
||||
using System;
|
||||
@@ -24,7 +26,6 @@ namespace AobaCore.Services;
|
||||
public class ThumbnailService(IMongoDatabase db, AobaService aobaService)
|
||||
{
|
||||
private readonly GridFSBucket _gridfs = new GridFSBucket(db);
|
||||
private Lock _lock = new();
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
@@ -92,17 +93,29 @@ public class ThumbnailService(IMongoDatabase db, AobaService aobaService)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
MediaType.Image => await GenerateImageThumbnailAsync(stream, size, cancellationToken),
|
||||
MediaType.Image => await GenerateImageThumbnailAsync(stream, size, ext, cancellationToken),
|
||||
MediaType.Video => GenerateVideoThumbnail(stream, size, cancellationToken),
|
||||
MediaType.Text or MediaType.Code => await GenerateDocumentThumbnailAsync(stream, size, cancellationToken),
|
||||
_ => new Error($"No Thumbnail for {type}"),
|
||||
};
|
||||
}
|
||||
|
||||
public static async Task<Stream> GenerateImageThumbnailAsync(Stream stream, ThumbnailSize size, CancellationToken cancellationToken = default)
|
||||
private static Maybe<Image> LoadImage(Stream stream, string ext)
|
||||
{
|
||||
var img = Image.Load(stream);
|
||||
img.Mutate(o =>
|
||||
if (ext is ".heif" or ".avif")
|
||||
{
|
||||
return new Error("Unsupported image type");
|
||||
}
|
||||
else
|
||||
return Image.Load(stream);
|
||||
}
|
||||
|
||||
public static async Task<Maybe<Stream>> GenerateImageThumbnailAsync(Stream stream, ThumbnailSize size, string ext, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var img = LoadImage(stream, ext);
|
||||
if (img.HasError)
|
||||
return img.Error;
|
||||
img.Value.Mutate(o =>
|
||||
{
|
||||
var size =
|
||||
o.Resize(new ResizeOptions
|
||||
@@ -112,8 +125,9 @@ public class ThumbnailService(IMongoDatabase db, AobaService aobaService)
|
||||
Size = new Size(300, 300)
|
||||
});
|
||||
});
|
||||
img.Value.Dispose();
|
||||
var result = new MemoryStream();
|
||||
await img.SaveAsWebpAsync(result, cancellationToken);
|
||||
await img.Value.SaveAsWebpAsync(result, cancellationToken);
|
||||
result.Position = 0;
|
||||
return result;
|
||||
}
|
||||
@@ -142,7 +156,7 @@ public class ThumbnailService(IMongoDatabase db, AobaService aobaService)
|
||||
output.Position = 0;
|
||||
return output;
|
||||
}
|
||||
catch(Exception ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ex;
|
||||
}
|
||||
@@ -156,4 +170,4 @@ public class ThumbnailService(IMongoDatabase db, AobaService aobaService)
|
||||
{
|
||||
return new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Isopoh.Cryptography.Argon2" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.7" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.12.1" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.22.1" />
|
||||
<PackageReference Include="MimeTypesMap" Version="1.0.9" />
|
||||
@@ -24,7 +24,7 @@
|
||||
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.9.0-beta.2" />
|
||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.12.0" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.12.0" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.12.0" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.12.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -14,10 +14,11 @@ namespace AobaServer.Controllers;
|
||||
public class MediaController(AobaService aobaService, ILogger<MediaController> logger) : Controller
|
||||
{
|
||||
[HttpGet("{id}")]
|
||||
[HttpGet("{id}/*")]
|
||||
[ResponseCache(Duration = int.MaxValue)]
|
||||
public async Task<IActionResult> MediaAsync(ObjectId id, [FromServices] MongoClient client, CancellationToken cancellationToken)
|
||||
{
|
||||
var file = await aobaService.GetFileStreamAsync(id, cancellationToken: cancellationToken);
|
||||
var file = await aobaService.GetFileStreamAsync(id, seekable: true, cancellationToken: cancellationToken);
|
||||
if (file.HasError)
|
||||
{
|
||||
logger.LogError(file.Error.Exception, "Failed to load media stream");
|
||||
@@ -58,6 +59,7 @@ public class MediaController(AobaService aobaService, ILogger<MediaController> l
|
||||
}
|
||||
|
||||
[HttpGet("/t/{id}")]
|
||||
[ResponseCache(Duration = int.MaxValue)]
|
||||
public async Task<IActionResult> ThumbAsync(ObjectId id, [FromServices] ThumbnailService thumbnailService, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var thumb = await thumbnailService.GetThumbnailByFileIdAsync(id, cancellationToken);
|
||||
|
||||
@@ -82,6 +82,7 @@ message MediaModel {
|
||||
int32 viewCount = 5;
|
||||
Id owner = 6;
|
||||
string thumbUrl = 7;
|
||||
string mediaUrl = 8;
|
||||
}
|
||||
|
||||
enum MediaType {
|
||||
@@ -99,3 +100,14 @@ message ShareXResponse {
|
||||
string error = 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
message SearchQuery {
|
||||
optional string queryText = 1;
|
||||
repeated Filter filters = 2;
|
||||
}
|
||||
|
||||
message Filter {
|
||||
string key = 1;
|
||||
repeated string values = 2;
|
||||
}
|
||||
@@ -56,6 +56,7 @@ public static class ProtoExtensions
|
||||
Owner = media.Owner.ToId(),
|
||||
ViewCount = media.ViewCount,
|
||||
ThumbUrl = thumbUrl,
|
||||
MediaUrl = media.GetMediaUrl()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user