You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
142 lines
5.1 KiB
142 lines
5.1 KiB
#include "thumbnailer.h"
|
|
#include "mimetype.h"
|
|
#include <string>
|
|
#include <QDebug>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
namespace Fm {
|
|
|
|
std::mutex Thumbnailer::mutex_;
|
|
std::vector<std::shared_ptr<Thumbnailer>> Thumbnailer::allThumbnailers_;
|
|
|
|
Thumbnailer::Thumbnailer(const char* id, GKeyFile* kf):
|
|
id_{g_strdup(id)},
|
|
try_exec_{g_key_file_get_string(kf, "Thumbnailer Entry", "TryExec", nullptr)},
|
|
exec_{g_key_file_get_string(kf, "Thumbnailer Entry", "Exec", nullptr)} {
|
|
}
|
|
|
|
CStrPtr Thumbnailer::commandForUri(const char* uri, const char* output_file, guint size) const {
|
|
if(exec_) {
|
|
/* FIXME: how to handle TryExec? */
|
|
|
|
/* parse the command line and do required substitutions according to:
|
|
* https://developer.gnome.org/integration-guide/stable/thumbnailer.html.en
|
|
*/
|
|
GString* cmd_line = g_string_sized_new(1024);
|
|
const char* p;
|
|
for(p = exec_.get(); *p; ++p) {
|
|
if(G_LIKELY(*p != '%')) {
|
|
g_string_append_c(cmd_line, *p);
|
|
}
|
|
else {
|
|
char* quoted;
|
|
++p;
|
|
switch(*p) {
|
|
case '\0':
|
|
break;
|
|
case 's':
|
|
g_string_append_printf(cmd_line, "%d", size);
|
|
break;
|
|
case 'i': {
|
|
char* src_path = g_filename_from_uri(uri, nullptr, nullptr);
|
|
if(src_path) {
|
|
quoted = g_shell_quote(src_path);
|
|
g_string_append(cmd_line, quoted);
|
|
g_free(quoted);
|
|
g_free(src_path);
|
|
}
|
|
break;
|
|
}
|
|
case 'u':
|
|
quoted = g_shell_quote(uri);
|
|
g_string_append(cmd_line, quoted);
|
|
g_free(quoted);
|
|
break;
|
|
case 'o':
|
|
g_string_append(cmd_line, output_file);
|
|
break;
|
|
default:
|
|
g_string_append_c(cmd_line, '%');
|
|
if(*p != '%') {
|
|
g_string_append_c(cmd_line, *p);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return CStrPtr{g_string_free(cmd_line, FALSE)};
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool Thumbnailer::run(const char* uri, const char* output_file, int size) const {
|
|
auto cmd = commandForUri(uri, output_file, size);
|
|
qDebug() << cmd.get();
|
|
int status;
|
|
bool ret = g_spawn_command_line_sync(cmd.get(), nullptr, nullptr, &status, nullptr);
|
|
return ret && status == 0;
|
|
}
|
|
|
|
static void find_thumbnailers_in_data_dir(std::unordered_map<std::string, const char*>& hash, const char* data_dir) {
|
|
CStrPtr dir_path{g_build_filename(data_dir, "thumbnailers", nullptr)};
|
|
GDir* dir = g_dir_open(dir_path.get(), 0, nullptr);
|
|
if(dir) {
|
|
const char* basename;
|
|
while((basename = g_dir_read_name(dir)) != nullptr) {
|
|
/* we only want filenames with .thumbnailer extension */
|
|
if(G_LIKELY(g_str_has_suffix(basename, ".thumbnailer"))) {
|
|
hash.insert(std::make_pair(basename, data_dir));
|
|
}
|
|
}
|
|
g_dir_close(dir);
|
|
}
|
|
}
|
|
|
|
void Thumbnailer::loadAll() {
|
|
const gchar* const* data_dirs = g_get_system_data_dirs();
|
|
const gchar* const* data_dir;
|
|
|
|
/* use a temporary hash table to collect thumbnailer basenames
|
|
* key: basename of thumbnailer entry file
|
|
* value: data dir the thumbnailer entry file is in */
|
|
std::unordered_map<std::string, const char*> hash;
|
|
|
|
/* load user-specific thumbnailers */
|
|
find_thumbnailers_in_data_dir(hash, g_get_user_data_dir());
|
|
|
|
/* load system-wide thumbnailers */
|
|
for(data_dir = data_dirs; *data_dir; ++data_dir) {
|
|
find_thumbnailers_in_data_dir(hash, *data_dir);
|
|
}
|
|
|
|
/* load all found thumbnailers */
|
|
if(!hash.empty()) {
|
|
std::lock_guard<std::mutex> lock{mutex_};
|
|
GKeyFile* kf = g_key_file_new();
|
|
for(auto& item: hash) {
|
|
auto& base_name = item.first;
|
|
auto& dir_path = item.second;
|
|
CStrPtr file_path{g_build_filename(dir_path, "thumbnailers", base_name.c_str(), nullptr)};
|
|
if(g_key_file_load_from_file(kf, file_path.get(), G_KEY_FILE_NONE, nullptr)) {
|
|
auto thumbnailer = std::make_shared<Thumbnailer>(base_name.c_str(), kf);
|
|
if(thumbnailer->exec_) {
|
|
char** mime_types = g_key_file_get_string_list(kf, "Thumbnailer Entry", "MimeType", nullptr, nullptr);
|
|
if(mime_types) {
|
|
for(char** name = mime_types; *name; ++name) {
|
|
auto mime_type = MimeType::fromName(*name);
|
|
if(mime_type) {
|
|
std::const_pointer_cast<MimeType>(mime_type)->addThumbnailer(thumbnailer);
|
|
}
|
|
}
|
|
g_strfreev(mime_types);
|
|
}
|
|
}
|
|
allThumbnailers_.push_back(std::move(thumbnailer));
|
|
}
|
|
}
|
|
g_key_file_free(kf);
|
|
}
|
|
}
|
|
|
|
} // namespace Fm
|