pleroma: media

This commit is contained in:
uwaa 2024-12-18 16:04:26 +00:00
parent 5e0feab2b3
commit 4a6d5dbb93
4 changed files with 177 additions and 9 deletions

18
Pleroma/CommonMIMEs.cs Normal file
View file

@ -0,0 +1,18 @@
namespace Uwaa.Pleroma;
public enum CommonMIMEs
{
PNG,
JPEG,
GIF,
MP4,
WEBM,
MOV,
WAV,
MP3,
OGG,
Text,
}

View file

@ -49,6 +49,16 @@ public class Attachment
/// </summary>
[JsonPropertyName("url")]
public string URL { get; set; } = null!;
/// <summary>
/// Downloads the attachment.
/// </summary>
public async Task<byte[]> Download()
{
using HttpClient client = new HttpClient();
HttpResponseMessage res = await client.GetAsync(URL);
return await res.Content.ReadAsByteArrayAsync();
}
}
[JsonConverter(typeof(EnumLowerCaseConverter<AttachmentType>))]
@ -77,3 +87,30 @@ public class PleromaAttachmentData
[JsonPropertyName("name")]
public string Name { get; set; } = null!;
}
[JsonConverter(typeof(MediaIDConverter))]
public readonly struct MediaID(string id)
{
public static implicit operator MediaID(string id) => new MediaID(id);
public static implicit operator MediaID(Attachment attachment) => new MediaID(attachment.ID);
public readonly string ID = id;
public override string ToString() => ID;
}
class MediaIDConverter : JsonConverter<MediaID>
{
public override bool CanConvert(Type type) => type.IsAssignableTo(typeof(MediaID));
public override MediaID Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return new MediaID(reader.GetString() ?? throw new NullReferenceException("Expected a string, got null"));
}
public override void Write(Utf8JsonWriter writer, MediaID value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ID);
}
}

View file

@ -7,5 +7,25 @@ public class PleromaException : Exception
public override string Message => Text;
public override string ToString() => Text;
public override string ToString() => Message;
}
public class PleromaAggregateException : Exception
{
[JsonPropertyName("errors")]
public PleromaInnerException[] Text { get; set; } = null!;
public override string Message => string.Join("\n", (object?[])Text);
public override string ToString() => Message;
}
public class PleromaInnerException : Exception
{
[JsonPropertyName("detail")]
public string Text { get; set; } = null!;
public override string Message => Text;
public override string ToString() => Message;
}

View file

