reddit hosted videos now working with sound
This commit is contained in:
parent
9c9da0648e
commit
6151593ef5
2
.gitignore
vendored
2
.gitignore
vendored
@ -5,3 +5,5 @@ data
|
||||
*.swp
|
||||
*.save
|
||||
testout
|
||||
audio
|
||||
video
|
||||
|
||||
3
TODO
3
TODO
@ -1,3 +1,4 @@
|
||||
use libmagic to determine file types?
|
||||
//create a raii interface for libav* which allows creation of thumbnails
|
||||
add av_freep cleanup to raii interface for libav* (probably replacing unique_ptr in that case)
|
||||
raii-ify the muxing function
|
||||
scale video thumbnails to same size as image thumbnails (500 in either dimension, same aspect ratio)
|
||||
|
||||
@ -26,6 +26,9 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#define HAS_FREEIMAGE
|
||||
#define HAS_FFMPEG
|
||||
|
||||
namespace matrix{
|
||||
struct auth_data{
|
||||
raii::rjp_string bot_name;
|
||||
@ -125,9 +128,11 @@ namespace matrix{
|
||||
|
||||
//upload media
|
||||
file_info upload_file(const raii::string_base& filename);
|
||||
image_info upload_image(const raii::string_base& filename, const raii::string_base& alias);
|
||||
file_info upload_file(const raii::string_base& filename, const raii::string_base& alias);
|
||||
image_info upload_image(const raii::string_base& filename);
|
||||
image_info upload_image(const raii::string_base& filename, const raii::string_base& alias);
|
||||
video_info upload_video(const raii::string_base& filename);
|
||||
video_info upload_video(const raii::string_base& filename, const raii::string_base& alias);
|
||||
|
||||
//send messages
|
||||
raii::rjp_string send_image(const raii::string_base& room, const image_info& image);
|
||||
@ -140,7 +145,7 @@ namespace matrix{
|
||||
|
||||
protected:
|
||||
raii::rjp_string _upload_file(raii::filerd& fp, const raii::curl_llist& header);
|
||||
raii::rjp_string _send_message(const raii::string_base& room, const raii::string_base& msg, const raii::curl_llist& header);
|
||||
raii::rjp_string _send_message(const raii::string_base& room, const raii::string_base& msg);
|
||||
static size_t _post_reply_curl_callback(char* ptr, size_t size, size_t nmemb, void* userdata);
|
||||
raii::string _get_curl(const raii::string_base& url);
|
||||
raii::string _post_curl(const raii::string_base& postdata, const raii::string_base& url, const raii::curl_llist& header);
|
||||
|
||||
@ -20,9 +20,7 @@
|
||||
#define RAII_VIDEO_MAN_HPP
|
||||
|
||||
#include <memory> //unique_ptr
|
||||
#include <utility> //exchange
|
||||
#include "raii/string.hpp"
|
||||
#include "raii/static_string.hpp"
|
||||
|
||||
//needs to be extern C because quality library writing
|
||||
extern "C"{
|
||||
@ -36,23 +34,10 @@ namespace raii{
|
||||
|
||||
class video_man
|
||||
{
|
||||
private:
|
||||
static inline auto _context_deleter = [](AVCodecContext* ptr){
|
||||
avcodec_close(ptr);
|
||||
avcodec_free_context(&ptr);
|
||||
};
|
||||
static inline auto _frame_deleter = [](AVFrame* ptr){
|
||||
av_frame_unref(ptr);
|
||||
av_frame_free(&ptr);
|
||||
};
|
||||
static inline auto _packet_deleter = [](AVPacket* ptr){
|
||||
av_packet_unref(ptr);
|
||||
av_packet_free(&ptr);
|
||||
};
|
||||
public:
|
||||
using context_type = std::unique_ptr<AVCodecContext,decltype(_context_deleter)>;
|
||||
using frame_type = std::unique_ptr<AVFrame,decltype(_frame_deleter)>;
|
||||
using packet_type = std::unique_ptr<AVPacket,decltype(_packet_deleter)>;
|
||||
using context_type = std::unique_ptr<AVCodecContext,void(&)(AVCodecContext*)>;
|
||||
using frame_type = std::unique_ptr<AVFrame,void(&)(AVFrame*)>;
|
||||
using packet_type = std::unique_ptr<AVPacket,void(&)(AVPacket*)>;
|
||||
|
||||
private:
|
||||
AVFormatContext* m_vid;
|
||||
@ -62,145 +47,30 @@ namespace raii{
|
||||
|
||||
|
||||
public:
|
||||
video_man(const raii::string_base& filename){
|
||||
av_register_all();
|
||||
_init(filename);
|
||||
}
|
||||
video_man(const raii::string_base& filename);
|
||||
video_man(const video_man& v) = delete;
|
||||
video_man(video_man&& v):
|
||||
m_vid(std::exchange(v.m_vid, nullptr)){}
|
||||
~video_man(void){
|
||||
reset();
|
||||
}
|
||||
video_man(video_man&& v);
|
||||
~video_man(void);
|
||||
|
||||
int height(void)const{
|
||||
return m_context->height;
|
||||
}
|
||||
int width(void)const{
|
||||
return m_context->width;
|
||||
}
|
||||
raii::string get_mimetype(void)const{
|
||||
const char* first = strstr(m_vid->iformat->name, ",");
|
||||
return raii::string(raii::static_string(m_vid->iformat->name, first - m_vid->iformat->name));
|
||||
}
|
||||
packet_type create_jpg_thumbnail(void){
|
||||
context_type jpg_context = _get_jpeg_context(m_context);
|
||||
frame_type frame(_init_frame(m_context->width, m_context->height, m_context->pix_fmt), _frame_deleter);
|
||||
frame_type jpg_frame(_init_frame(m_context->width, m_context->height, jpg_context->pix_fmt), _frame_deleter);
|
||||
packet_type packet(av_packet_alloc(), _packet_deleter);
|
||||
SwsContext* sws_ctx = sws_getContext(m_context->width, m_context->height, m_context->pix_fmt,
|
||||
m_context->width, m_context->height, AV_PIX_FMT_YUV420P,
|
||||
SWS_BILINEAR, NULL, NULL, NULL);
|
||||
_change_color_range(sws_ctx);
|
||||
int height(void)const;
|
||||
int width(void)const;
|
||||
raii::string get_mimetype(void)const;
|
||||
packet_type create_jpg_thumbnail(void);
|
||||
|
||||
av_image_alloc(jpg_frame->data, jpg_frame->linesize, m_context->width, m_context->height, jpg_context->pix_fmt, 32);
|
||||
while(av_read_frame(m_vid, packet.get()) >= 0){
|
||||
if(packet->stream_index == m_stream_index){
|
||||
avcodec_send_packet(m_context, packet.get());
|
||||
if(avcodec_receive_frame(m_context, frame.get()) >= 0)
|
||||
break;
|
||||
}
|
||||
av_packet_unref(packet.get());
|
||||
}
|
||||
sws_scale(sws_ctx, frame->data, frame->linesize, 0, m_context->height, jpg_frame->data, jpg_frame->linesize);
|
||||
AVFormatContext* release(void);
|
||||
void reset(const raii::string_base& filename);
|
||||
void reset(void);
|
||||
|
||||
sws_freeContext(sws_ctx);
|
||||
|
||||
packet_type jpg_packet(av_packet_alloc(), _packet_deleter);
|
||||
if(avcodec_send_frame(jpg_context.get(), jpg_frame.get()) < 0){
|
||||
av_freep(&jpg_frame->data[0]);
|
||||
return packet_type(nullptr, _packet_deleter);
|
||||
}
|
||||
if(avcodec_receive_packet(jpg_context.get(), jpg_packet.get()) < 0){
|
||||
return packet_type(nullptr, _packet_deleter);
|
||||
}
|
||||
|
||||
av_freep(&jpg_frame->data[0]);
|
||||
return jpg_packet;
|
||||
}
|
||||
|
||||
AVFormatContext* release(void){
|
||||
avcodec_close(m_context);
|
||||
avcodec_free_context(&m_context);
|
||||
m_context = nullptr;
|
||||
m_codec = nullptr;
|
||||
m_stream_index = -1;
|
||||
return std::exchange(m_vid, nullptr);
|
||||
}
|
||||
void reset(const raii::string_base& filename){
|
||||
reset();
|
||||
_init(filename);
|
||||
}
|
||||
void reset(void){
|
||||
avcodec_close(m_context);
|
||||
avcodec_free_context(&m_context);
|
||||
avformat_close_input(&m_vid);
|
||||
m_context = nullptr;
|
||||
m_vid = nullptr;
|
||||
m_codec = nullptr;
|
||||
m_stream_index = -1;
|
||||
}
|
||||
operator bool(void)const;
|
||||
private:
|
||||
void _init(const raii::string_base& filename){
|
||||
m_vid = _open_video_file(filename);
|
||||
if(!m_vid)
|
||||
return;
|
||||
|
||||
AVCodecParameters* codec_parms = m_vid->streams[m_stream_index]->codecpar;
|
||||
m_codec = avcodec_find_decoder(codec_parms->codec_id);
|
||||
m_context = avcodec_alloc_context3(m_codec);
|
||||
avcodec_parameters_to_context(m_context, codec_parms);
|
||||
avcodec_open2(m_context, m_codec, NULL);
|
||||
}
|
||||
AVFormatContext* _open_video_file(const raii::string_base& filename){
|
||||
AVFormatContext* context = NULL;
|
||||
avformat_open_input(&context, filename.get(), NULL, NULL);
|
||||
avformat_find_stream_info(context, NULL);
|
||||
for(size_t i = 0;i < context->nb_streams;++i){
|
||||
if(context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){
|
||||
m_stream_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(m_stream_index != -1)
|
||||
return context;
|
||||
avformat_close_input(&context);
|
||||
return nullptr;
|
||||
}
|
||||
static AVFrame* _init_frame(int w, int h, int format){
|
||||
AVFrame* f = av_frame_alloc();
|
||||
f->data[0] = NULL;
|
||||
f->width = w;
|
||||
f->height = h;
|
||||
f->format = format;
|
||||
return f;
|
||||
}
|
||||
static void _change_color_range(SwsContext* sws_ctx){
|
||||
//we want to convert from YUVJ420P to YUV420P for sws_scale to be happy. but we actually want to output as YUVJ420P.
|
||||
//so we change the color range to use the full range like YUVJ420P. dummy is used to ignore unneeded returns.
|
||||
int dummy[4];
|
||||
int src_range, dest_range;
|
||||
int br, con, sat;
|
||||
sws_getColorspaceDetails(sws_ctx, (int**)&dummy, &src_range, (int**)&dummy, &dest_range, &br, &con, &sat);
|
||||
const int* coefs = sws_getCoefficients(SWS_CS_DEFAULT);
|
||||
src_range = 2;
|
||||
sws_setColorspaceDetails(sws_ctx, coefs, src_range, coefs, dest_range, br, con, sat);
|
||||
}
|
||||
static context_type _get_jpeg_context(const AVCodecContext* decoder){
|
||||
AVCodec* jpg_codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG);
|
||||
context_type jpg_context(avcodec_alloc_context3(jpg_codec), _context_deleter);
|
||||
jpg_context->pix_fmt = AV_PIX_FMT_YUVJ420P;
|
||||
jpg_context->width = decoder->width;
|
||||
jpg_context->height = decoder->height;
|
||||
jpg_context->codec_id = AV_CODEC_ID_MJPEG;
|
||||
jpg_context->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||
if(decoder->time_base.num == 0 || decoder->time_base.den == 0)
|
||||
jpg_context->time_base = AVRational{1, 30};
|
||||
else
|
||||
jpg_context->time_base = decoder->time_base;
|
||||
avcodec_open2(jpg_context.get(), jpg_codec, NULL);
|
||||
return jpg_context;
|
||||
}
|
||||
void _init(const raii::string_base& filename);
|
||||
AVFormatContext* _open_video_file(const raii::string_base& filename);
|
||||
static AVFrame* _init_frame(int w, int h, int format);
|
||||
static void _change_color_range(SwsContext* sws_ctx);
|
||||
static context_type _get_jpeg_context(const AVCodecContext* decoder);
|
||||
static void _context_deleter(AVCodecContext* ptr);
|
||||
static void _frame_deleter(AVFrame* ptr);
|
||||
static void _packet_deleter(AVPacket* ptr);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -65,6 +65,7 @@ namespace reddit{
|
||||
private:
|
||||
raii::string m_post;
|
||||
raii::rjp_string m_media_url;
|
||||
raii::string m_hosted_video_audio;
|
||||
raii::rjp_string m_author;
|
||||
raii::rjp_string m_post_hint;
|
||||
raii::rjp_string m_title;
|
||||
@ -88,6 +89,7 @@ namespace reddit{
|
||||
|
||||
const raii::string& raw(void)const;
|
||||
const raii::rjp_string& mediaurl(void)const;
|
||||
const raii::string& hosted_video_audio(void)const;
|
||||
const raii::rjp_string& posturl(void)const;
|
||||
const raii::rjp_string& author(void)const;
|
||||
const raii::rjp_string& post_hint(void)const;
|
||||
|
||||
@ -1,3 +1,21 @@
|
||||
/**
|
||||
This file is a part of rexy's matrix bot
|
||||
Copyright (C) 2019 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "fat_strings.hpp"
|
||||
#include "matrix.hpp"
|
||||
#include "raii/static_string.hpp"
|
||||
|
||||
@ -1,3 +1,21 @@
|
||||
/**
|
||||
This file is a part of rexy's matrix bot
|
||||
Copyright (C) 2019 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "matrix.hpp"
|
||||
|
||||
#include <FreeImagePlus.h>
|
||||
@ -111,14 +129,18 @@ namespace matrix{
|
||||
return _post_curl(postdata, m_urls.create_room, raii::curl_llist());
|
||||
}
|
||||
file_info bot::upload_file(const raii::string_base& filename){
|
||||
return upload_file(filename, raii::static_string());
|
||||
}
|
||||
file_info bot::upload_file(const raii::string_base& filename, const raii::string_base& alias){
|
||||
raii::filerd fd(filename);
|
||||
if(!fd) return {};
|
||||
|
||||
return file_info{_upload_file(fd, raii::curl_llist{}), filename, {}, fd.length()};
|
||||
return file_info{_upload_file(fd, raii::curl_llist{}), alias ? alias : filename, {}, fd.length()};
|
||||
}
|
||||
image_info bot::upload_image(const raii::string_base& filename){
|
||||
return upload_image(filename, raii::static_string());
|
||||
}
|
||||
#ifdef HAS_FREEIMAGE
|
||||
image_info bot::upload_image(const raii::string_base& filename, const raii::string_base& alias){
|
||||
image_info ret;
|
||||
|
||||
@ -133,24 +155,25 @@ namespace matrix{
|
||||
};
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
//determine type of image. used to create thumbnail of the same type and verify this is an image.
|
||||
auto type = fipImage::identifyFIF(filename.get());
|
||||
if(type == FIF_UNKNOWN){
|
||||
return {};
|
||||
}
|
||||
|
||||
image.load(filename.get());
|
||||
|
||||
raii::filerd fd(filename, "rb");
|
||||
if(!fd) return {};
|
||||
|
||||
//save fullsize image info
|
||||
ret.width = image.getWidth();
|
||||
ret.height = image.getHeight();
|
||||
ret.filetype = formattotype(type);
|
||||
ret.filename = alias ? alias : filename;
|
||||
ret.filesize = fd.length();
|
||||
raii::curl_llist header(raii::string("Content-Type: image/" + ret.filetype));
|
||||
ret.fileurl = _upload_file(fd, header);
|
||||
fd.reset();
|
||||
|
||||
//create thumbnail
|
||||
raii::curl_llist header(raii::string("Content-Type: image/" + ret.filetype));
|
||||
|
||||
//create and upload thumbnail
|
||||
image.makeThumbnail(500);
|
||||
|
||||
FreeImageIO fileout;
|
||||
std::vector<char> buffer;
|
||||
fileout.write_proc = [](void* ptr, unsigned int size, unsigned int nmemb, void* fp) -> unsigned int{
|
||||
@ -184,45 +207,75 @@ namespace matrix{
|
||||
ret.thumbsize = buffer.size();
|
||||
}
|
||||
|
||||
raii::filerd fd(filename, "rb");
|
||||
if(!fd)
|
||||
return ret;
|
||||
|
||||
ret.filesize = fd.length();
|
||||
ret.fileurl = _upload_file(fd, header);
|
||||
fd.reset();
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else //HAS_FREEIMAGE
|
||||
image_info bot::upload_image(const raii::string_base& filename, const raii::string_base& alias){
|
||||
image_info ret = {};
|
||||
ret = upload_file(filename, alias);
|
||||
return ret;
|
||||
}
|
||||
#endif //HAS_FREEIMAGE
|
||||
|
||||
video_info bot::upload_video(const raii::string_base& filename){
|
||||
return upload_video(filename, raii::static_string());
|
||||
}
|
||||
#ifdef HAS_FFMPEG
|
||||
video_info bot::upload_video(const raii::string_base& filename, const raii::string_base& alias){
|
||||
video_info ret = {};
|
||||
|
||||
raii::video_man context(filename);
|
||||
if(!context)
|
||||
return {};
|
||||
auto packet = context.create_jpg_thumbnail();
|
||||
raii::curl_llist header("Content-Type: image/jpeg");
|
||||
|
||||
ret.thumb_width = context.width();
|
||||
ret.thumb_height = context.height();
|
||||
ret.width = context.width();
|
||||
ret.height = context.height();
|
||||
raii::string mimetype = context.get_mimetype();
|
||||
context.reset(); //early cleanup to save memory
|
||||
ret.thumbsize = packet->size;
|
||||
ret.thumburl = _post_and_find(raii::static_string((char*)packet->data, packet->size), m_urls.file_upload, header, "content_uri"_ss);
|
||||
raii::string mimetype = context.get_mimetype();
|
||||
|
||||
packet.reset();
|
||||
|
||||
header = raii::curl_llist(raii::string("Content-Type: video/" + mimetype));
|
||||
raii::filerd fd(filename);
|
||||
if(!fd) return {};
|
||||
ret.filesize = fd.length();
|
||||
ret.width = context.width();
|
||||
ret.height = context.height();
|
||||
ret.filetype = std::move(mimetype);
|
||||
ret.fileurl = _upload_file(fd, header);
|
||||
ret.filename = filename;
|
||||
ret.filename = alias ? alias : filename;
|
||||
return ret;
|
||||
}
|
||||
#else //HAS_FFMPEG
|
||||
video_info bot::upload_video(const raii::string_base& filename, const raii::string_base& alias){
|
||||
video_info ret = {};
|
||||
ret = upload_file(filename, alias);
|
||||
return ret;
|
||||
}
|
||||
#endif //HAS_FFMPEG
|
||||
|
||||
raii::rjp_string bot::send_file(const raii::string_base& room, const file_info& file){
|
||||
return _send_message(room, detail::_file_body(file), raii::curl_llist{});
|
||||
return _send_message(room, detail::_file_body(file));
|
||||
}
|
||||
raii::rjp_string bot::send_image(const raii::string_base& room, const image_info& image){
|
||||
return _send_message(room, detail::_image_body(image), raii::curl_llist{});
|
||||
return _send_message(room, detail::_image_body(image));
|
||||
}
|
||||
raii::rjp_string bot::send_video(const raii::string_base& room, const video_info& video){
|
||||
return _send_message(room, detail::_video_body(video), raii::curl_llist());
|
||||
return _send_message(room, detail::_video_body(video));
|
||||
}
|
||||
raii::rjp_string bot::send_message(const raii::string_base& room, const raii::string_base& text){
|
||||
return _send_message(room, detail::_message_body(text), raii::curl_llist());
|
||||
return _send_message(room, detail::_message_body(text));
|
||||
}
|
||||
void bot::logout(void){
|
||||
_get_curl(raii::string("https://" + m_homeserver + "/_matrix/client/r0/logout?access_token=" + m_access_token));
|
||||
@ -261,11 +314,11 @@ namespace matrix{
|
||||
|
||||
return res.value;
|
||||
}
|
||||
raii::rjp_string bot::_send_message(const raii::string_base& room, const raii::string_base& msg, const raii::curl_llist& header){
|
||||
raii::rjp_string bot::_send_message(const raii::string_base& room, const raii::string_base& msg){
|
||||
raii::rjp_string reply = _post_and_find(
|
||||
msg,
|
||||
raii::string("https://" + m_homeserver + "/_matrix/client/r0/rooms/" + m_curl.encode(room) + "/send/m.room.message?access_token=" + m_access_token),
|
||||
header,
|
||||
raii::curl_llist(),
|
||||
"event_id"_ss);
|
||||
return reply;
|
||||
}
|
||||
|
||||
178
src/raii/video_man.cpp
Normal file
178
src/raii/video_man.cpp
Normal file
@ -0,0 +1,178 @@
|
||||
/**
|
||||
This file is a part of rexy's matrix bot
|
||||
Copyright (C) 2019 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "raii/video_man.hpp"
|
||||
#include "raii/static_string.hpp"
|
||||
|
||||
#include <utility> //exchange
|
||||
|
||||
namespace raii{
|
||||
|
||||
video_man::video_man(const raii::string_base& filename){
|
||||
av_register_all();
|
||||
_init(filename);
|
||||
}
|
||||
video_man::video_man(video_man&& v):
|
||||
m_vid(std::exchange(v.m_vid, nullptr)){}
|
||||
video_man::~video_man(void){
|
||||
reset();
|
||||
}
|
||||
|
||||
int video_man::height(void)const{
|
||||
return m_context->height;
|
||||
}
|
||||
int video_man::width(void)const{
|
||||
return m_context->width;
|
||||
}
|
||||
raii::string video_man::get_mimetype(void)const{
|
||||
const char* first = strstr(m_vid->iformat->name, ",");
|
||||
return raii::string(raii::static_string(m_vid->iformat->name, first - m_vid->iformat->name));
|
||||
}
|
||||
auto video_man::create_jpg_thumbnail(void) -> packet_type{
|
||||
context_type jpg_context = _get_jpeg_context(m_context);
|
||||
frame_type frame(_init_frame(m_context->width, m_context->height, m_context->pix_fmt), _frame_deleter);
|
||||
frame_type jpg_frame(_init_frame(m_context->width, m_context->height, jpg_context->pix_fmt), _frame_deleter);
|
||||
packet_type packet(av_packet_alloc(), _packet_deleter);
|
||||
SwsContext* sws_ctx = sws_getContext(m_context->width, m_context->height, m_context->pix_fmt,
|
||||
m_context->width, m_context->height, AV_PIX_FMT_YUV420P,
|
||||
SWS_BILINEAR, NULL, NULL, NULL);
|
||||
_change_color_range(sws_ctx);
|
||||
|
||||
av_image_alloc(jpg_frame->data, jpg_frame->linesize, m_context->width, m_context->height, jpg_context->pix_fmt, 32);
|
||||
while(av_read_frame(m_vid, packet.get()) >= 0){
|
||||
if(packet->stream_index == m_stream_index){
|
||||
avcodec_send_packet(m_context, packet.get());
|
||||
if(avcodec_receive_frame(m_context, frame.get()) >= 0)
|
||||
break;
|
||||
}
|
||||
av_packet_unref(packet.get());
|
||||
}
|
||||
sws_scale(sws_ctx, frame->data, frame->linesize, 0, m_context->height, jpg_frame->data, jpg_frame->linesize);
|
||||
|
||||
sws_freeContext(sws_ctx);
|
||||
|
||||
packet_type jpg_packet(av_packet_alloc(), _packet_deleter);
|
||||
if(avcodec_send_frame(jpg_context.get(), jpg_frame.get()) < 0){
|
||||
av_freep(&jpg_frame->data[0]);
|
||||
return packet_type(nullptr, _packet_deleter);
|
||||
}
|
||||
if(avcodec_receive_packet(jpg_context.get(), jpg_packet.get()) < 0){
|
||||
return packet_type(nullptr, _packet_deleter);
|
||||
}
|
||||
|
||||
av_freep(&jpg_frame->data[0]);
|
||||
return jpg_packet;
|
||||
}
|
||||
|
||||
AVFormatContext* video_man::release(void){
|
||||
avcodec_close(m_context);
|
||||
avcodec_free_context(&m_context);
|
||||
m_context = nullptr;
|
||||
m_codec = nullptr;
|
||||
m_stream_index = -1;
|
||||
return std::exchange(m_vid, nullptr);
|
||||
}
|
||||
void video_man::reset(const raii::string_base& filename){
|
||||
reset();
|
||||
_init(filename);
|
||||
}
|
||||
void video_man::reset(void){
|
||||
avcodec_close(m_context);
|
||||
avcodec_free_context(&m_context);
|
||||
avformat_close_input(&m_vid);
|
||||
m_context = nullptr;
|
||||
m_vid = nullptr;
|
||||
m_codec = nullptr;
|
||||
m_stream_index = -1;
|
||||
}
|
||||
video_man::operator bool(void)const{
|
||||
return m_vid;
|
||||
}
|
||||
void video_man::_init(const raii::string_base& filename){
|
||||
m_vid = _open_video_file(filename);
|
||||
if(!m_vid)
|
||||
return;
|
||||
|
||||
AVCodecParameters* codec_parms = m_vid->streams[m_stream_index]->codecpar;
|
||||
m_codec = avcodec_find_decoder(codec_parms->codec_id);
|
||||
m_context = avcodec_alloc_context3(m_codec);
|
||||
avcodec_parameters_to_context(m_context, codec_parms);
|
||||
avcodec_open2(m_context, m_codec, NULL);
|
||||
}
|
||||
AVFormatContext* video_man::_open_video_file(const raii::string_base& filename){
|
||||
AVFormatContext* context = NULL;
|
||||
avformat_open_input(&context, filename.get(), NULL, NULL);
|
||||
avformat_find_stream_info(context, NULL);
|
||||
for(size_t i = 0;i < context->nb_streams;++i){
|
||||
if(context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){
|
||||
m_stream_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(m_stream_index != -1)
|
||||
return context;
|
||||
avformat_close_input(&context);
|
||||
return nullptr;
|
||||
}
|
||||
AVFrame* video_man::_init_frame(int w, int h, int format){
|
||||
AVFrame* f = av_frame_alloc();
|
||||
f->data[0] = NULL;
|
||||
f->width = w;
|
||||
f->height = h;
|
||||
f->format = format;
|
||||
return f;
|
||||
}
|
||||
void video_man::_change_color_range(SwsContext* sws_ctx){
|
||||
//we want to convert from YUVJ420P to YUV420P for sws_scale to be happy. but we actually want to output as YUVJ420P.
|
||||
//so we change the color range to use the full range like YUVJ420P. dummy is used to ignore unneeded returns.
|
||||
int dummy[4];
|
||||
int src_range, dest_range;
|
||||
int br, con, sat;
|
||||
sws_getColorspaceDetails(sws_ctx, (int**)&dummy, &src_range, (int**)&dummy, &dest_range, &br, &con, &sat);
|
||||
const int* coefs = sws_getCoefficients(SWS_CS_DEFAULT);
|
||||
src_range = 2;
|
||||
sws_setColorspaceDetails(sws_ctx, coefs, src_range, coefs, dest_range, br, con, sat);
|
||||
}
|
||||
auto video_man::_get_jpeg_context(const AVCodecContext* decoder) -> context_type{
|
||||
AVCodec* jpg_codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG);
|
||||
context_type jpg_context(avcodec_alloc_context3(jpg_codec), _context_deleter);
|
||||
jpg_context->pix_fmt = AV_PIX_FMT_YUVJ420P;
|
||||
jpg_context->width = decoder->width;
|
||||
jpg_context->height = decoder->height;
|
||||
jpg_context->codec_id = AV_CODEC_ID_MJPEG;
|
||||
jpg_context->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||
if(decoder->time_base.num == 0 || decoder->time_base.den == 0)
|
||||
jpg_context->time_base = AVRational{1, 30};
|
||||
else
|
||||
jpg_context->time_base = decoder->time_base;
|
||||
avcodec_open2(jpg_context.get(), jpg_codec, NULL);
|
||||
return jpg_context;
|
||||
}
|
||||
void video_man::_context_deleter(AVCodecContext* ptr){
|
||||
avcodec_close(ptr);
|
||||
avcodec_free_context(&ptr);
|
||||
}
|
||||
void video_man::_frame_deleter(AVFrame* ptr){
|
||||
av_frame_unref(ptr);
|
||||
av_frame_free(&ptr);
|
||||
}
|
||||
void video_man::_packet_deleter(AVPacket* ptr){
|
||||
av_packet_unref(ptr);
|
||||
av_packet_free(&ptr);
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,21 @@
|
||||
/**
|
||||
This file is a part of rexy's matrix bot
|
||||
Copyright (C) 2019 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "reddit.hpp"
|
||||
#include "raii/rjp_string.hpp"
|
||||
#include "raii/string.hpp"
|
||||
@ -6,6 +24,13 @@
|
||||
#include "raii/static_string.hpp"
|
||||
|
||||
#include <algorithm> //search
|
||||
#include <cstring>
|
||||
|
||||
//no idea if this covers everything reddit might dish out at me
|
||||
//there is no consistency in their content tagging. there are gifs marked as images, others as videos
|
||||
//they separate audio and video streams for their hosted videos, there is no true way to tell
|
||||
//what kind of content a post contains since the post_hint field might be completely nonexistent.
|
||||
//it's just a game of hacking together solutions each time reddit throws me a new type of unexpected complication.
|
||||
|
||||
namespace reddit{
|
||||
|
||||
@ -39,17 +64,16 @@ namespace reddit{
|
||||
}
|
||||
|
||||
|
||||
static raii::rjp_string media_search(RJP_value* root){
|
||||
RJP_search_res media = rjp_search_member(root, "media", 0);
|
||||
if(!media.value)
|
||||
static raii::rjp_string media_search(RJP_value* media){
|
||||
if(!media)
|
||||
return {};
|
||||
RJP_search_res res = rjp_search_member(media, "reddit_video", 0);
|
||||
if(!res.value)
|
||||
return raii::rjp_string{};
|
||||
media = rjp_search_member(media.value, "reddit_video", 0);
|
||||
if(!media.value)
|
||||
res = rjp_search_member(res.value, "fallback_url", 0);
|
||||
if(!res.value)
|
||||
return raii::rjp_string{};
|
||||
media = rjp_search_member(media.value, "fallback_url", 0);
|
||||
if(!media.value)
|
||||
return raii::rjp_string{};
|
||||
return raii::rjp_string{media.value};
|
||||
return raii::rjp_string(res.value);
|
||||
}
|
||||
static raii::rjp_string preview_search(RJP_value* root){
|
||||
RJP_search_res media = rjp_search_member(root, "preview", 0);
|
||||
@ -69,8 +93,10 @@ namespace reddit{
|
||||
}
|
||||
|
||||
static raii::rjp_string find_video_url(RJP_value* root){
|
||||
if(raii::rjp_string res = media_search(root))
|
||||
RJP_search_res media = rjp_search_member(root, "media", 0);
|
||||
if(raii::rjp_string res = media_search(media.value)){
|
||||
return res;
|
||||
}
|
||||
raii::rjp_string res = preview_search(root);
|
||||
return res;
|
||||
}
|
||||
@ -142,6 +168,13 @@ namespace reddit{
|
||||
data = rjp_search_member(data.value, "data", 0);
|
||||
if(!data.value) return;
|
||||
|
||||
RJP_search_res crosspost = rjp_search_member(data.value, "crosspost_parent_list", 0);
|
||||
if(crosspost.value){
|
||||
crosspost.value = rjp_get_element(crosspost.value);
|
||||
if(crosspost.value)
|
||||
data = crosspost;
|
||||
}
|
||||
|
||||
rjp_search_members(data.value, num_searches, search_items, results, 0);
|
||||
m_media_url = results[0].value;
|
||||
m_author = results[1].value;
|
||||
@ -178,8 +211,41 @@ namespace reddit{
|
||||
}
|
||||
}
|
||||
//handle hosted video
|
||||
else if(!strcmp(m_post_hint, "hosted:video") || !strcmp(m_post_hint, "rich:video")){
|
||||
raii::rjp_string res = media_search(data.value);
|
||||
else if(!strcmp(m_post_hint, "hosted:video")){
|
||||
RJP_search_res media = rjp_search_member(data.value, "media", 0);
|
||||
RJP_search_res gif = rjp_search_member(media.value, "reddit_video", 0);
|
||||
if(gif.value)
|
||||
gif = rjp_search_member(media.value, "is_gif", 0);
|
||||
if(gif.value && rjp_value_boolean(gif.value)){
|
||||
m_type = post_type::image;
|
||||
}else{
|
||||
raii::rjp_string res = media_search(media.value);
|
||||
if(!res){
|
||||
res = preview_search(data.value);
|
||||
if(!res){
|
||||
m_type = post_type::link;
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_type = post_type::video;
|
||||
m_media_url = std::move(res);
|
||||
}
|
||||
|
||||
//reddit hosts audio and video separately. Meaning I have to find a way to manually recombine them
|
||||
static constexpr char url_base[] = "https://v.redd.it/";
|
||||
static constexpr size_t url_base_len = sizeof(url_base)-1;
|
||||
char* end = strstr(m_media_url.get()+url_base_len, "/");
|
||||
if(!end)
|
||||
end = m_media_url.get()+m_media_url.length();
|
||||
size_t len = end - m_media_url.get();
|
||||
m_hosted_video_audio = raii::string(len + 6);
|
||||
memcpy(m_hosted_video_audio.get(), m_media_url.get(), len);
|
||||
memcpy(m_hosted_video_audio.get()+len, "/audio", 6);
|
||||
m_hosted_video_audio[len+6] = 0;
|
||||
}
|
||||
else if(!strcmp(m_post_hint, "rich:video")){
|
||||
RJP_search_res media = rjp_search_member(data.value, "media", 0);
|
||||
raii::rjp_string res = media_search(media.value);
|
||||
if(res){
|
||||
m_type = post_type::video;
|
||||
m_media_url = std::move(res);
|
||||
@ -230,6 +296,9 @@ namespace reddit{
|
||||
const raii::rjp_string& post::mediaurl(void)const{
|
||||
return m_media_url;
|
||||
}
|
||||
const raii::string& post::hosted_video_audio(void)const{
|
||||
return m_hosted_video_audio;
|
||||
}
|
||||
const raii::rjp_string& post::posturl(void)const{
|
||||
return m_post_url;
|
||||
}
|
||||
|
||||
247
src/test.cpp
247
src/test.cpp
@ -1,3 +1,21 @@
|
||||
/**
|
||||
This file is a part of rexy's matrix bot
|
||||
Copyright (C) 2019 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <rjp.h>
|
||||
#include <utility> //move
|
||||
@ -20,18 +38,191 @@
|
||||
#include "reddit.hpp"
|
||||
#include "matrix.hpp"
|
||||
|
||||
extern "C"{
|
||||
# include <libavcodec/avcodec.h> //AVCodecContext, AVCodec
|
||||
# include <libavformat/avformat.h> //AVFormatContext
|
||||
# include <libswscale/swscale.h> //sws_scale
|
||||
# include <libavutil/imgutils.h> //av_image_alloc
|
||||
}
|
||||
|
||||
//a lot copied from a github repo, but with all deprecation warnings fixed.
|
||||
//no idea how the one guy managed to figure out all this with the minimal and conflicting documentation for ffmpeg and libav
|
||||
bool mux_audio_video(const raii::string_base& audio_file, const raii::string_base& video_file, const raii::string_base& output_file){
|
||||
av_register_all();
|
||||
av_log_set_level(AV_LOG_FATAL);
|
||||
|
||||
AVOutputFormat* out_format = NULL;
|
||||
AVFormatContext* audio_context = NULL, *video_context = NULL, *output_context = NULL;
|
||||
int video_index_in = -1, audio_index_in = -1;
|
||||
int video_index_out = -1, audio_index_out = -1;
|
||||
|
||||
|
||||
if(avformat_open_input(&audio_context, audio_file.get(), 0, 0) < 0)
|
||||
return false;
|
||||
if(avformat_find_stream_info(audio_context, 0) < 0){
|
||||
avformat_close_input(&audio_context);
|
||||
return false;
|
||||
}
|
||||
if(avformat_open_input(&video_context, video_file.get(), 0, 0) < 0){
|
||||
avformat_close_input(&audio_context);
|
||||
return false;
|
||||
}
|
||||
if(avformat_find_stream_info(video_context, 0) < 0){
|
||||
avformat_close_input(&audio_context);
|
||||
avformat_close_input(&video_context);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(avformat_alloc_output_context2(&output_context, av_guess_format("mp4", NULL, NULL), NULL, output_file.get()) < 0){
|
||||
avformat_close_input(&audio_context);
|
||||
avformat_close_input(&video_context);
|
||||
return false;
|
||||
}
|
||||
out_format = output_context->oformat;
|
||||
|
||||
for(size_t i = 0;i < audio_context->nb_streams;++i){
|
||||
if(audio_context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO){
|
||||
audio_index_in = i;
|
||||
|
||||
AVStream* in_stream = audio_context->streams[i];
|
||||
AVCodec* codec = avcodec_find_encoder(in_stream->codecpar->codec_id);
|
||||
AVCodecContext* tmp = avcodec_alloc_context3(codec);
|
||||
avcodec_parameters_to_context(tmp, in_stream->codecpar);
|
||||
AVStream* out_stream = avformat_new_stream(output_context, codec);
|
||||
audio_index_out = out_stream->index;
|
||||
if(output_context->oformat->flags & AVFMT_GLOBALHEADER){
|
||||
tmp->flags |= CODEC_FLAG_GLOBAL_HEADER;
|
||||
}
|
||||
tmp->codec_tag = 0;
|
||||
avcodec_parameters_from_context(out_stream->codecpar, tmp);
|
||||
avcodec_free_context(&tmp);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(size_t i = 0;i < video_context->nb_streams;++i){
|
||||
if(video_context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){
|
||||
video_index_in = i;
|
||||
|
||||
AVStream* in_stream = video_context->streams[i];
|
||||
AVCodec* codec = avcodec_find_encoder(in_stream->codecpar->codec_id);
|
||||
AVCodecContext* tmp = avcodec_alloc_context3(codec);
|
||||
avcodec_parameters_to_context(tmp, in_stream->codecpar);
|
||||
AVStream* out_stream = avformat_new_stream(output_context, codec);
|
||||
video_index_out = out_stream->index;
|
||||
if(output_context->oformat->flags & AVFMT_GLOBALHEADER){
|
||||
tmp->flags |= CODEC_FLAG_GLOBAL_HEADER;
|
||||
}
|
||||
tmp->codec_tag = 0;
|
||||
avcodec_parameters_from_context(out_stream->codecpar, tmp);
|
||||
avcodec_free_context(&tmp);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!(out_format->flags & AVFMT_NOFILE)){
|
||||
if(avio_open(&output_context->pb, output_file.get(), AVIO_FLAG_WRITE) < 0){
|
||||
avformat_free_context(output_context);
|
||||
avformat_close_input(&audio_context);
|
||||
avformat_close_input(&video_context);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(avformat_write_header(output_context, NULL) < 0){
|
||||
if(!(out_format->flags & AVFMT_NOFILE)){
|
||||
avio_close(output_context->pb);
|
||||
}
|
||||
avformat_free_context(output_context);
|
||||
avformat_close_input(&audio_context);
|
||||
avformat_close_input(&video_context);
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t video_pts = 0, audio_pts = 0;
|
||||
AVPacket packet;
|
||||
|
||||
while(true){
|
||||
AVFormatContext* in_context;
|
||||
int stream_index = 0;
|
||||
AVStream* in_stream, *out_stream;
|
||||
|
||||
if(av_compare_ts(video_pts, video_context->streams[video_index_in]->time_base,
|
||||
audio_pts, audio_context->streams[audio_index_in]->time_base) <= 0)
|
||||
{
|
||||
//video
|
||||
in_context = video_context;
|
||||
stream_index = video_index_out;
|
||||
|
||||
if(av_read_frame(in_context, &packet) >= 0){
|
||||
do{
|
||||
if(packet.stream_index == video_index_in){
|
||||
video_pts = packet.pts;
|
||||
break;
|
||||
}
|
||||
av_packet_unref(&packet);
|
||||
}while(av_read_frame(in_context, &packet) >= 0);
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
//audio
|
||||
in_context = audio_context;
|
||||
stream_index = audio_index_out;
|
||||
|
||||
if(av_read_frame(in_context, &packet) >= 0){
|
||||
do{
|
||||
if(packet.stream_index == audio_index_in){
|
||||
audio_pts = packet.pts;
|
||||
break;
|
||||
}
|
||||
av_packet_unref(&packet);
|
||||
}while(av_read_frame(in_context, &packet) >= 0);
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
in_stream = in_context->streams[packet.stream_index];
|
||||
out_stream = output_context->streams[stream_index];
|
||||
|
||||
packet.pts = av_rescale_q_rnd(packet.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
|
||||
packet.dts = av_rescale_q_rnd(packet.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
|
||||
packet.duration = av_rescale_q(packet.duration, in_stream->time_base, out_stream->time_base);
|
||||
packet.pos = -1;
|
||||
packet.stream_index = stream_index;
|
||||
|
||||
if(av_interleaved_write_frame(output_context, &packet) < 0){
|
||||
break;
|
||||
}
|
||||
av_packet_unref(&packet);
|
||||
|
||||
}
|
||||
|
||||
av_write_trailer(output_context);
|
||||
|
||||
if(!(out_format->flags & AVFMT_NOFILE)){
|
||||
avio_close(output_context->pb);
|
||||
}
|
||||
avformat_free_context(output_context);
|
||||
avformat_close_input(&audio_context);
|
||||
avformat_close_input(&video_context);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//Get username/password for reddit account and bot. Plus a useragent string
|
||||
std::tuple<reddit::auth_data,matrix::auth_data,raii::rjp_string> parse_data_file(const raii::rjp_ptr& root){
|
||||
std::tuple<reddit::auth_data,matrix::auth_data,raii::rjp_string, raii::rjp_string> parse_data_file(const raii::rjp_ptr& root){
|
||||
RJP_search_res res = rjp_search_member(root.get(), "reddit", 0);
|
||||
reddit::auth_data red_ret = reddit::parse_auth_data(res.value);
|
||||
|
||||
res = rjp_search_member(root.get(), "matrix", 0);
|
||||
matrix::auth_data mat_ret = matrix::parse_auth_data(res.value);
|
||||
raii::rjp_string roomid(rjp_search_member(res.value, "roomid", 0).value);
|
||||
|
||||
res = rjp_search_member(root.get(), "useragent", 0);
|
||||
|
||||
return std::tuple<reddit::auth_data,matrix::auth_data,raii::rjp_string>(std::move(red_ret), std::move(mat_ret), raii::rjp_string(res.value));
|
||||
return std::tuple<reddit::auth_data,matrix::auth_data,raii::rjp_string,raii::rjp_string>(std::move(red_ret), std::move(mat_ret), raii::rjp_string(res.value), std::move(roomid));
|
||||
}
|
||||
//Read in file containing username/password details
|
||||
raii::rjp_ptr read_data_file(const char* file){
|
||||
@ -53,9 +244,9 @@ size_t filewrite_response(char* ptr, size_t size, size_t nmemb, void* userdata){
|
||||
return nmemb*size;
|
||||
}
|
||||
|
||||
void file_output_curl(raii::curler& curl, const raii::string_base& url){
|
||||
bool file_output_curl(raii::curler& curl, const raii::string_base& filename, const raii::string_base& url){
|
||||
//Download the post's image
|
||||
raii::filerd fp("testout", "w");
|
||||
raii::filerd fp(filename, "w");
|
||||
if(!fp)
|
||||
fprintf(stderr, "unable to open file for writing\n");
|
||||
curl.seturl(url);
|
||||
@ -63,10 +254,16 @@ void file_output_curl(raii::curler& curl, const raii::string_base& url){
|
||||
curl.setopt(CURLOPT_NOPROGRESS, 1L);
|
||||
curl.setopt(CURLOPT_FOLLOWLOCATION, 1L);
|
||||
curl.setopt(CURLOPT_MAXREDIRS, 50L);
|
||||
curl.setopt(CURLOPT_FAILONERROR, 1L);
|
||||
curl.forcessl(CURL_SSLVERSION_TLSv1_2);
|
||||
curl.setopt(CURLOPT_WRITEFUNCTION, filewrite_response);
|
||||
curl.setopt(CURLOPT_WRITEDATA, fp.get());
|
||||
curl.perform();
|
||||
int ret = curl.perform();
|
||||
if(ret != CURLE_OK)
|
||||
return false;
|
||||
if(!fp.length())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -86,7 +283,7 @@ int main(){
|
||||
}
|
||||
|
||||
//Parse data file
|
||||
auto [reddit_auth,matrix_auth,useragent] = parse_data_file(root);
|
||||
auto [reddit_auth,matrix_auth,useragent,roomid] = parse_data_file(root);
|
||||
if(!(reddit_auth && matrix_auth && useragent)){
|
||||
fprintf(stderr, "Missing data field\n");
|
||||
return 2;
|
||||
@ -103,8 +300,8 @@ int main(){
|
||||
{
|
||||
int retries = 5;
|
||||
do{
|
||||
reply = mybot.get_top_post("ProgrammerHumor"_ss, reply.name());
|
||||
if(reply.type() != reddit::post_type::text)
|
||||
reply = mybot.get_top_post("ProgrammerHumor"_ss, reply.name(), reddit::time::hour);
|
||||
if(reply.type() != reddit::post_type::text && reply.type() != reddit::post_type::link)
|
||||
break;
|
||||
--retries;
|
||||
DEBUG_PRINT("Not an image.\nTODO, search for another\n");
|
||||
@ -125,26 +322,44 @@ int main(){
|
||||
DEBUG_PRINT("mediaurl: %s\n", reply.mediaurl().get());
|
||||
DEBUG_PRINT("posturl: %s\n", reply.posturl().get());
|
||||
|
||||
|
||||
raii::curler curl;
|
||||
if(reply.type() == reddit::post_type::image)
|
||||
{
|
||||
DEBUG_PRINT("Got an image\n");
|
||||
file_output_curl(curl, reply.mediaurl());
|
||||
file_output_curl(curl, "testout"_ss, reply.mediaurl());
|
||||
auto img_data = matbot.upload_image("testout"_ss, reply.name());
|
||||
auto val = matbot.send_image("!QeYfNDCRodtNohhnaI:matrix.org"_ss, img_data);
|
||||
auto val = matbot.send_image(roomid, img_data);
|
||||
DEBUG_PRINT("image event: %s\n", val.get());
|
||||
val = matbot.send_message("!QeYfNDCRodtNohhnaI:matrix.org"_ss, raii::string(reply.title() + "\n" + reply.posturl()));
|
||||
val = matbot.send_message(roomid, raii::string(reply.title() + "\n" + reply.posturl()));
|
||||
DEBUG_PRINT("text event: %s\n", val.get());
|
||||
}
|
||||
else if(reply.type() == reddit::post_type::video)
|
||||
{
|
||||
DEBUG_PRINT("Got a video\n");
|
||||
file_output_curl(curl, reply.mediaurl());
|
||||
auto vid_data = matbot.upload_video("testout"_ss);
|
||||
auto val = matbot.send_video("!QeYfNDCRodtNohhnaI:matrix.org"_ss, vid_data);
|
||||
|
||||
raii::static_string target = "testout";
|
||||
|
||||
if(reply.hosted_video_audio()){
|
||||
DEBUG_PRINT("fuck reddit\n");
|
||||
file_output_curl(curl, "video"_ss, reply.mediaurl());
|
||||
if(!file_output_curl(curl, "audio"_ss, reply.hosted_video_audio())){
|
||||
target = "video";
|
||||
remove("audio");
|
||||
}else{
|
||||
DEBUG_PRINT("Remuxing audio and video\n");
|
||||
mux_audio_video("audio"_ss, "video"_ss, "testout"_ss);
|
||||
remove("video");
|
||||
remove("audio");
|
||||
}
|
||||
}else{
|
||||
file_output_curl(curl, target, reply.mediaurl());
|
||||
}
|
||||
|
||||
DEBUG_PRINT("Uploading video\n");
|
||||
auto vid_data = matbot.upload_video(target);
|
||||
auto val = matbot.send_video(roomid, vid_data);
|
||||
DEBUG_PRINT("video event: %s\n", val.get());
|
||||
val = matbot.send_message("!QeYfNDCRodtNohhnaI:matrix.org"_ss, raii::string(reply.title() + "\n" + reply.posturl()));
|
||||
val = matbot.send_message(roomid, raii::string(reply.title() + "\n" + reply.posturl()));
|
||||
DEBUG_PRINT("text event: %s\n", val.get());
|
||||
}
|
||||
else if(reply.type() == reddit::post_type::link)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user