Sending attachments¶
Sometimes, you want to upload attachments in your chat. Be that images, videos, or other kinds of files. NioBot supports this, and it's very easy to do.
Before you start¶
In order to use the majority of the features in this guide, you will need to install ffmpeg
and imagemagick
.
These are used for thumbnail generation, and metadata detection.
You should use your package manager to install these, as they are not python packages.
FAQ¶
Why do I need to install ffmpeg
and imagemagick
?
imagemagick
is actually optional - if you trust ffprobe
to work with all of your images (in some cases it can
fail to detect newer image formats), then you can skip installing it.
However, ffmpeg
is required for all but file attachments. This is because in order to get some rich data, such as
dimensions and duration, we need to use ffprobe
to get this data.
Furthermore, in the event imagemagick
is not installed, the metadata fetcher falls back to ffprobe
.
Not having these installed will result in a RuntimeError
being raised when you try to send an attachment
when it tries to fetch metadata. This is because the metadata fetcher will not be able to find ffprobe
or
imagemagick
in your PATH.
Why does it take a couple of seconds for <attachment>.from_file()
to return?
The from_file
method (see: niobot.VideoAttachment.from_file, niobot.ImageAttachment.from_file, etc.)
does a lot of heavy lifting in terms of preparing a file with all the bells and whistles for an upload.
This means that it has to do a lot
of processing, which may take a couple of seconds to return.
Sending:¶
Regular files¶
Here, regular files can be anything that isn't a video, image, or audio file. This includes text files, PDFs, etc. You can even send binary or pre-encrypted (why?) files if you want to.
Regular files are the simplest file type in niobot, in terms of code complexity and also features. Regular files do not support:
- Thumbnails
- Rich data
- Previews
All you get from thumbnails is the file name, and the file size. That's it.
Anyway, here's how you could send an example text (foo.txt) file:
from niobot import NioBot, Context, FileAttachment
...
@bot.comand(name="upload.txt")
async def upload_txt(ctx: Context):
"""Sends a text file!"""
attachment = await FileAttachment.from_file("file.txt")
await ctx.respond(file=attachment)
This results in the following:
You can then click on the file to download it!
Images¶
Images are a bit more complex than regular files. They support thumbnails, rich data, and previews.
Thumbnails for images
While you may think that thumbnails for images are useless, they are actually very useful for clients. Just beware though, having a larger or equal size image for your thumbnail is very counter-productive.
A valid use case for image thumbnails is for lower-resolution, likely compressed versions of the image you're sending. Paired with a blurhash, this can provide a very good "placeholder" image for people on painfully slow connections.
For your convenience, unless disabled, niobot will automatically generate a "blurhash" for your image.
A blurhash is very good for providing a "placeholder" image, as it is generated by a string of around 30 characters. This means people on super slow connections can see a pretty preview of the image (without much detail), instead of having an ugly loading spinner or outright blank space in place of a loading image.
For example:
This may slow down your image upload
Generating blurhashes, especially for large images, even more especially with a weak CPU, can be very slow. While this will not block your code execution, it means you must wait for the blurhash to be generated before you can do anything with the image.
You may want to disable this behaviour. See disabling extra media features.
And here's an example:
from niobot import NioBot, Context, ImageAttachment
...
@bot.comand(name="upload.png")
async def upload_png(ctx: Context):
"""Sends a png image!"""
attachment = await ImageAttachment.from_file("file.png")
await ctx.respond(file=attachment)
Audio¶
Audio files are actually simpler than images, however they do not support thumbnails or rich data outside of their duration.
Beware your codec!
You should aim to have your audio files as opus
, vorbis
, aac
, flac
, or mp3
encoded files, as some clients
may not be able to play other formats.
Also be mindful of their containers, since some (such as mkv
) won't play in some clients.
Here's an example:
from niobot import NioBot, Context, AudioAttachment
...
@bot.comand(name="upload.mp3")
async def upload_mp3(ctx: Context):
"""Sends a mp3 audio file!"""
attachment = await AudioAttachment.from_file("file.mp3")
await ctx.respond(file=attachment)
Videos¶
Videos are the most complex file type in niobot. They support thumbnails, rich data, and previews.
Again though, NioBot makes this easy. All you need to do is pass a video file to VideoAttachment.from_file()
.
The same warnings apply as images, except for the blurhash. Blurhashes are not generated for videos. However, thumbnails are generated by default, with their own blurhashes. For simplicity, the video's auto-generated thumbnail is simply the first frame of the video.
Beware of your codec(s)!
A lot of matrix clients at the moment are simple HTML5-based clients - meaning they can only play a limited set of codecs out of the box.
You should aim to keep your video codecs as h264
, vp8
, or vp9
, as these are the most widely supported.
However, some native apps may not even support vp8/vp9. Use h264/avc when in doubt.
Look at audio's warning for more information about audio codecs.
Here's an example:
from niobot import NioBot, Context, VideoAttachment
...
@bot.comand(name="upload.mp4")
async def upload_mp4(ctx: Context):
"""Sends a mp4 video!"""
attachment = await VideoAttachment.from_file("file.mp4")
await ctx.respond(file=attachment)
Unsure which to use?¶
If you aren't sure which file type you have, you can find out the most appropriate Attachment type using
niobot.attachment.which
- this will return either VideoAttachment
,
ImageAttachment
, AudioAttachment
,
or FileAttachment
, based on the mime type of the file.
Fpr example:
import random
import niobot
...
@bot.comand(name="upload")
async def upload_mp4(ctx: Context):
"""Sends a random file!"""
files = ("file.txt", "file.mp4", "file.mp3", "file.png")
file_name = random.choice(files)
attachment_type = niobot.which(file_name)
attachment = await attachment_type.from_file(file_name)
# ^ can also be written as `attachment = await niobot.which(file_name).from_file(file_name)`
await ctx.respond(file=attachment)
This will upload using the appropriate file type.
Disabling extra media features¶
Disabling blurhash generation¶
This will harm the user experience
Disabling blurhash generation is a terrible idea - unless you make sure your uploads are a matter of kilobytes, you will always see blank spots while at least a thumbnail is loaded. Please consider alternative options.
for niobot.VideoAttachment and niobot.ImageAttachment:
from niobot import NioBot, Context, ImageAttachment, VideoAttachment
...
async def foo():
attachment = await ImageAttachment.from_file("file.png", generate_blurhash=False)
# or for Videos
attachment = await VideoAttachment.from_file("file.mp4", generate_blurhash=False)
Disabling thumbnail generation¶
This will harm the user experience
If you intend to disable thumbnail generation, you should provide your own thumbnail, or at the very least leave blurhash generation enabled.
Otherwise, while your video loads, clients will most likely just show a completely transparent box, with a loading spinner at a stretch. This leaves a massive chunk of the UI completely blank while your video loads.
for niobot.VideoAttachment only:
from niobot import NioBot, Context, ImageAttachment, VideoAttachment
...
async def foo():
attachment = await VideoAttachment.from_file("file.mp4", thumbnail=False)
Disabling rich data¶
A lot of rich data fields will still require values for clients to properly render the media!
In this case, "rich data" refers to some "optional" fields in media uploads, such as height
, width
, duration
,
etc. These fields are not required for the server to accept the upload, but they are often used by clients to
figure out how to properly display the media.
"Rich data" is gathered from the get_metadata function, which itself calls ffprobe
/imagemagick
as a subprocess. If, for whatever reason, this is undesirable, you can avoid it.
Disabling rich data is not 100% possible, but you can avoid it by passing minimal values where it would automatically be filled in:
Images¶
from niobot import ImageAttachment
async def foo():
attachment = await ImageAttachment.from_file("file.png", width=0, height=0, unsafe=True)
Videos¶
from niobot import VideoAttachment
async def foo():
attachment = await VideoAttachment.from_file("file.mp4", width=0, height=0, duration=0)