# Media Support

> \[!CAUTION]
>
> Programmable Chat has been deprecated and is no longer supported. Instead, we'll be focusing on the next generation of chat: Twilio Conversations. Find out more about the [EOL process here](https://www.twilio.com/en-us/changelog/programmable-chat-end-of-life-notice).
>
> If you're starting a new project, please visit the [Conversations Docs](/docs/conversations) to begin. If you've already built on Programmable Chat, please visit our [Migration Guide](/docs/conversations/migrating-chat-conversations) to learn about how to switch.

Programmable Chat supports media messages, allowing your users to add photos, video or any other type of file to their chat. A media message appears on the channel similar to a text only message but with additional properties that let your client know the media's size as well as an optional content type and default filename. When creating your media message, you will pass media to the Programmable Chat SDK which will be read and saved to your Chat instance. Your application can later request a stream to retrieve the media associated with a message and display it to your users.

## Using Media Messaging via the client SDKs (iOS, Android and JavaScript)

A typical media message creation will include the following steps with the details depending on your client platform:

* Create a new message, passing in a stream to the media and its mime content type. (The current maximum size of media accepted is 150mb.)
* Optionally specify a default download filename to help your application display the media to other users.
* Programmable Chat will provide you feedback of upload progress as well as an indication your media has been successfully saved. (iOS and Android only)
* The message is created in the specified channel.

On the receiving side of a media message, you will:

* Receive a chat message that includes a media SID.
* When you're ready to display the media to your user, you'll obtain the download URL
* Your client will display or otherwise make available to the user the message's media content.

Media on messages within Programmable Chat are attachments which live separately from your chat message. They are associated to the owning message by a media SID. Media files cannot exist without their owning message, and deletion of a message will result in cleanup of its associated media. Media files are immutable once created, you can modify other supported attributes of a message that has media content but the media itself is not changeable. Media messages do not contain a per-message text portion, instead being populated by the service with a placeholder message for legacy clients without media attachment support - this placeholder message is developer definable on the service instance.

Most developers will want to implement media caching to avoid unnecessary battery or network consumption should the application need to view the same media multiple times. An example of such a caching mechanism can be found in the helper classes of the Chat demo applications published to GitHub. It is also a good idea to prevent concurrent downloads of the same media in your clients, the SDK also handles queueing up requests for the same media until a single download completes before returning the requested media object.

### Required Role Permission

