Files
decePubClient/Services/MediaService.cs
T

92 lines
4.3 KiB
C#
Raw Normal View History

using System.Net;
using System.Net.Http.Headers;
using collAnon.Client.Services;
using decePubClient.Extensions;
using decePubClient.Models;
using decePubClient.Models.Mastodon;
using PrivaPub.ClientModels;
namespace decePubClient.Services
{
/// <summary>
/// Uploads to PrivaPub (/api/v2/media, multipart), streamed from the browser's file; follows what it is still processing
/// (audio and video: 202, then 206 until ready); describes what isn't posted yet; and knows what PrivaPub takes.
/// </summary>
public class MediaService(IHttpService httpService, CoalescingStringLocalizer localizer)
{
static readonly MediaConfiguration Fallback = new()
{
SupportedMimeTypes = ["image/jpeg", "image/png", "image/gif", "image/webp", "image/avif", "video/mp4", "video/quicktime", "video/webm",
"audio/mpeg", "audio/mp3", "audio/ogg", "audio/wav", "audio/x-wav", "audio/mp4", "audio/x-m4a", "audio/flac", "audio/webm"],
ImageSizeLimit = 16 * 1024 * 1024,
VideoSizeLimit = 99 * 1024 * 1024
};
MediaConfiguration limits;
/// <summary>What PrivaPub takes, read once a session (/api/v2/instance); its defaults while it can't be read.</summary>
public async Task<MediaConfiguration> Limits(CancellationToken cancellationToken = default)
{
if (limits is not null)
return limits;
using var response = await httpService.GetAnon(APIs.Mastodon.GET_InstanceV2, cancellationToken: cancellationToken);
if (response.IsSuccessStatusCode
&& await response.MastodonReadFromJsonAsync<InstanceV2>(cancellationToken) is { Configuration.MediaAttachments.SupportedMimeTypes.Count: > 0 } instance)
return limits = instance.Configuration.MediaAttachments;
return Fallback;
}
/// <summary>The attachment as Data; its Url is null while PrivaPub still processes it (follow it with Get).</summary>
public async Task<WebResult> Upload(UploadMedia media, long maximumSize, CancellationToken cancellationToken = default)
{
using var response = await httpService.Post(APIs.Mastodon.POST_Media, (Func<HttpContent>)(() =>
{
var file = new StreamContent(media.File.OpenReadStream(maximumSize, cancellationToken));
file.Headers.ContentType = new MediaTypeHeaderValue(media.ContentType);
var content = new MultipartFormDataContent { { file, "file", media.FileName } };
if (media.AltText is { Length: > 0 } description)
content.Add(new StringContent(description), "description");
return content;
}), cancellationToken: cancellationToken);
if (!response.IsSuccessStatusCode)
return await response.Content.ReadWebResult(response.StatusCode, localizer, cancellationToken);
return await Attachment(response, cancellationToken);
}
/// <summary>An upload PrivaPub is processing: Data is the attachment once it is ready, null while it isn't; invalid
/// when it can't be.</summary>
public async Task<WebResult> Get(string mediaId, CancellationToken cancellationToken = default)
{
using var response = await httpService.Get(APIs.Mastodon.GET_M_Media(mediaId), cancellationToken: cancellationToken);
if (response.StatusCode == HttpStatusCode.PartialContent)
return new WebResult();
if (!response.IsSuccessStatusCode)
return await response.Content.ReadWebResult(response.StatusCode, localizer, cancellationToken);
return await Attachment(response, cancellationToken);
}
/// <summary>Describes an upload not posted yet (a posted one is described again by editing its post).</summary>
public async Task<WebResult> Describe(string mediaId, string description, CancellationToken cancellationToken = default)
{
using var response = await httpService.Put(APIs.Mastodon.PUT_M_Media(mediaId),
new KeyValuePair<string, string>[] { new("description", description ?? string.Empty) }, cancellationToken: cancellationToken);
if (!response.IsSuccessStatusCode)
return await response.Content.ReadWebResult(response.StatusCode, localizer, cancellationToken);
return new WebResult();
}
async Task<WebResult> Attachment(HttpResponseMessage response, CancellationToken cancellationToken)
{
var attachment = await response.MastodonReadFromJsonAsync<MediaAttachment>(cancellationToken);
return attachment?.Id is null
? new WebResult().Invalidate(localizer["The server sent an invalid response."], (int)HttpStatusCode.BadGateway)
: new WebResult { Data = attachment };
}
}
}