aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/AsyncImageLoader.hpp1
-rw-r--r--include/FileAnalyzer.hpp3
-rw-r--r--include/MessageQueue.hpp10
-rw-r--r--include/Program.hpp1
-rw-r--r--plugins/Matrix.hpp2
-rw-r--r--src/AsyncImageLoader.cpp16
-rw-r--r--src/FileAnalyzer.cpp32
-rw-r--r--src/Program.cpp17
-rw-r--r--src/QuickMedia.cpp2
-rw-r--r--src/Text.cpp3
-rw-r--r--src/plugins/Matrix.cpp27
11 files changed, 86 insertions, 28 deletions
diff --git a/include/AsyncImageLoader.hpp b/include/AsyncImageLoader.hpp
index 986430c..42e7915 100644
--- a/include/AsyncImageLoader.hpp
+++ b/include/AsyncImageLoader.hpp
@@ -27,6 +27,7 @@ namespace QuickMedia {
std::unique_ptr<mgl::Image> image; // Set in another thread. This should be .reset after loading it into |texture|, to save memory
size_t counter = 0;
Path thumbnail_path;
+ std::string url;
};
struct ThumbnailLoadData {
diff --git a/include/FileAnalyzer.hpp b/include/FileAnalyzer.hpp
index 7be154f..ed17140 100644
--- a/include/FileAnalyzer.hpp
+++ b/include/FileAnalyzer.hpp
@@ -45,6 +45,9 @@ namespace QuickMedia {
// Set |width| or |height| to 0 to disable scaling.
// TODO: Make this async
+ bool video_get_start_frame(const FileAnalyzer &file, const char *destination_path, int width = 0, int height = 0);
+ // Set |width| or |height| to 0 to disable scaling.
+ // TODO: Make this async
bool video_get_middle_frame(const FileAnalyzer &file, const char *destination_path, int width = 0, int height = 0);
class FileAnalyzer {
diff --git a/include/MessageQueue.hpp b/include/MessageQueue.hpp
index ba28431..1e48102 100644
--- a/include/MessageQueue.hpp
+++ b/include/MessageQueue.hpp
@@ -59,14 +59,18 @@ namespace QuickMedia {
}
// Return true from |callback| to remove the element
- void erase_if(std::function<bool(T&)> callback) {
+ int erase_if(std::function<bool(T&)> callback) {
std::unique_lock<std::mutex> lock(mutex);
+ int removed = 0;
for(auto it = data_queue.begin(); it != data_queue.end();) {
- if(callback(*it))
+ if(callback(*it)) {
it = data_queue.erase(it);
- else
+ ++removed;
+ } else {
++it;
+ }
}
+ return removed;
}
bool is_running() const {
diff --git a/include/Program.hpp b/include/Program.hpp
index 674c834..5249de6 100644
--- a/include/Program.hpp
+++ b/include/Program.hpp
@@ -33,6 +33,7 @@ int exec_program_write_stdin(const char **args, const char *str, size_t size, Pr
|buffer_size| has to be between 1 and 65536.
*/
int exec_program(const char **args, ProgramOutputCallback output_callback, void *userdata, int buffer_size = 16384);
+int exec_program(const char **args, ProgramOutputCallback output_callback, void *userdata, int *allowed_exit_status, int num_allowed_exit_status, int buffer_size = 16384);
// Return the exit status, or a negative value if waiting failed
int wait_program(pid_t process_id);
diff --git a/plugins/Matrix.hpp b/plugins/Matrix.hpp
index 4af8442..ccbd8c0 100644
--- a/plugins/Matrix.hpp
+++ b/plugins/Matrix.hpp
@@ -253,7 +253,6 @@ namespace QuickMedia {
std::string room_avatar_url;
std::shared_ptr<UserInfo> invited_by;
time_t timestamp = 0; // In milliseconds
- bool new_invite = false;
};
enum class MessageDirection {
@@ -755,6 +754,7 @@ namespace QuickMedia {
PluginResult parse_notifications(const rapidjson::Value &notifications_json, std::function<void(const MatrixNotification&)> callback_func);
PluginResult parse_sync_account_data(const rapidjson::Value &account_data_json);
PluginResult parse_sync_room_data(const rapidjson::Value &rooms_json, bool initial_sync);
+ void add_new_invites();
void parse_custom_emoji(const rapidjson::Value &custom_emoji_json);
void load_custom_emoji_from_cache();
PluginResult get_previous_room_messages(RoomData *room_data, bool latest_messages, size_t &num_new_messages, bool *reached_end = nullptr);
diff --git a/src/AsyncImageLoader.cpp b/src/AsyncImageLoader.cpp
index 4021c55..8613cf9 100644
--- a/src/AsyncImageLoader.cpp
+++ b/src/AsyncImageLoader.cpp
@@ -312,15 +312,19 @@ namespace QuickMedia {
}
std::shared_ptr<ThumbnailData> AsyncImageLoader::get_thumbnail(const std::string &url, bool local, mgl::vec2i resize_target_size) {
- // TODO: Instead of generating a new hash everytime to access thumbnail, cache the hash of the thumbnail url
- auto &thumbnail_data = thumbnails[url + "_" + std::to_string(resize_target_size.x) + "x" + std::to_string(resize_target_size.y)];
- if(!thumbnail_data)
+ // TODO: Instead of generating a new hash everytime to access thumbnail, cache the hash of the thumbnail url.
+ // TODO: Cache this resize_url
+ std::string resize_url = url + "_" + std::to_string(resize_target_size.x) + "x" + std::to_string(resize_target_size.y);
+ auto &thumbnail_data = thumbnails[resize_url];
+ if(!thumbnail_data) {
thumbnail_data = std::make_shared<ThumbnailData>();
+ thumbnail_data->url = url;
+ }
thumbnail_data->counter = counter;
if(thumbnail_data->thumbnail_path.data.empty()) {
SHA256 sha256;
- sha256.add(url.data(), url.size());
+ sha256.add(resize_url.data(), resize_url.size());
thumbnail_data->thumbnail_path = get_cache_dir().join("thumbnails").join(sha256.getHash());
}
@@ -358,7 +362,7 @@ namespace QuickMedia {
if(download.read_program.pid == -1)
continue;
- if(download.url == it->first) {
+ if(download.url == it->second->url) {
reset_download(download);
break;
}
@@ -366,7 +370,7 @@ namespace QuickMedia {
}
image_thumbnail_create_queue.erase_if([&it](ThumbnailLoadData &load_data) {
- return load_data.path.data == it->first;
+ return load_data.path.data == it->second->url;
});
it = thumbnails.erase(it);
diff --git a/src/FileAnalyzer.cpp b/src/FileAnalyzer.cpp
index 361211f..61eff7b 100644
--- a/src/FileAnalyzer.cpp
+++ b/src/FileAnalyzer.cpp
@@ -21,10 +21,12 @@ namespace QuickMedia {
// https://mimesniff.spec.whatwg.org/
// TODO: Test all of these
- static const std::array<MagicNumber, 31> magic_numbers = {
+ static const std::array<MagicNumber, 33> magic_numbers = {
MagicNumber{ {'R', 'I', 'F', 'F', -1, -1, -1, -1, 'A', 'V', 'I', ' '}, 12, ContentType::VIDEO_AVI },
MagicNumber{ {0x00, 0x00, 0x00, -1, 'f', 't', 'y', 'p', 'i', 's', 'o', 'm'}, 12, ContentType::VIDEO_MP4 },
+ MagicNumber{ {0x00, 0x00, 0x00, -1, 'f', 't', 'y', 'p', 'i', 's', 'o', '5'}, 12, ContentType::VIDEO_MP4 },
MagicNumber{ {0x00, 0x00, 0x00, -1, 'f', 't', 'y', 'p', 'm', 'p', '4', '2'}, 12, ContentType::VIDEO_MP4 },
+ MagicNumber{ {0x00, 0x00, 0x00, -1, 'f', 't', 'y', 'p', 'M', '4', 'V'}, 11, ContentType::VIDEO_MP4 },
MagicNumber{ {0x00, 0x00, 0x00, -1, 'f', 't', 'y', 'p', '3', 'g', 'p', '4'}, 12, ContentType::VIDEO_MP4 },
MagicNumber{ {0x00, 0x00, 0x00, -1, 'f', 't', 'y', 'p', '3', 'g', 'p', '5'}, 12, ContentType::VIDEO_MP4 },
MagicNumber{ {0x00, 0x00, 0x00, -1, 'f', 't', 'y', 'm', 'p', '4', '2'}, 11, ContentType::VIDEO_MP4 },
@@ -136,29 +138,29 @@ namespace QuickMedia {
|| strcase_equals(ext, ".wav")
|| strcase_equals(ext, ".wma")
|| strcase_equals(ext, ".mid");
-
}
- bool video_get_middle_frame(const FileAnalyzer &file, const char *destination_path, int width, int height) {
+ bool video_get_frame(const FileAnalyzer &file, const char *destination_path, int width, int height, int seconds) {
Path destination_path_tmp = destination_path;
destination_path_tmp.append(".tmp.jpg"); // TODO: .png, but the below code also needs to be changed for that
- const int middle_seconds = file.get_duration_seconds().value_or(0.0) / 2.0;
- char middle_seconds_str[32];
- snprintf(middle_seconds_str, sizeof(middle_seconds_str), "%d", middle_seconds);
+ char seconds_str[32];
+ snprintf(seconds_str, sizeof(seconds_str), "%d", seconds);
+
+ int allowed_exit_status[] = { 0, 69 };
if(width > 0 && height > 0) {
char size_arg_str[512];
snprintf(size_arg_str, sizeof(size_arg_str), "scale=%d:%d:force_original_aspect_ratio=decrease", width, height);
- const char *program_args[] = { "ffmpeg", "-y", "-v", "quiet", "-ss", middle_seconds_str, "-i", file.get_filepath().c_str(), "-vframes", "1", "-vf", size_arg_str, "--", destination_path_tmp.data.c_str(), nullptr };
- if(exec_program(program_args, nullptr, nullptr) != 0) {
+ const char *program_args[] = { "ffmpeg", "-y", "-v", "quiet", "-ss", seconds_str, "-i", file.get_filepath().c_str(), "-frames:v", "1", "-vf", size_arg_str, "--", destination_path_tmp.data.c_str(), nullptr };
+ if(exec_program(program_args, nullptr, nullptr, allowed_exit_status, 2) != 0) {
fprintf(stderr, "Failed to execute ffmpeg, maybe its not installed?\n");
return false;
}
} else {
- const char *program_args[] = { "ffmpeg", "-y", "-v", "quiet", "-ss", middle_seconds_str, "-i", file.get_filepath().c_str(), "-vframes", "1", "--", destination_path_tmp.data.c_str(), nullptr };
- if(exec_program(program_args, nullptr, nullptr) != 0) {
+ const char *program_args[] = { "ffmpeg", "-y", "-v", "quiet", "-ss", seconds_str, "-i", file.get_filepath().c_str(), "-frames:v", "1", "--", destination_path_tmp.data.c_str(), nullptr };
+ if(exec_program(program_args, nullptr, nullptr, allowed_exit_status, 2) != 0) {
fprintf(stderr, "Failed to execute ffmpeg, maybe its not installed?\n");
return false;
}
@@ -167,6 +169,16 @@ namespace QuickMedia {
return rename_atomic(destination_path_tmp.data.c_str(), destination_path) == 0;
}
+ bool video_get_start_frame(const FileAnalyzer &file, const char *destination_path, int width, int height) {
+ const int start_seconds = std::max(0.0, std::min(3.0, file.get_duration_seconds().value_or(0.0) - 1.0));
+ return video_get_frame(file, destination_path, width, height, start_seconds);
+ }
+
+ bool video_get_middle_frame(const FileAnalyzer &file, const char *destination_path, int width, int height) {
+ const int middle_seconds = file.get_duration_seconds().value_or(0.0) / 2.0;
+ return video_get_frame(file, destination_path, width, height, middle_seconds);
+ }
+
// TODO: Remove dependency on ffprobe
static bool ffprobe_extract_metadata(const char *filepath, std::optional<Dimensions> &dimensions, std::optional<double> &duration_seconds) {
const char *program_args[] = { "ffprobe", "-v", "quiet", "-print_format", "json", "-show_streams", "-show_format", "--", filepath, nullptr };
diff --git a/src/Program.cpp b/src/Program.cpp
index 8b3cc49..5e54971 100644
--- a/src/Program.cpp
+++ b/src/Program.cpp
@@ -303,6 +303,11 @@ int exec_program_write_stdin(const char **args, const char *str, size_t size, Pr
}
int exec_program(const char **args, ProgramOutputCallback output_callback, void *userdata, int buffer_size) {
+ int allowed_exit_status[1] = {0};
+ return exec_program(args, output_callback, userdata, allowed_exit_status, 1, buffer_size);
+}
+
+int exec_program(const char **args, ProgramOutputCallback output_callback, void *userdata, int *allowed_exit_status, int num_allowed_exit_status, int buffer_size) {
ReadProgram read_program;
int res = exec_program_pipe(args, &read_program);
if(res != 0)
@@ -311,6 +316,7 @@ int exec_program(const char **args, ProgramOutputCallback output_callback, void
int result = 0;
int status;
int exit_status;
+ bool is_error = true;
assert(buffer_size >= 1 && buffer_size <= 65536);
char *buffer = (char*)alloca(buffer_size + 1);
@@ -349,7 +355,14 @@ int exec_program(const char **args, ProgramOutputCallback output_callback, void
}
exit_status = WEXITSTATUS(status);
- if(exit_status != 0) {
+ for(int i = 0; i < num_allowed_exit_status; ++i) {
+ if(exit_status == allowed_exit_status[i]) {
+ is_error = false;
+ break;
+ }
+ }
+
+ if(is_error) {
fprintf(stderr, "Failed to execute program (");
const char **arg = args;
while(*arg) {
@@ -360,6 +373,8 @@ int exec_program(const char **args, ProgramOutputCallback output_callback, void
}
fprintf(stderr, "), exit status %d\n", exit_status);
result = -exit_status;
+ if(result == 0)
+ result = -1;
}
cleanup:
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 84b6cc4..9e360b7 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -3007,7 +3007,7 @@ namespace QuickMedia {
const char *args[] = { "curl", "-sLf", "-r", "0-40", "--no-buffer", "-H", useragent_str, "--", url, nullptr };
exec_program(args, accumulate_string_limit_head, &result, 42);
return (result.size() >= 42)
- && (memcmp(&result[4], "ftypisom", 8) == 0 || memcmp(&result[4], "ftypmp42", 8) == 0 || memcmp(&result[4], "ftymp42", 7) == 0 || memcmp(&result[4], "ftyp3gp4", 8) == 0 || memcmp(&result[4], "ftyp3gp5", 8) == 0 || memcmp(&result[4], "fty3gp5", 7) == 0 || memcmp(&result[4], "ftypqt", 6) == 0)
+ && (memcmp(&result[4], "ftypisom", 8) == 0 || memcmp(&result[4], "ftypiso5", 8) == 0 || memcmp(&result[4], "ftypmp42", 8) == 0 || memcmp(&result[4], "ftypM4V", 7) == 0 || memcmp(&result[4], "ftymp42", 7) == 0 || memcmp(&result[4], "ftyp3gp4", 8) == 0 || memcmp(&result[4], "ftyp3gp5", 8) == 0 || memcmp(&result[4], "fty3gp5", 7) == 0 || memcmp(&result[4], "ftypqt", 6) == 0)
&& (memmem(&result[0], result.size(), "moov", 4) == NULL);
}
diff --git a/src/Text.cpp b/src/Text.cpp
index 0597080..787c3a4 100644
--- a/src/Text.cpp
+++ b/src/Text.cpp
@@ -277,7 +277,6 @@ namespace QuickMedia
}
}
- fprintf(stderr, "old caret index: %d, advance: %d\n", caretIndex, caret_advance);
caretIndex += caret_advance;
dirtyCaret = true;
}
@@ -1577,7 +1576,7 @@ namespace QuickMedia
if(textElement.text_type == TextElement::TextType::EMOJI) {
// TODO:
if(textElement.pos.to_vec2f().y + vspace > boundingBox.size.y + 10.0f) {
- fprintf(stderr, "bounding box y: %f\n", boundingBox.size.y);
+ //fprintf(stderr, "bounding box y: %f\n", boundingBox.size.y);
continue;
}
diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp
index fae8451..c93a22b 100644
--- a/src/plugins/Matrix.cpp
+++ b/src/plugins/Matrix.cpp
@@ -684,7 +684,7 @@ namespace QuickMedia {
if(silenced_invite)
body_item->add_reaction("Silenced", nullptr);
invites_page->add_body_item(std::move(body_item));
- if(invite.new_invite && !silenced_invite) {
+ if(!silenced_invite) {
show_notification("QuickMedia matrix - " + invite.room_name, "You were invited to " + invite.room_name + " by " + invited_by_display_name + " (" + invite.invited_by->user_id + ")");
}
}
@@ -1147,7 +1147,7 @@ namespace QuickMedia {
}
void MatrixInvitesPage::add_body_item(std::shared_ptr<BodyItem> body_item) {
- body->insert_item_by_timestamp(std::move(body_item));
+ body->insert_item_by_timestamp_reverse(std::move(body_item));
if(body->get_num_items() != prev_invite_count) {
prev_invite_count = body->get_num_items();
title = "Invites (" + std::to_string(body->get_num_items()) + ")";
@@ -1773,6 +1773,8 @@ namespace QuickMedia {
goto sync_end;
}
+ add_new_invites();
+
next_batch_json = &GetMember(json_root, "next_batch");
if(next_batch_json->IsString()) {
set_next_batch(next_batch_json->GetString(), true);
@@ -2037,6 +2039,14 @@ namespace QuickMedia {
}
}
+ void Matrix::add_new_invites() {
+ std::lock_guard<std::mutex> lock(invite_mutex);
+ for(auto &[room_id, invite] : invites) {
+ ui_thread_tasks.push([this, room_id{std::move(room_id)}, invite{std::move(invite)}]{ delegate->add_invite(room_id, std::move(invite)); });
+ }
+ invites.clear();
+ }
+
PluginResult Matrix::parse_sync_response(const rapidjson::Document &root, bool initial_sync) {
if(!root.IsObject())
return PluginResult::ERR;
@@ -3673,8 +3683,7 @@ namespace QuickMedia {
invite.timestamp = timestamp;
std::string room_id_str(room_id.GetString(), room_id.GetStringLength());
- if(set_invite(room_id_str, invite))
- ui_thread_tasks.push([this, room_id_str{std::move(room_id_str)}, invite{std::move(invite)}]{ delegate->add_invite(room_id_str, std::move(invite)); });
+ set_invite(room_id_str, invite);
break;
}
@@ -3918,6 +3927,16 @@ namespace QuickMedia {
case '\n':
case '\r':
case '\t':
+ case '?':
+ case ',':
+ case ')':
+ case '(':
+ case '[':
+ case ']':
+ case '{':
+ case '}':
+ case '!':
+ case ';':
case '@': {
user_id_finished = true;
user_id_end = i;