For your users to create messages with media content, their Role must contain the `sendMediaMessage` and `sendMessage` permissions. On new Chat instances this is in the default channel roles, but must be added to existing instances if you wish to support it. For an example on how to apply this permission to your Channel User and Admin roles, see [Update a Role](/docs/chat/rest/role-resource#update-a-role-resource) or [Roles and Permissions](/docs/chat/permissions) for more general information on Roles in Programmable Chat.

### Downloading Media

On the receiver side, media is exposed with help of temporary URL. Only authenticated and authorized users can request the URL. The URL is valid for 300 seconds. You can request a new temporary URL at any point of time.

### Platform Differences for Media

The media creation methods within the client SDK's take a stream or file as a parameter. How the media is expressed in the client SDK will depend on your platform:

#### JavaScript

For JavaScript, you can provide a FormData containing file information (including file name, content type,
size and all FormData provided information), a String or a Node.js Buffer containing media byte stream to be used as the source for a new media message.

#### iOS

Media files are uploaded by providing an NSInputStream compliant stream to the TCHMessageOptions for your new message.

#### Android

For Android, any java.io.InputStream compliant stream can be used as the source for a new media message.

### Creating a Media Message

Creating a media enriched message is very similar to creating a new text-only message - you start by creating a message as usual but instead of body for the content, you provide media for the message:

Creating a Media Message

```js
// example for sending media message as FormData
// ---------------------------------------------
const formData = new FormData();
formData.append('file', $('#formInputFile')[0].files[0]);
// get desired channel (for example, with getChannelBySid promise)
chatClient.getChannelBySid(channelSid).then(function(channel) {
  // send media with all FormData parsed atrtibutes
  channel.sendMessage(formData);
});

// example for sending media message as String
// -------------------------------------------
// get desired channel (for example, with getChannelBySid promise)
chatClient.getChannelBySid(channelSid).then(function(channel) {
  // send SVG image as string with content type image/svg+xml; charset=utf-8
  channel.sendMessage({
    contentType: 'image/svg+xml; charset=utf-8',
    media:
      '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">' +
      '<path d="M50,3l12,36h38l-30,22l11,36l-31-21l-31,21l11-36l-30-22h38z"' +
      ' fill="#FF0" stroke="#FC0" stroke-width="2"/></svg>',
  });
});

// example for sending media message as Buffer
// -------------------------------------------
// get desired channel (for example, with getChannelBySid promise)
chatClient.getChannelBySid(channelSid).then(function(channel) {
  // send PNG image as Buffer with content type image/png
  channel.sendMessage({
    contentType: 'image/png',
    media: fs.readFileSync(pngFile),
  });
});
```

```java
// Messages messagesObject;
messagesObject.sendMessage(
    Message.options()
        .withMedia(new FileInputStream("/path/to/Somefile.txt"), "text/plain")
        .withMediaFileName("file.txt")
        .withMediaProgressListener(new ProgressListener() {
            @Override
            public void onStarted() {
                Timber.d("Upload started");
            }

            @Override
            public void onProgress(long bytes) {
                Timber.d("Uploaded " + bytes + " bytes");
            }

            @Override
            public void onCompleted(String mediaSid) {
                Timber.d("Upload completed");
            }
        }),
    new CallbackListener<Message>() {
        @Override
        public void onSuccess(Message msg) {
            Timber.d("Successfully sent MEDIA message");
        }

        @Override
        public void onError(ErrorInfo error) {
            Timber.e("Error sending MEDIA message");
        }
    });
```

```objective-c
// The data for the image you would like to send
NSData *data = nil;

// Prepare the message
TCHMessageBuilder *messageBuilder = [self.conversation prepareMessage];

TCHMediaListener *mediaListener = [[TCHMediaListener alloc] initWithOnStarted:^{
    // Called when upload of media begins.
    NSLog(@"Media upload started");
} onProgress^(NSUInteger bytes) {
    // Called as upload progresses, with the current byte count.
    NSLog(@"Media upload progress: %ld", bytes);
} onCompleted:^(NSString * _Nonnull mediaSid) {
    // Called when upload is completed, with the new mediaSid if successful.
    NSLog(@"Media upload completed");
} onError:^(TCHError *error) {
    // Error details in case upload has failed.
    NSLog(@"Media upload failed with error: %@", error);
}];

[messageBuilder addMediaWithData:data contentType:@"image/jpeg" filename:@"image.jpg" listener:mediaListener];

// Trigger the sending of the message.
[messageBuilder buildAndSendWithCompletion:^(TCHResult *result, TCHMessage *message) {
    if (!result.isSuccessful) {
       NSLog(@"Creation failed: %@", result.error);
    } else {
       NSLog(@"Creation successful");
    }
}];
```

```swift
// The data for the image you would like to send
let data = Data()

// Prepare the message and send it
self.conversation.prepareMessage
    .addMedia(data: data, contentType: "image/jpeg", filename: "image.jpg", listener: .init(onStarted: {
        // Called when upload of media begins.
        print("Media upload started")
    }, onProgress: { bytes in
        // Called as upload progresses, with the current byte count.
        print("Media upload progress: \(bytes)")
    }, onCompleted: { sid in
        // Called when upload is completed, with the new mediaSid if successful.
        // Full failure details will be provided through sendMessage's completion.
        print("Media upload completed")
    }, onFailed: { error in
        // Called when upload is completed, with the new mediaSid if successful.
        // Full failure details will be provided through sendMessage's completion.
        print("Media upload failed with error: \(error)")
    }))
    .buildAndSend { result, message in
        if !result.isSuccessful {
            print("Creation failed: \(String(describing: result.error))")
        } else {
            print("Creation successful")
        }
    }
```

### Checking for Media Content

Only messages which contain media will have a media SID associated with them. The code sample provided here shows how to detect a media message on each platform:

Checking for Media Content

```js
// get desired channel (for example, with getChannelBySid promise)
chatClient.getChannelBySid(channelSid).then(function(channel) {
  // get channel's messages paginator
  channel.getMessages().then(function(messagesPaginator) {
    // check the first message type
    const message = messagesPaginator.items[0];
    if (message.type === 'media') {
      console.log('Message is media message');
      // log media properties
      console.log('Media properties', message.media);
    }
  });
});
```

```java
if (message.hasMedia()) {
    Message.Media media = message.getMedia();
}
```

```objective-c
if (!message.attachedMedia.isEmpty) {
    for (TCHMedia *media in message.attachedMedia) {
        NSLog(@"media filename: %@ (optional)", media.filename);
        NSLog(@"media size: %ld", media.size);
    }
}
```

```swift
if (!message.attachedMedia.isEmpty) {
    for media in message.attachedMedia {
        print("media filename: \(String(describing: media.filename)) (optional)")
        print("media size: \(media.size)")
    }
}
```

### Retrieving Message Media Content

It's important to note that at this time, Programmable Chat does not maintain an internal cache of media so you are encouraged to cache downloaded media in your application to avoid unnecessary downloads. It is also important for best performance to only allow a particular media file to be downloaded one at a time to avoid unnecessary battery and network consumption.

Retrieving Message Media Content

```js
// get desired channel (for example, with getChannelBySid promise)
chatClient.getChannelBySid(channelSid).then(function(channel) {
  // get channel's messages paginator
  channel.getMessages().then(function(messagesPaginator) {
    // check the first message type
    const message = messagesPaginator.items[0];
    if (message.type === 'media') {
      console.log('Message is media message');
      // log media properties
      console.log('Media attributes', message.media);
      // get media temporary URL for displaying/fetching
      message.media.getContentTemporaryUrl().then(function(url) {
        // log media temporary URL
        console.log('Media temporary URL is ' + url);
      });
    }
  });
});
```

```java
if (message.hasMedia()) {
    message.getMediaContentTemporaryUrl(new CallbackListener<String>() {
        @Override
        public void onSuccess(String mediaContentUrl) {
            Log.d("TAG", mediaContentUrl);
        }
    });
}
```

```objective-c
[conversationsClient getTemporaryContentUrlsForMedia:message.attachedMedia completion:^(TCHResult *result, NSDictionary *mediaSidToUrlMap) {
    if (!result.isSuccessful) {
        NSLog("Cannot get temporary urls with error: %@", result.error);
        return;
    }

    for (NSString *sid in mediaSidToUrlMap) {
        NSLog(@"%@ -> %@", sid, mediaSidToUrlMap[sid]);
    }
}];
```

```swift
conversationsClient.getTemporaryContentUrlsForMedia(message.attachedMedia) { result, mediaSidToUrlMap in
    guard result.isSuccessful else {
        print("Couldn't get temporary urls with error: \(String(describing: result.error))")
        return
    }

    for (sid, url) in sidToUrlMap {
        print("\(sid) -> \(url)")
    }
}
```

## Using Media Messaging via the Chat REST API

Your backend services can also use the Media feature off Chat - uploading new files for attachment with Messages, and of course sending Messages with Media attached. This section provides a brief overview of typical Media operations and flows. For a more detailed API description, please refer to [Chat Media REST API](/docs/chat/rest/media) documentation.

At present the underlying Media REST endpoint which is used to create (upload) the media (files) is a separate endpoint and not supported in the Twilio SDKs.

### Sending a Media Message

Sending a Media Message via REST is a 2 step process:

1. Upload media to MCS
2. Send Media Message to chat channel (linking/attaching the Media created in step 1 to the new Message)

Uploading media should be done directly from the source machine, using native HTTP facilities. Using cURL, the equivalent request looks like this:

```bash
curl -u "<acount_sid>:<account_secret>" --data-binary "@<filename>" https://mcs.us1.twilio.com/v1/Services/<chat_service_sid>/Media
```

That will leave your media on Twilio servers, ready to be bound to a Chat Message by the returned SID. The [REST Message endpoint documentation](/docs/chat/rest/message-resource) is a good general description for sending Chat messages; the `MediaSid` parameter accepts the SID from the media upload and binds that media to the message.

```bash
curl -u "<acount_sid>:<account_secret>" -X POST https://chat.twilio.com/v2/Services/<chat_service_sid>/Channels/<channel_sid>/Messages -d MediaSid=<media_sid>
```

Now that you're ready to enable your users to share the finest cute animal photos the internet has to offer, let's learn about [Result Paging in Chat](/docs/chat/result-paging).
