From 3d96fe99d083b0ca46ab7e268a64e8b8d14fd201 Mon Sep 17 00:00:00 2001 From: Helge Date: Tue, 10 Dec 2024 20:27:42 +0000 Subject: [PATCH] Media Attachments (#458) Reviewed-on: https://codeberg.org/fediverse/fep/pulls/458 Co-authored-by: Helge Co-committed-by: Helge --- fep/1311/cow.jpg | Bin 0 -> 15356 bytes fep/1311/fep-1311.md | 465 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 465 insertions(+) create mode 100644 fep/1311/cow.jpg create mode 100644 fep/1311/fep-1311.md diff --git a/fep/1311/cow.jpg b/fep/1311/cow.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2680c5e5b475b9080a7a50f2e78211ebfebc14bc GIT binary patch literal 15356 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 +status: DRAFT +dateReceived: 2024-12-08 +discussionsTo: https://socialhub.activitypub.rocks/t/fep-1311-media-attachments/4796 +trackingIssue: +--- +# FEP-1311: Media Attachments + +## Summary + +Media Attachments are ubiquitous in the Fediverse. My quick investigation +into the explore tab on `mastodon.social` yields that about half the +posts contain an image attachment. The mechanism for these is poorly +documented. For example, it is not mentioned in [ActivityPub][]. + +My goal in this FEP is to document current usage, and issue recommendations +on how to improve it. These recommendations are based on the +support table _Recommended Media Attachment Format_ +available at [FunFedi.dev][FunFedi]. + +For developers that enjoy making their keyboards smoke, I believe +that the above link combined with the content of [Testing](#testing) +should be enough to adapt their Fediverse applications. +The other parts are meant for people, who which to improve +the situation related to media attachments. + +## Basic format + +We will discuss our basic suggested format with the following +example of an image attachment. + +```json +{ + "type": "Image", + "name": "A beautiful cow", + "url": "http://pasture-one-actor/assets/cow.jpg", + "width": 100, + "height": 162, + "mediaType": "image/jpeg", + "digestMultibase": "zQmaeDPzhNL32WQZnnzB1H6QJWvvFNEHdViDB71yrxyXU1t", + "size": 9045 +} +``` + +There is a lot to say here, first how does this relate to communication +in [ActivityPub][] which is done by activity. For this consider the +activity (taken from [data.funfedi.dev][data1]) given by + +```json +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://www.w3.org/ns/credentials/v2", + { + "size": "https://joinpeertube.org/ns#size" + } + ], + "type": "Create", + "actor": "http://pasture-one-actor/actor", + "to": [ + "http://akkoma/users/witch", + "https://www.w3.org/ns/activitystreams#Public" + ], + "id": "http://pasture-one-actor/actor/S5Szzuugy50", + "published": "2024-12-05T08:18:48Z", + "object": { + "type": "Note", + "attributedTo": "http://pasture-one-actor/actor", + "to": [ + "https://www.w3.org/ns/activitystreams#Public", + "http://akkoma/users/witch" + ], + "id": "http://pasture-one-actor/actor/qDqgbPpNQPw", + "published": "2024-12-05T08:18:48Z", + "content": "Recommended Image Format", + "attachment": [ + { + "type": "Image", + "name": "A beautiful cow", + "url": "http://pasture-one-actor/assets/cow.jpg", + "width": 100, + "height": 162, + "mediaType": "image/jpeg", + "digestMultibase": "zQmaeDPzhNL32WQZnnzB1H6QJWvvFNEHdViDB71yrxyXU1t", + "size": 9045 + } + ] + } +} +``` + +We note that the media attachment is contained in the array of +`attachment` of the `Note` object. Furthermore, in difference +to the activity and the object, there is no `id` property +nor `actor` or `attributedTo` property. This is on purpose, +as those are inherited from the object the media attachment +is attached to. See [Content Licensing](#content-licensing) +for discussion about not having an `attributedTo` property. + +In particular, one should emphasize that a media attachment +not having an `id` is useful to signify that it is not useful +as an object without the note, it is attached to. + +### Specifying basic properties + +To use media attachments, an object MUST have an `attachment` +property, whose value is an array containing objects. +Furthermore, the contained objects MUST have a type +property. For it to be a media attachment the type +property MUST be `Audio`, `Image`, or `Video`. However, +the attachment property MAY contain other form +of attachments, e.g. [FEP-0ea0: Payment Links][0ea0]. + +This can be expressed as the json-schema: + +```json +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "attachment": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "examples": ["Audio", "Image", "Video"] + } + }, + "required": ["type"] + } + } + }, + "required": ["attachment"] +} +``` + +We now discuss the specific form a media attachment. +In addition to `type`, a media attachment MUST also +contain an `url` property providing the link +to the media. The `url` property MAY also be +an array, see [Multiple Media Versions](#multiple-media-versions). +However, this just to be future proof. + +Furthermore, media attachments SHOULD contain +a `name` property providing an alternative plain text +description of the media object. + +Again this can be represented as a json-schema. + +```json +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["Audio", "Image", "Video"] + }, + "name": { + "type": "string", + "examples": ["A beautiful cow"] + }, + "url": { + "type": ["string", "array"], + "examples": ["http://you.example/image.png"] + } + }, + "required": ["type", "url"] +} +``` + +### Current state of type + +Many current implementations use `Document` for media attachments. +This has the distinct disadvantage to using `Audio`, `Image`, +or `Video` that the parser needs to inspect more content than +`type` to discover what type of attachment it is. + +Let's write a bit of python to determine if something is +a media attachment based on `mediaType`, discussed later. + +```python +def is_media(attachment:dict) -> bool: + media_type = attachment.get("mediaType") + if media_type is None: + ... # handle error case + + main_type, _ = media_type.split("/", 1) + + return main_type in ["audio", "image", "video"] +``` + +This already is fairly complicated, but there is more. If +one allows `url` to be an array, one needs a different check, +so it turns into something like: + +```python +def is_media(attachment:dict) -> bool: + url = attachment.get("url") + if isinstance(url, list): + return is_media(url[0]) + + media_type = attachment.get("mediaType") + if media_type is None: + ... # handle error case + + main_type, _ = media_type.split("/", 1) + + return main_type in ["audio", "image", "video"] +``` + +There are more exceptions and poor configuration to treat, +e.g. url could be an empty list, or the implementation +could attach `mediaType` to the full object instead +of the `Link`. + +The goal of a specification needs to be to simplify the code +that needs to be written, so we insist on people using +the types `Audio`, `Image`, and `Video` for media +attachments. + +## Properties of the linked file + +In this section, we discuss properties related to the linked file. The linked file is retrieved either by performing a `GET` request on the value of `url` or on `href` of the `Link` objects if `url` is an array. Just to mention it, this requirement might change, once [Authentication and Authorization](#authentication-and-authorization) is dealt with. + +Let's look back at our original example of a media attachment + +```json +{ + "type": "Image", + "name": "A beautiful cow", + "url": "http://pasture-one-actor/assets/cow.jpg", + "width": 100, + "height": 162, + "mediaType": "image/jpeg", + "digestMultibase": "zQmaeDPzhNL32WQZnnzB1H6QJWvvFNEHdViDB71yrxyXU1t", + "size": 9045 +} +``` + +Here the linked file is given by a `GET` on `http://pasture-one-actor/assets/cow.jpg` and the result would be + +![A beautiful cow](./cow.jpg) + +The properties `mediaType`, `digestMultibase`, and `size` could +be valid for any attached file, even a non media one, e.g. +a text document. Let's quickly review them. [mediaType](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-mediatype) is defined +in the [ActivityStreams Vocabulary][ASC]. It describes the MIME type +and tells us important information on how to render the file. + +`digestMultibase` is defined [here](https://w3c.github.io/vc-data-integrity/#resource-integrity) as part +of [Verifiable Credential Data Integrity][VC]. The +encoding of a digest in multibase with [multihash](https://www.multiformats.io/multihash/) is somewhat different to the rest of multicodec, +because one first has a byte to indicate the format, then another one +to indicate the length. This means in particular that all +`digestMultibase` using `sha-256` will start with `zQm`, the `z` indicating +base58 encoding. Checking the digest is important to ensure __integrity__. +As media is often hosted off site using S3, this seems important. +For another usage see [Content Addressed Storage](#content-addressed-storage). + +Finally `size` being the file size in bytes is borrowed from PeerTube. +The `size` should tell us if we want to preload the media or not. + +There is something missing in the file properties: _access control_, see +the section [Authentication and Authorization](#authentication-and-authorization) +in the open questions below. + +### Specifying file properties + +The creator of a media attachment SHOULD include the +values of `mediaType`, `digestMultibase`, and `size`. The +consumer of a media attachment SHOULD ensure integrity +of the downloaded attachment based on `digestMultibase`, +i.e. check the digest. The consumer of a media attachment +SHOuLD decide based on `size` and `mediaType` the best way +to consume the attachment. + +`size` and `mediaType` become more relevant when +[multiple versions of the media attachment](#multiple-media-versions) +are provided. For example, this could mean that in one +feeds one only sees the low quality video by default. + +## Properties of an image + +We have now discussed all properties of our example document +except for `width` and `height`. These properties are only +relevant for an image and a video, but not for audio. Similarly, audio and video can have a `duration`, which images don't. Finally, Mastodon has introduced the additional properties + +- [`focalPoint`](https://docs.joinmastodon.org/spec/activitypub/#focalPoint) +- [`blurHash`](https://docs.joinmastodon.org/spec/activitypub/#blurhash) + +where at least `focalPoint` is user defined. There are a lot +of other properties one can consider for media, e.g. + +- Where was the picture taken? e.g. `location` +- What is the frame rate of the video? e.g. `fps` +- Provide an album cover for audio? + +In order to standardize these things further work is needed. + +## Multiple Media Versions + +As it is currently not supported in the Fediverse, +I will just give the basic example how to use multiple +attached Links: + +```json +{ + "type": "Video", + "name": "A beautiful cow eating", + "url": [ + { + "type": "Link", + "size": 54373, + "digest": "zQmSzK5qEe5tpjwGMhmjx9RvVoPkWhEmCwxP2s7wPMpKMoK", + "width": 256, + "height": 144, + "href": "http://pasture-one-actor/assets/cow_eating.mp4", + "mediaType": "video/mp4" + }, + { + "type": "Link", + "size": 2271723, + "digest": "zQme2X4rgWuRdmAtGGMSEbdoeRQ2NAL2VptcdRGTYDZbSKG", + "width": 1920, + "height": 1080, + "href": "http://pasture-one-actor/assets/cow_eating_hd.mp4", + "mediaType": "video/mp4" + } + ], + "duration": "PT3S" +} +``` + +As the example shows, this is useful to attach both a low quality +version (54kb) and a high quality one (2.2MB) of a video. + +We think that supporting this will open the door for richer +applications. + +## Testing + +By using json-schema, one can validate some level of correctness +of generated media attachments. Relevant schemas +are available at [Fediverse schemas][schemas] for media +attachments. They can be combined into a feature test +using Gherkin, see [Media Format][MF]. + +If you wish to validate everything, including digest, +you can use the examples provided at [FunFedi.dev][FunFedi]. + +## Open Question + +This section is essentially a todo list for the community +on stuff that should be fixed, but isn't yet. + +### Content Licensing + +The picture in the examples was created based on [this picture](https://pixabay.com/photos/highland-cow-cow-cattle-animal-8678950/) available for +free on pixabay by photographer [derekmuller][derekmuller]. +Unfortunately, the current standards to not let me attach +this information to my media object. + +One could now say that this could be solved with just +using the `attributedTo` property. Unfortunately, this +has a lot of drawbacks. For example [derekmuller][derekmuller] +is not an ActivityPub actor. Also attributing my cropped +low resolution image to him, might be something he does not appreciate. +Finally, just attributing this image is probably not enough, +one should also inform people on how it is licensed. + +See [FEP-c118][c118] and its discussions for more on the topic. + +### Authentication and Authorization + +Currently, image links must be accessible without any +form of authorization. This is due while communication +between a user and their server requiring authentication +and between servers requiring authentication, images +are often stored on third party services, e.g. S3, +thus adding authentication is hard. + +For some approaches to resolve this see [this Fediverse discussion][silverpill1]. + +One approach to achieve authentication and authorization +easily with existing technologies would be [Bearcaps][bearcaps]. + +For a different approach see also [Binary Fediverse transport](#binary-fediverse-transport). + +### Content addressed storage + +Storing media is costly. It is thus important to avoid +duplication. By having a digest for all media through +the `digestMultibase` property, we can use this to +index our media storage. This means that before downloading +a file, we can check if we already have it. + +### Mixed media content + +Consider posting a song, e.g. something from the brat summer, then +you might want to attach the album cover, e.g. an image featuring +the color `#8ACE00`. Maybe you will also want to attach some +lyrics. This means that your media content contains three parts +of separate media type. + +One might want to extend the schema for media attachments to convey +this information. + +### Binary Fediverse transport + +A failure of [ActivityPub][] is that it restricts transport to +be JSON. ActivityPub thus forces people to use external means, e.g. +download the file, to convey media content. + +One could solve some problems, e.g. +[Authentication and Authorization](#authentication-and-authorization), +by just allowing transport of binary blobs. This would require +an extension of the wire format. + +Allowing for messages to contain binary blobs would also for +sharing media via thick clients. + +## References + +- Christine Lemmer Webber, Jessica Tallon, [ActivityPub][ActivityPub], 2018 +- Helge, [FunFedi.dev: Recommended Media Attachment Format][FunFedi] +- Helge / funfedi.dev [Interoperability Data for the Fediverse][data1], akkoma v3.13.3, inputs 0.1.11, Recommended Media Attachment Format, example 1 +- Tim Bray, [FEP-c118: Content licensing support][c118] +- [Pixabay user derekmuller][derekmuller] +- silverpill, [FEP-0ea0: Payment Links][0ea0] +- Helge, [Feature: Media Attachments][MF] part of fediverse-features +- Helge, [Schemas for the Fediverse][schemas] +- Mastodon, [Bearcaps][bearcaps] + +## Posts + +- [Social Media Post][silverpill1] by silverpill et al. Relevant parts: + +> Apparently Streams has some mechanism for protecting attachments. Media URLs in non-public posts look like this: +> https://{domain}/photo/{filename}.jpg?token={token} +> +> IIRR at least in Hubzilla that token is just part of OpenWebAuth's "magic authentication". Where I guess the token contains info about which instance(s?) to contact in order to verify your identity. The audience is kept in the media server(s) database and sync'ed between clones. + +[ActivityPub]: https://www.w3.org/TR/activitypub/ +[FunFedi]: https://funfedi.dev/support_tables/generated/recommended_attachments/ +[data1]: https://data.funfedi.dev/0.1.11/akkoma__v3.13.3/recommended_attachments +[c118]: https://codeberg.org/fediverse/fep/src/branch/main/fep/c118/fep-c118.md +[derekmuller]: https://pixabay.com/users/derekmuller-1225161/ +[silverpill1]: https://mitra.social/post/01939c02-dedc-dc4c-0431-a5fe7925b337 +[0ea0]: https://codeberg.org/fediverse/fep/src/branch/main/fep/0ea0/fep-0ea0.md +[ASC]: https://www.w3.org/TR/activitystreams-vocabulary/ +[VC]: https://w3c.github.io/vc-data-integrity/ +[MF]: https://codeberg.org/helge/fediverse-features/src/branch/main/features/schema/schema_media.feature +[schemas]: https://schemas.funfedi.dev/ +[bearcaps]: https://docs.joinmastodon.org/spec/bearcaps/ + +## Copyright + +CC0 1.0 Universal (CC0 1.0) Public Domain Dedication + +To the extent possible under law, the authors of this Fediverse Enhancement Proposal have waived all copyright and related or neighboring rights to this work.