Reworked file uploads to include server-generated thumbnails
This commit is contained in:
parent
46d3652fa3
commit
c1b387d3e7
@ -103,41 +103,36 @@ namespace matrix{
|
|||||||
//NOTE alias equivalents just give the uploaded file a name other than the filename.
|
//NOTE alias equivalents just give the uploaded file a name other than the filename.
|
||||||
/*
|
/*
|
||||||
* Upload a file as a raw file.
|
* Upload a file as a raw file.
|
||||||
* Takes filename as argument.
|
* Takes file data as argument.
|
||||||
* Returns: file_info struct containing the url of the file on the homeserver.
|
* Returns: file_info struct containing the url of the file on the homeserver.
|
||||||
* Note check the file_info::fileurl field to check if the upload succeeded.
|
* Note check the file_info::fileurl field to check if the upload succeeded.
|
||||||
*/
|
*/
|
||||||
file_info upload_file(const raii::string_base& filename)const;
|
file_info upload_file(const file_details& file)const;
|
||||||
file_info upload_file(const raii::string_base& filename, const raii::string_base& alias)const;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Upload a file as an image.
|
* Upload a file as an image.
|
||||||
* Note: requires FreeImagePlus to treat the file as an image, otherwise will just upload as a raw file.
|
|
||||||
* Returns: image_info struct containing the url of the image on the homeserver.
|
* Returns: image_info struct containing the url of the image on the homeserver.
|
||||||
* Note check the image_info::fileurl field to check if the upload succeeded.
|
* Note check the image_info::fileurl field to check if the upload succeeded.
|
||||||
*/
|
*/
|
||||||
image_info upload_image(const raii::string_base& filename)const;
|
image_info upload_image(const image_details& file)const;
|
||||||
image_info upload_image(const raii::string_base& filename, const raii::string_base& alias)const;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Upload a file as a video.
|
* Upload data as a video.
|
||||||
* Note: requires ffmpeg to treat the file as a video, otherwise will just upload as a raw file.
|
|
||||||
* Returns: video_info struct containing the url of the image on the homeserver.
|
* Returns: video_info struct containing the url of the image on the homeserver.
|
||||||
* Note check the video_info::fileurl field to check if the upload succeeded.
|
* Note check the video_info::fileurl field to check if the upload succeeded.
|
||||||
*/
|
*/
|
||||||
video_info upload_video(const raii::string_base& filename)const;
|
video_info upload_video(const video_details& file)const;
|
||||||
video_info upload_video(const raii::string_base& filename, const raii::string_base& alias)const;
|
|
||||||
/*
|
/*
|
||||||
* Upload a file as an audio file.
|
* Upload a file as an audio file.
|
||||||
* Note: requires ffmpeg to treat the file as a audio, otherwise will just upload as a raw file.
|
|
||||||
* Returns: audio_info struct containing the url of the image on the homeserver.
|
* Returns: audio_info struct containing the url of the image on the homeserver.
|
||||||
* Note check the audio_info::fileurl field to check if the upload succeeded.
|
* Note check the audio_info::fileurl field to check if the upload succeeded.
|
||||||
*/
|
*/
|
||||||
audio_info upload_audio(const raii::string_base& filename)const;
|
audio_info upload_audio(const audio_details& file)const;
|
||||||
audio_info upload_audio(const raii::string_base& filename, const raii::string_base& alias)const;
|
|
||||||
|
|
||||||
|
bool create_thumbnail(image_info& info)const;
|
||||||
|
bool create_thumbnail(video_info& video)const;
|
||||||
private:
|
private:
|
||||||
raii::rjp_string _upload_file(raii::filerd& fp, const raii::curl_llist& header)const;
|
file_info _upload_file(const file_details& file, const raii::curl_llist& header)const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -52,6 +52,7 @@ namespace matrix{
|
|||||||
const raii::string& whoami(void)const;
|
const raii::string& whoami(void)const;
|
||||||
const raii::string& displayname(void)const;
|
const raii::string& displayname(void)const;
|
||||||
const raii::string& profile_picture(void)const;
|
const raii::string& profile_picture(void)const;
|
||||||
|
raii::string file_thumbnail(const raii::string_base& homeserver, const raii::string_base& fileurl, int width, int height, const raii::string_base& method)const;
|
||||||
raii::string logout(const raii::string_base& homeserver, const raii::string_base& access_token)const;
|
raii::string logout(const raii::string_base& homeserver, const raii::string_base& access_token)const;
|
||||||
raii::string sync(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& next_batch, const raii::string_base& timeout)const;
|
raii::string sync(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& next_batch, const raii::string_base& timeout)const;
|
||||||
raii::string power_level(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const;
|
raii::string power_level(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const;
|
||||||
|
|||||||
@ -25,6 +25,19 @@
|
|||||||
|
|
||||||
namespace matrix{
|
namespace matrix{
|
||||||
|
|
||||||
|
struct file_details{
|
||||||
|
raii::string data;
|
||||||
|
raii::string name;
|
||||||
|
};
|
||||||
|
struct image_details : public file_details{
|
||||||
|
int width, height;
|
||||||
|
raii::string mimetype;
|
||||||
|
};
|
||||||
|
struct video_details : public image_details{};
|
||||||
|
struct audio_details : public file_details{
|
||||||
|
raii::string mimetype;
|
||||||
|
};
|
||||||
|
|
||||||
struct file_info{
|
struct file_info{
|
||||||
raii::rjp_string fileurl;
|
raii::rjp_string fileurl;
|
||||||
raii::string filename;
|
raii::string filename;
|
||||||
@ -37,6 +50,7 @@ namespace matrix{
|
|||||||
size_t height;
|
size_t height;
|
||||||
|
|
||||||
raii::rjp_string thumburl;
|
raii::rjp_string thumburl;
|
||||||
|
raii::string thumbmime;
|
||||||
size_t thumb_width;
|
size_t thumb_width;
|
||||||
size_t thumb_height;
|
size_t thumb_height;
|
||||||
size_t thumbsize;
|
size_t thumbsize;
|
||||||
|
|||||||
@ -102,6 +102,7 @@ namespace raii{
|
|||||||
void reset(char* val = nullptr);
|
void reset(char* val = nullptr);
|
||||||
//Stop managing stored pointer. Does not free.
|
//Stop managing stored pointer. Does not free.
|
||||||
char* release(void);
|
char* release(void);
|
||||||
|
bool resize(size_t newsize);
|
||||||
|
|
||||||
//Length of string not including null terminator
|
//Length of string not including null terminator
|
||||||
constexpr size_t length(void)const{return m_length;}
|
constexpr size_t length(void)const{return m_length;}
|
||||||
@ -144,6 +145,8 @@ namespace raii{
|
|||||||
string_intermediary(void) = default;
|
string_intermediary(void) = default;
|
||||||
string_intermediary(char* data, size_t len):
|
string_intermediary(char* data, size_t len):
|
||||||
string_base(data, len){}
|
string_base(data, len){}
|
||||||
|
string_intermediary(const char* data, size_t len):
|
||||||
|
string_base(reinterpret_cast<char*>(Allocator::copy(data, len+1)), len){}
|
||||||
string_intermediary(const char* data):
|
string_intermediary(const char* data):
|
||||||
string_base(strlen(data))
|
string_base(strlen(data))
|
||||||
{
|
{
|
||||||
|
|||||||
@ -88,60 +88,112 @@ namespace matrix{
|
|||||||
}
|
}
|
||||||
|
|
||||||
//upload media
|
//upload media
|
||||||
file_info client::upload_file(const raii::string_base& filename)const{
|
file_info client::upload_file(const file_details& file)const{
|
||||||
return upload_file(filename, raii::static_string());
|
return _upload_file(file, raii::curl_llist{});
|
||||||
}
|
}
|
||||||
file_info client::upload_file(const raii::string_base& filename, const raii::string_base& alias)const{
|
image_info client::upload_image(const image_details& file)const{
|
||||||
raii::filerd fd(filename);
|
|
||||||
if(!fd) return {};
|
|
||||||
|
|
||||||
return file_info{_upload_file(fd, raii::curl_llist{}), alias ? alias : filename, {}, fd.length()};
|
|
||||||
}
|
|
||||||
image_info client::upload_image(const raii::string_base& filename)const{
|
|
||||||
return upload_image(filename, raii::static_string());
|
|
||||||
}
|
|
||||||
image_info client::upload_image(const raii::string_base& filename, const raii::string_base& alias)const{
|
|
||||||
image_info ret = {};
|
image_info ret = {};
|
||||||
ret = upload_file(filename, alias);
|
raii::curl_llist headers(raii::string("Content-Type: "_ss + file.mimetype));
|
||||||
|
ret = _upload_file(file, headers);
|
||||||
|
ret.width = file.width;
|
||||||
|
ret.height = file.height;
|
||||||
|
ret.mimetype = file.mimetype;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
video_info client::upload_video(const raii::string_base& filename)const{
|
audio_info client::upload_audio(const audio_details& file)const{
|
||||||
return upload_video(filename, raii::static_string());
|
|
||||||
}
|
|
||||||
audio_info client::upload_audio(const raii::string_base& filename)const{
|
|
||||||
return upload_audio(filename, raii::static_string());
|
|
||||||
}
|
|
||||||
video_info client::upload_video(const raii::string_base& filename, const raii::string_base& alias)const{
|
|
||||||
video_info ret = {};
|
|
||||||
ret = upload_file(filename, alias);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
audio_info client::upload_audio(const raii::string_base& filename, const raii::string_base& alias)const{
|
|
||||||
audio_info ret = {};
|
audio_info ret = {};
|
||||||
ret = upload_file(filename, alias);
|
raii::curl_llist headers(raii::string("Content-Type: "_ss + file.mimetype));
|
||||||
|
ret = _upload_file(file, headers);
|
||||||
|
ret.mimetype = file.mimetype;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
video_info client::upload_video(const video_details& file)const{
|
||||||
|
video_info ret = {};
|
||||||
|
raii::curl_llist headers(raii::string("Content-Type: "_ss + file.mimetype));
|
||||||
|
ret = _upload_file(file, headers);
|
||||||
|
ret.width = file.width;
|
||||||
|
ret.height = file.height;
|
||||||
|
ret.mimetype = file.mimetype;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t _thumbnail_header_callback(char* ptr, size_t size, size_t nmemb, void* userdata){
|
||||||
|
raii::string* data = reinterpret_cast<raii::string*>(userdata);
|
||||||
|
if(size*nmemb > 13 && !strncmp("Content-Type:", ptr, 13)){
|
||||||
|
(*data) += (ptr + 13);
|
||||||
|
data->get()[data->length()-1] = 0;
|
||||||
|
}
|
||||||
|
return size*nmemb;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool client::create_thumbnail(image_info& info)const{
|
||||||
|
image_details i;
|
||||||
|
raii::string reply_header;
|
||||||
|
if(info.thumb_width > info.width || info.thumb_height > info.height){
|
||||||
|
info.thumburl = info.fileurl;
|
||||||
|
info.thumbsize = info.filesize;
|
||||||
|
info.thumbmime = info.mimetype;
|
||||||
|
}
|
||||||
|
m_curl.setopt(CURLOPT_HEADERFUNCTION, _thumbnail_header_callback);
|
||||||
|
m_curl.setopt(CURLOPT_HEADERDATA, &reply_header);
|
||||||
|
i.data = _get_curl(m_ses->urls.file_thumbnail(m_ses->homeserver, info.fileurl, info.thumb_width, info.thumb_height, "crop"_ss));
|
||||||
|
m_curl.setopt(CURLOPT_HEADERFUNCTION, NULL);
|
||||||
|
m_curl.setopt(CURLOPT_HEADERDATA, NULL);
|
||||||
|
if(!i.data){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
i.width = info.thumb_width;
|
||||||
|
i.height = info.thumb_height;
|
||||||
|
i.mimetype = std::move(reply_header);
|
||||||
|
image_info thumb_data = upload_image(i);
|
||||||
|
if(!thumb_data.fileurl)
|
||||||
|
return false;
|
||||||
|
info.thumburl = std::move(thumb_data.fileurl);
|
||||||
|
info.thumbsize = thumb_data.filesize;
|
||||||
|
info.thumbmime = std::move(thumb_data.mimetype);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool client::create_thumbnail(video_info& info)const{
|
||||||
|
return create_thumbnail(static_cast<image_info&>(info));
|
||||||
|
}
|
||||||
|
|
||||||
/*******************************
|
/*******************************
|
||||||
Internal functions
|
Internal functions
|
||||||
********************************/
|
********************************/
|
||||||
|
|
||||||
raii::rjp_string client::_upload_file(raii::filerd& fp, const raii::curl_llist& header)const{
|
struct internal_upload_data{
|
||||||
|
const char* data;
|
||||||
|
size_t len;
|
||||||
|
};
|
||||||
|
static size_t _upload_file_read_callback(char* buffer, size_t size, size_t nmemb, void* userdata){
|
||||||
|
internal_upload_data* src = reinterpret_cast<internal_upload_data*>(userdata);
|
||||||
|
size_t curl_size = size*nmemb;
|
||||||
|
size_t to_copy = std::min(curl_size, src->len);
|
||||||
|
memcpy(buffer, src->data, to_copy);
|
||||||
|
src->len -= to_copy;
|
||||||
|
src->data += to_copy;
|
||||||
|
return to_copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_info client::_upload_file(const file_details& file, const raii::curl_llist& header)const{
|
||||||
raii::string fileurl;
|
raii::string fileurl;
|
||||||
|
file_info retval = {};
|
||||||
|
internal_upload_data upload_data = {file.data.get(), file.data.length()};
|
||||||
m_curl.postreq();
|
m_curl.postreq();
|
||||||
m_curl.setopt(CURLOPT_POSTFIELDS, NULL);
|
m_curl.setopt(CURLOPT_POSTFIELDS, NULL);
|
||||||
m_curl.setopt(CURLOPT_READDATA, (void*)fp.get());
|
m_curl.setopt(CURLOPT_READFUNCTION, _upload_file_read_callback);
|
||||||
m_curl.setopt(CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)fp.length());
|
m_curl.setopt(CURLOPT_READDATA, &upload_data);
|
||||||
m_curl.setopt(CURLOPT_INFILESIZE_LARGE, (curl_off_t)fp.length());
|
m_curl.setopt(CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)upload_data.len);
|
||||||
|
m_curl.setopt(CURLOPT_INFILESIZE_LARGE, (curl_off_t)upload_data.len);
|
||||||
m_curl.seturl(m_ses->urls.file_upload());
|
m_curl.seturl(m_ses->urls.file_upload());
|
||||||
m_curl.setheader(header);
|
m_curl.setheader(header);
|
||||||
m_curl.setopt(CURLOPT_WRITEFUNCTION, _post_reply_curl_callback);
|
m_curl.setopt(CURLOPT_WRITEFUNCTION, _post_reply_curl_callback);
|
||||||
m_curl.setopt(CURLOPT_WRITEDATA, &fileurl);
|
m_curl.setopt(CURLOPT_WRITEDATA, &fileurl);
|
||||||
CURLcode cres = m_curl.perform();
|
CURLcode cres = m_curl.perform();
|
||||||
m_curl.setopt(CURLOPT_READDATA, NULL);
|
m_curl.setopt(CURLOPT_READDATA, NULL);
|
||||||
if(cres != CURLE_OK)
|
if(cres != CURLE_OK){
|
||||||
return {};
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
if(!fileurl)
|
if(!fileurl)
|
||||||
return {};
|
return {};
|
||||||
@ -150,7 +202,11 @@ namespace matrix{
|
|||||||
if(!root)
|
if(!root)
|
||||||
return {};
|
return {};
|
||||||
RJP_search_res res = rjp_search_member(root.get(), "content_uri", 0);
|
RJP_search_res res = rjp_search_member(root.get(), "content_uri", 0);
|
||||||
|
if(!res.value)
|
||||||
return res.value;
|
return {};
|
||||||
|
retval.fileurl = res.value;
|
||||||
|
retval.filename = file.name;
|
||||||
|
retval.filesize = file.data.length();
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include "matrix/client_url_list.hpp"
|
#include "matrix/client_url_list.hpp"
|
||||||
#include "raii/string.hpp"
|
#include "raii/string.hpp"
|
||||||
|
#include "raii/util.hpp"
|
||||||
|
|
||||||
namespace matrix{
|
namespace matrix{
|
||||||
|
|
||||||
@ -77,6 +78,13 @@ namespace matrix{
|
|||||||
const raii::string& client_url_list::profile_picture(void)const{
|
const raii::string& client_url_list::profile_picture(void)const{
|
||||||
return m_profile_picture;
|
return m_profile_picture;
|
||||||
}
|
}
|
||||||
|
raii::string client_url_list::file_thumbnail(const raii::string_base& homeserver, const raii::string_base& fileurl, int width, int height, const raii::string_base& method)const{
|
||||||
|
if(strncmp(fileurl.get(), "mxc://", 6))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
raii::string media(fileurl.get()+6, fileurl.length()-6);
|
||||||
|
return raii::string(s_proto + homeserver + "/_matrix/media/r0/thumbnail/" + media + "?width=" + raii::itostr(width) + "&height=" + raii::itostr(height) + "&method=" + method);
|
||||||
|
}
|
||||||
raii::string client_url_list::logout(const raii::string_base& homeserver, const raii::string_base& access_token)const{
|
raii::string client_url_list::logout(const raii::string_base& homeserver, const raii::string_base& access_token)const{
|
||||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/logout?access_token=" + access_token);
|
return raii::string(s_proto + homeserver + "/_matrix/client/r0/logout?access_token=" + access_token);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,7 +49,9 @@ namespace matrix{
|
|||||||
}
|
}
|
||||||
size_t connection::_post_reply_curl_callback(char* ptr, size_t size, size_t nmemb, void* userdata){
|
size_t connection::_post_reply_curl_callback(char* ptr, size_t size, size_t nmemb, void* userdata){
|
||||||
raii::string* data = reinterpret_cast<raii::string*>(userdata);
|
raii::string* data = reinterpret_cast<raii::string*>(userdata);
|
||||||
(*data) += ptr;
|
size_t oldlen = data->length();
|
||||||
|
data->resize(data->length() + (size*nmemb));
|
||||||
|
memcpy(data->get()+oldlen, ptr, size*nmemb);
|
||||||
return size*nmemb;
|
return size*nmemb;
|
||||||
}
|
}
|
||||||
raii::string connection::_get_curl(const raii::string_base& url)const{
|
raii::string connection::_get_curl(const raii::string_base& url)const{
|
||||||
|
|||||||
@ -51,6 +51,19 @@ namespace raii{
|
|||||||
char* string_base::release(void){
|
char* string_base::release(void){
|
||||||
return std::exchange(m_data, nullptr);
|
return std::exchange(m_data, nullptr);
|
||||||
}
|
}
|
||||||
|
bool string_base::resize(size_t newsize){
|
||||||
|
if(newsize < m_length)
|
||||||
|
return false;
|
||||||
|
char* newbuf = static_cast<char*>(_allocate(newsize+1));
|
||||||
|
if(!newbuf) return false;
|
||||||
|
memset(newbuf, 0, newsize+1);
|
||||||
|
memcpy(newbuf, m_data, m_length);
|
||||||
|
newbuf[m_length] = 0;
|
||||||
|
m_length = newsize;
|
||||||
|
_free(m_data);
|
||||||
|
m_data = newbuf;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
char& string_base::operator[](size_t i){
|
char& string_base::operator[](size_t i){
|
||||||
return m_data[i];
|
return m_data[i];
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user