SocialCG/ActivityPub/MediaUpload

From W3C Wiki

The Media Upload mechanism defines a protocol for ActivityPub servers to support uploading document types to be referenced in activites, such as images, video or other binary data. To accomplish this, a client MUST submit a multipart/form-data message to the user's uploadMedia endpoint on their ActivityStreams profile object. (Unlike most client to server interactions, using this endpoint does not involve submitting to the outbox). A client should expect that it must be properly authenticated in order to be able to upload media.

The uploadMedia endpoint is part of the ActivityPub endpoints mapping. See ActivityPub extensions.

The submitted form data should contain two parts / fields:

file
The media file file being uploaded.
object
A shell of an ActivityStreams object, which will be finalized by the server.

Assuming that the server accepts the request and that the user was appropriately authorized to upload media, servers MUST respond with a 201 Created if the object is immediately available or a 202 Accepted if the server is still processing the submitted media. The response MUST contain a Location header pointing to the new or to-be-created object's id.

The server, having done any appropriate processing on the received file and putting it in place, transforms the object that will be retrievable by the id. In particular, servers MUST append an id property to the object, and SHOULD include the uploaded and/or processed file paths in the object's url property. The server MAY wrap the shell object submitted by the user in a Create via [[#object-without-create|]] if appropriate and present this as the object pointed to by the forementioned Location header in the post-media-upload response.

Ben Bitdiddle just recorded a video of his friend's latest invention and wants to upload it to his ActivityPub-compatible social network to show it off. He wants to post a Create of a new Video object, but first he has to upload the video somewhere. Luckily the ActivityPub instance he is on supports the uploadMedia endpoint! In his client, he selects the video for upload and adds a title and a description and presses submit.

Ben's client takes a look at Ben's actor profile and finds the uploadMedia endpoint there.

            {"@context": "https://www.w3.org/ns/activitystreams",
             "type": "Person",
             "id": "https://chatty.example/ben/",
             "name": "Ben Bitdiddle",
             "preferredUsername": "ben",
             "summary": "Constantly fidgeting with ones and zeroes.",
             "inbox": "https://chatty.example/ben/inbox/",
             "outbox": "https://chatty.example/ben/collections/outbox/",
             "followers": "https://chatty.example/ben/collections/followers/",
             "following": "https://chatty.example/ben/collections/following/",
             "liked": "https://chatty.example/ben/collections/liked/",
             "endpoints": {
               "id": "https://chatty.example/ben/#endpoints",
               "uploadMedia": "https://chatty.example/ben/endpoints/uploadMedia/"}}
          

Ben's client then constructs a shell ActivityPub object (this doesn't have the url for the video itself yet, but that's okay, because that will be added by the server) and submits it (as the object) along with the video (as the file) by POST'ing to the uploadMedia endpoint.

            POST /ben/endpoints/uploadMedia/ HTTP/1.1
            Host: chatty.example
            Content-Type: multipart/form-data; boundary=bfe40faf4ef
            Authorization: Bearer XXXXXXXXXXX
  
            --bfe40faf4ef
            Content-Disposition: form-data; name="object"
            Content-Type: application/ld+json; profile="https://www.w3.org/ns/activitystreams"
  
            {"@context": "https://www.w3.org/ns/activitystreams",
             "type": "Create",
             "actor": "https://chatty.example/ben/",
             "to": ["https://chatty.example/ben/collections/followers/",
                    "https://www.w3.org/ns/activitystreams#Public"],
             "object": {
               "type": "Video",
               "name": "A wireworld computer!",
               "attributedTo": "https://chatty.example/ben/",
               "to": ["https://chatty.example/ben/collections/followers/",
                      "https://www.w3.org/ns/activitystreams#Public"],
               "summary": "<p>My friend Conway and I went over to our friend
                              Silverman's place and saw the wireworld computer
                              he's been constructing.  Pretty crazy!</p>
                           <p>Tomorrow we're going over to our friend Langton's
                              place and see that ant farm he's been working
                              on...</p>"}}
  
            --bfe40faf4ef
            Content-Disposition: form-data; name="file"; filename="wireworld.webm"
            Content-Type: video/webm
  
            ... (binary data) ...
            --bfe40faf4ef
          

Ben's client receives an HTTP response from the server with the status code 202 Accepted since the video is still being transcoded. The response contains a Location header pointing at the url https://chatty.example/ben/posts/d4152a7c/. Sure enough, a few minutes later Ben's video shows up in his outbox with the rest of the fields filled in:

            {"@context": "https://www.w3.org/ns/activitystreams",
             "type": "Create",
             "id": "https://chatty.example/ben/posts/d4152a7c/",
             "actor": "https://chatty.example/ben/",
             "to": ["https://chatty.example/ben/collections/followers/",
                    "https://www.w3.org/ns/activitystreams#Public"],
             "object": {
               "type": "Video",
               "id": "https://chatty.example/ben/posts/6e69f564/",
               "url": [
                 {"type": "Link",
                  "href": "https://chatty.example/media/6e69f564/wireworld-small.webm",
                  "mediaType": "video/webm; codecs=\"vp8, vorbis\"",
                  "name": "A wireworld computer! (small)",
                  "width": 640,
                  "height": 480},
                 {"type": "Link",
                  "href": "https://chatty.example/media/6e69f564/wireworld-large.webm",
                  "mediaType": "video/webm; codecs=\"vp8, vorbis\"",
                  "name": "A wireworld computer! (large)",
                  "width": 1280,
                  "height": 720},
               "name": "A wireworld computer!",
               "attributedTo": "https://chatty.example/ben/",
               "to": ["https://chatty.example/ben/collections/followers/",
                      "https://www.w3.org/ns/activitystreams#Public"],
               "summary": "<p>My friend Conway and I went over to our friend
                              Silverman's place and saw the wireworld computer
                              he's been constructing.  Pretty crazy!</p>
                           <p>Tomorrow we're going over to our friend Langton's
                              place and see that ant farm he's been working
                              on...</p>"}}