@ -65,7 +65,7 @@ public class Pleroma
string text = await res.Content.ReadAsStringAsync();
if (res.StatusCode is >= (HttpStatusCode)400 and < (HttpStatusCode)600)
if (res.StatusCode is >= (HttpStatusCode)300)
{
try
{
@ -87,6 +87,17 @@ public class Pleroma
{
//Not an error
}
try
{
PleromaAggregateException? err = JsonSerializer.Deserialize<PleromaAggregateException>(text, SerializerOptions);
if (err != null && err.Text != null)
throw err;
}
catch (JsonException)
{
//Not an error
}
}
if (res.StatusCode is >= (HttpStatusCode)200 and < (HttpStatusCode)300)
@ -179,28 +190,36 @@ public class Pleroma
/// <param name="replyTo">ID of the status being replied to, if status is a reply</param>
/// <param name="quoting"> ID of the status being quoted.</param>
/// <param name="language">ISO 639 language code for this status.</param>
/// <param name="attachments">Array of Attachment ids to be attached as media.</param>
/// <param name="visibility">Visibility of the posted status.</param>
/// <exception cref="HttpRequestException">Thrown if something goes wrong while uploading the status.</exception>
/// <returns>The status if posting was successful.</returns>
public Task<Status> PostStatus(string content,
/// <exception cref="HttpRequestException">Thrown if something goes wrong while publishing the status.</exception>
/// <returns>The newly published status if posting was successful.</returns>
public Task<Status> PostStatus(string? content = null,
bool sensitive = false,
int? expiresIn = null,
string? replyTo = null,
string? quoting = null,
string? language = null,
MediaID[]? attachments = null,
StatusVisibility visibility = StatusVisibility.Public)
{
if (content == null && (attachments == null || attachments.Length == 0))
throw new ArgumentException("Cannot post nothing. Content and/or attachments must be provided.");
MemoryStream mem = new MemoryStream();
{
Utf8JsonWriter writer = new Utf8JsonWriter(mem, new JsonWriterOptions() { SkipValidation = true });
writer.WriteStartObject();
if (content != null)
{
writer.WritePropertyName("status");
writer.WriteStringValue(content);
writer.WritePropertyName("content-type");
writer.WriteStringValue("text/plain");
}
if (sensitive)
{
@ -232,6 +251,15 @@ public class Pleroma
writer.WriteStringValue(language);
}
if (attachments != null)
{
writer.WritePropertyName("media_ids");
writer.WriteStartArray();
foreach (MediaID media in attachments)
writer.WriteStringValue(media.ID);
writer.WriteEndArray();
}
if (visibility != StatusVisibility.Public)
{
writer.WritePropertyName("visibility");
@ -250,6 +278,71 @@ public class Pleroma
return Retry<Status>(req)!;
}
static string CommonMIMEString(CommonMIMEs mime) => mime switch
{
CommonMIMEs.PNG => "image/png",
CommonMIMEs.JPEG => "image/jpeg",
CommonMIMEs.GIF => "image/gif",
CommonMIMEs.MP4 => "video/mp4",
CommonMIMEs.WEBM => "video/webm",
CommonMIMEs.MOV => "video/quicktime",
CommonMIMEs.WAV => "audio/vnd.wav",
CommonMIMEs.MP3 => "audio/mpeg",
CommonMIMEs.OGG => "audio/ogg",
CommonMIMEs.Text => "text/plain",
_ => throw new ArgumentException("Unknown common MIME", nameof(mime)),
};
/// <summary>
/// Uploads a file to the server. The resulting attachment can then be included in a status as an attachment.
/// </summary>
/// <param name="mime">The MIME type of the file.</param>
/// <param name="file">An array containing the file's contents.</param>
/// <returns>A handle of the uploaded file, including its ID and URLs.</returns>
public Task<Attachment> Upload(CommonMIMEs mime, byte[] file) => Upload(CommonMIMEString(mime), file);
/// <summary>
/// Uploads a file to the server. The resulting attachment can then be included in a status as an attachment.
/// </summary>
/// <param name="mime">The MIME type of the file.</param>
/// <param name="file">An array containing the file's contents.</param>
/// <returns>A handle of the uploaded file, including its ID and URLs.</returns>
public Task<Attachment> Upload(string mime, byte[] file) => Upload(mime, new ByteArrayContent(file));
/// <summary>
/// Uploads a file to the server. The resulting attachment can then be included in a status as an attachment.
/// </summary>
/// <param name="mime">The MIME type of the file.</param>
/// <param name="stream">A stream of the file's contents.</param>
/// <returns>A handle of the uploaded file, including its ID and URLs.</returns>
public Task<Attachment> Upload(CommonMIMEs mime, Stream stream) => Upload(CommonMIMEString(mime), stream);
/// <summary>
/// Uploads a file to the server. The resulting attachment can then be included in a status as an attachment.
/// </summary>
/// <param name="mime">The MIME type of the file.</param>
/// <param name="stream">A stream of the file's contents.</param>
/// <returns>A handle of the uploaded file, including its ID and URLs.</returns>
public Task<Attachment> Upload(string mime, Stream stream) => Upload(mime, new StreamContent(stream));
async Task<Attachment> Upload(string mime, HttpContent content)
{
content.Headers.ContentType = new MediaTypeHeaderValue(mime);
content.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
{
Name = "\"file\"",
FileName = $"\"{Random.Shared.NextInt64()}\"",
};
MultipartFormDataContent form = new MultipartFormDataContent();
form.Add(content);
HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, "/api/v1/media");
req.Content = form;
return (await Retry<Attachment>(req))!;
}
/// <summary>
/// Reposts/boosts/shares a status.
/// </summary>