Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions deploy/portainer-stack.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
services:
beacon-server:
image: beacon-server:0.1.10
image: beacon-server:0.1.11
restart: always
environment:
ASPNETCORE_ENVIRONMENT: Production
Expand Down Expand Up @@ -59,7 +59,7 @@ services:
/bin/sh -c '(while sleep 86400; do nginx -s reload; done) & exec nginx -g "daemon off;"'

beacon-backup:
image: beacon-backup:0.1.10
image: beacon-backup:0.1.11
restart: always
environment:
BACKUP_INTERVAL_SECONDS: "86400"
Expand Down
2 changes: 1 addition & 1 deletion src/Beacon.Plugin/Beacon.Plugin.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<!-- Drives the built DLL, the generated manifest, and the folder Dalamud loads it from. -->
<AssemblyName>Beacon</AssemblyName>
<RootNamespace>Beacon</RootNamespace>
<Version>0.1.10.0</Version>
<Version>0.1.11.0</Version>
<!-- The Dalamud SDK does not turn these on, and every service file wants System.Threading.Tasks. -->
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
Expand Down
2 changes: 1 addition & 1 deletion src/Beacon.Plugin/UI/ProfileLabels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ public static string LastSeen(DateTimeOffset? lastActive)
< 90 => "Seen within the hour",
< 60 * 12 => "Seen today",
< 60 * 36 => "Seen yesterday",
< 60 * 24 * 7 => $"Seen {(int)span.TotalDays} days ago",
< 60 * 24 * 7 => (int)span.TotalDays == 1 ? "Seen yesterday" : $"Seen {(int)span.TotalDays} days ago",
< 60 * 24 * 14 => "Seen last week",
< 60 * 24 * 60 => $"Seen {(int)(span.TotalDays / 7)} weeks ago",
_ => "Not seen in months",
Expand Down
30 changes: 25 additions & 5 deletions src/Beacon.Server/Endpoints/ImageEndpoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,29 @@ private static async Task<IResult> ServeAsync(
BeaconDbContext db,
CancellationToken ct)
{
// Beacon screenshots and profile gallery images share one store on disk but keep separate
// metadata tables. Looking in only the screenshot table is what made every profile portrait
// return 404 while its bytes sat on disk the whole time.
var record = await db.Images.FirstOrDefaultAsync(i => i.Id == imageId, ct);
if (record is null)
return Results.NotFound();

string contentType;
DateTimeOffset createdAt;

if (record is not null)
{
contentType = record.ContentType;
createdAt = record.CreatedAt;
}
else
{
var gallery = await db.ProfileImages.FirstOrDefaultAsync(i => i.ImageId == imageId, ct);
if (gallery is null)
return Results.NotFound();

// The gallery stores no content type because everything in the store is written as WebP.
contentType = "image/webp";
createdAt = gallery.CreatedAt;
}

var wantsPng = string.Equals(format, "png", StringComparison.OrdinalIgnoreCase);

Expand All @@ -168,16 +188,16 @@ private static async Task<IResult> ServeAsync(
if (path is null || !File.Exists(path))
return Results.NotFound();

var contentType = wantsPng ? "image/png" : record.ContentType;
var served = wantsPng ? "image/png" : contentType;
var variant = $"{(thumb ? "t" : "f")}{(wantsPng ? "p" : "w")}";

// Image ids are never reused and the bytes never change, so this can be cached hard.
// The tag includes the variant so a PNG and a WebP of the same image never collide in a cache.
return Results.File(
path,
contentType,
served,
enableRangeProcessing: true,
lastModified: record.CreatedAt,
lastModified: createdAt,
entityTag: new Microsoft.Net.Http.Headers.EntityTagHeaderValue($"\"{imageId:N}{variant}\""));
}

Expand Down
Loading