aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md5
-rw-r--r--example-config.json5
-rw-r--r--include/Config.hpp7
-rw-r--r--include/FileAnalyzer.hpp10
-rw-r--r--include/VideoPlayer.hpp33
-rw-r--r--plugins/LocalAnime.hpp21
-rw-r--r--src/AsyncImageLoader.cpp6
-rw-r--r--src/Config.cpp45
-rw-r--r--src/FileAnalyzer.cpp64
-rw-r--r--src/QuickMedia.cpp20
-rw-r--r--src/VideoPlayer.cpp69
-rw-r--r--src/plugins/LocalAnime.cpp42
-rw-r--r--src/plugins/Matrix.cpp4
13 files changed, 230 insertions, 101 deletions
diff --git a/README.md b/README.md
index 91f89bb..9533bc0 100644
--- a/README.md
+++ b/README.md
@@ -161,11 +161,12 @@ Type text and then wait and QuickMedia will automatically search.\
`/id`: Show the room id.
## <a id="config"></a>Config
Config is loaded from `~/.config/quickmedia/config.json` if it exists. See [example-config.json](https://git.dec05eba.com/QuickMedia/plain/example-config.json) for an example config. All fields in the config file are optional.\
-If `use_system_mpv_config` is set to `true` then your systems mpv config in `~/.config/mpv/mpv.conf` and plugins will be used. If you have a mpv plugin installed that uses `input-ipc-server` (such as `mpv-discord`) then it will break quickmedia integration with mpv (especially key inputs such as ctrl+r).
+If `use_system_mpv_config` is set to `true` then your systems mpv config in `~/.config/mpv/mpv.conf` and plugins will be used.
## Theme
Theme is loaded from `~/.config/quickmedia/themes/<theme-name>.json` if it exists or from `/usr/share/quickmedia/themes`. Theme name is set in `~/.config/quickmedia/config.json` under the variable `theme`.\
Default themes available: `default, nord`.\
-See [default.json](https://git.dec05eba.com/QuickMedia/plain/themes/default.json) for an example theme.
+See [default.json](https://git.dec05eba.com/QuickMedia/plain/themes/default.json) for an example theme.\
+The `default` there is used by default.
## Environment variables
If `xdg-open` is not installed then the `BROWSER` environment variable is used to open links in a browser.\
Set `QM_PHONE_FACTOR=1` to disable the room list side panel in matrix.
diff --git a/example-config.json b/example-config.json
index 566bbae..321f331 100644
--- a/example-config.json
+++ b/example-config.json
@@ -26,6 +26,11 @@
"sort_by_name": false,
"sort_chapters_by_name": false
},
+ "local_anime": {
+ "directory": "",
+ "sort_by_name": false,
+ "sort_episodes_by_name": true
+ },
"use_system_fonts": false,
"use_system_mpv_config": false,
"theme": "default",
diff --git a/include/Config.hpp b/include/Config.hpp
index b2abbf1..ee9e190 100644
--- a/include/Config.hpp
+++ b/include/Config.hpp
@@ -36,6 +36,12 @@ namespace QuickMedia {
bool sort_chapters_by_name = false;
};
+ struct LocalAnimeConfig {
+ std::string directory;
+ bool sort_by_name = false;
+ bool sort_episodes_by_name = true;
+ };
+
struct Config {
Config() = default;
Config(const Config&) = delete;
@@ -47,6 +53,7 @@ namespace QuickMedia {
InputConfig input;
VideoConfig video;
LocalMangaConfig local_manga;
+ LocalAnimeConfig local_anime;
bool use_system_fonts = false;
bool use_system_mpv_config = false;
std::string theme = "default";
diff --git a/include/FileAnalyzer.hpp b/include/FileAnalyzer.hpp
index b06ef91..acc62d7 100644
--- a/include/FileAnalyzer.hpp
+++ b/include/FileAnalyzer.hpp
@@ -5,6 +5,8 @@
#include <string>
namespace QuickMedia {
+ class FileAnalyzer;
+
struct Dimensions {
int width;
int height;
@@ -17,6 +19,7 @@ namespace QuickMedia {
VIDEO_MPEG,
VIDEO_WEBM,
VIDEO_FLV,
+ VIDEO_WMV,
AUDIO_BASIC,
AUDIO_AIFF,
AUDIO_MPEG,
@@ -40,13 +43,15 @@ namespace QuickMedia {
bool is_video_ext(const char *ext);
// Set |width| or |height| to 0 to disable scaling.
- // This function is async.
- bool video_get_first_frame(const char *filepath, const char *destination_path, int width = 0, int height = 0);
+ // TODO: Make this async
+ bool video_get_first_frame(const FileAnalyzer &file, const char *destination_path, int width = 0, int height = 0);
class FileAnalyzer {
public:
FileAnalyzer();
bool load_file(const char *filepath, bool load_file_metadata = true);
+
+ const std::string& get_filepath() const;
ContentType get_content_type() const;
size_t get_file_size() const;
std::optional<Dimensions> get_dimensions() const;
@@ -55,6 +60,7 @@ namespace QuickMedia {
FileAnalyzer(FileAnalyzer&) = delete;
FileAnalyzer& operator=(FileAnalyzer&) = delete;
private:
+ std::string filepath;
ContentType content_type;
size_t file_size;
std::optional<Dimensions> dimensions;
diff --git a/include/VideoPlayer.hpp b/include/VideoPlayer.hpp
index f75d76f..989bfd2 100644
--- a/include/VideoPlayer.hpp
+++ b/include/VideoPlayer.hpp
@@ -33,14 +33,31 @@ namespace QuickMedia {
EXITED
};
+ struct StartupArgs {
+ std::string path;
+ std::string audio_path;
+ mgl::WindowHandle parent_window;
+ bool no_video = false;
+ bool use_system_mpv_config = false;
+ bool use_system_input_config = false; // use_system_mpv_config has to be true for this
+ bool keep_open = false;
+ std::string resource_root;
+ int monitor_height = 1080;
+ bool use_youtube_dl = false;
+ std::string title;
+ std::string start_time;
+ std::vector<MediaChapter> chapters;
+ std::string plugin_name;
+ };
+
// @event_callback is called from another thread
- VideoPlayer(bool no_video, bool use_system_mpv_config, bool keep_open, EventCallbackFunc event_callback, VideoPlayerWindowCreateCallback window_create_callback, const std::string &resource_root, int monitor_height, std::string plugin_name);
+ VideoPlayer(StartupArgs startup_args, EventCallbackFunc event_callback, VideoPlayerWindowCreateCallback window_create_callback);
~VideoPlayer();
VideoPlayer(const VideoPlayer&) = delete;
VideoPlayer& operator=(const VideoPlayer&) = delete;
// |audio_path| is only set when video and audio are separate files/urls.
- Error load_video(const char *path, const char *audio_path, mgl::WindowHandle parent_window, bool use_youtube_dl, const std::string &title, const std::string &start_time = "", const std::vector<MediaChapter> &chapters = {});
+ Error load_video();
// Should be called every update frame
Error update();
@@ -52,24 +69,19 @@ namespace QuickMedia {
private:
uint32_t get_next_request_id();
Error send_command(Json::Value &json_root, Json::Value *result, Json::ValueType result_type);
- Error launch_video_process(const char *path, const char *audio_path, mgl::WindowHandle parent_window, const std::string &title, const std::string &start_time);
+ Error launch_video_process();
VideoPlayer::Error read_ipc_func();
private:
- std::string plugin_name;
- bool no_video;
- bool use_system_mpv_config;
- bool keep_open;
- bool use_youtube_dl;
+ StartupArgs startup_args;
+
pid_t video_process_id;
mgl::Clock retry_timer;
int connect_tries;
int find_window_tries;
- int monitor_height;
int ipc_socket = -1;
EventCallbackFunc event_callback;
VideoPlayerWindowCreateCallback window_create_callback;
mgl::WindowHandle window_handle;
- mgl::WindowHandle parent_window;
Display *display;
unsigned int request_id_counter;
unsigned int expected_request_id;
@@ -81,7 +93,6 @@ namespace QuickMedia {
ERROR
};
ResponseDataStatus response_data_status;
- std::string resource_root;
char tmp_chapters_filepath[27];
};
}
diff --git a/plugins/LocalAnime.hpp b/plugins/LocalAnime.hpp
new file mode 100644
index 0000000..cd240d3
--- /dev/null
+++ b/plugins/LocalAnime.hpp
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "Page.hpp"
+
+// TODO: Progress > 90% = fully watched (because ending might have been skipped)
+
+namespace QuickMedia {
+ class LocalAnimeSearchPage : public LazyFetchPage {
+ public:
+ LocalAnimeSearchPage(Program *program) : LazyFetchPage(program) {}
+ const char* get_title() const override { return "Search"; }
+ bool search_is_filter() override { return true; }
+ PluginResult submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) override;
+ PluginResult lazy_fetch(BodyItems &result_items) override;
+ const char* get_bookmark_name() const override { return "local-anime"; }
+ bool reload_on_page_change() override { return true; }
+ bool reseek_to_body_item_by_url() override { return true; }
+ std::shared_ptr<BodyItem> get_bookmark_body_item(BodyItem *selected_item) override;
+ void toggle_read(BodyItem *selected_item) override;
+ };
+} \ No newline at end of file
diff --git a/src/AsyncImageLoader.cpp b/src/AsyncImageLoader.cpp
index ddcb604..d7fbfbe 100644
--- a/src/AsyncImageLoader.cpp
+++ b/src/AsyncImageLoader.cpp
@@ -161,7 +161,7 @@ namespace QuickMedia {
void AsyncImageLoader::load_create_thumbnail(const Path &thumbnail_path, const Path &thumbnail_path_resized, ThumbnailData *thumbnail_data, mgl::vec2i resize_target_size) {
FileAnalyzer file_analyzer;
- if(!file_analyzer.load_file(thumbnail_path.data.c_str(), false)) {
+ if(!file_analyzer.load_file(thumbnail_path.data.c_str(), true)) {
fprintf(stderr, "Failed to convert %s to a thumbnail\n", thumbnail_path.data.c_str());
thumbnail_data->image = std::make_unique<mgl::Image>();
thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
@@ -169,10 +169,10 @@ namespace QuickMedia {
}
if(is_content_type_video(file_analyzer.get_content_type())) {
- if(video_get_first_frame(thumbnail_path.data.c_str(), thumbnail_path_resized.data.c_str(), resize_target_size.x, resize_target_size.y)) {
+ if(video_get_first_frame(file_analyzer, thumbnail_path_resized.data.c_str(), resize_target_size.x, resize_target_size.y)) {
thumbnail_data->loading_state = LoadingState::READY_TO_LOAD;
} else {
- fprintf(stderr, "Failed to get first frame of %s\n", thumbnail_path.data.c_str());
+ fprintf(stderr, "Failed to get video frame of %s\n", thumbnail_path.data.c_str());
thumbnail_data->image = std::make_unique<mgl::Image>();
thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
}
diff --git a/src/Config.cpp b/src/Config.cpp
index 780bf32..e98f4c7 100644
--- a/src/Config.cpp
+++ b/src/Config.cpp
@@ -131,6 +131,36 @@ namespace QuickMedia {
config->video.max_height = max_height_json.asInt();
}
+ const Json::Value &local_manga_json = json_root["local_manga"];
+ if(local_manga_json.isObject()) {
+ const Json::Value &directory_json = local_manga_json["directory"];
+ if(directory_json.isString())
+ config->local_manga.directory = directory_json.asString();
+
+ const Json::Value &sort_by_name_json = local_manga_json["sort_by_name"];
+ if(sort_by_name_json.isBool())
+ config->local_manga.sort_by_name = sort_by_name_json.asBool();
+
+ const Json::Value &sort_chapters_by_name_json = local_manga_json["sort_chapters_by_name"];
+ if(sort_chapters_by_name_json.isBool())
+ config->local_manga.sort_chapters_by_name = sort_chapters_by_name_json.asBool();
+ }
+
+ const Json::Value &local_anime_json = json_root["local_anime"];
+ if(local_anime_json.isObject()) {
+ const Json::Value &directory_json = local_anime_json["directory"];
+ if(directory_json.isString())
+ config->local_anime.directory = directory_json.asString();
+
+ const Json::Value &sort_by_name_json = local_anime_json["sort_by_name"];
+ if(sort_by_name_json.isBool())
+ config->local_anime.sort_by_name = sort_by_name_json.asBool();
+
+ const Json::Value &sort_episodes_by_name_json = local_anime_json["sort_episodes_by_name"];
+ if(sort_episodes_by_name_json.isBool())
+ config->local_anime.sort_episodes_by_name = sort_episodes_by_name_json.asBool();
+ }
+
const Json::Value &use_system_fonts_json = json_root["use_system_fonts"];
if(use_system_fonts_json.isBool())
config->use_system_fonts = use_system_fonts_json.asBool();
@@ -154,21 +184,6 @@ namespace QuickMedia {
const Json::Value &spacing_scale = json_root["spacing_scale"];
if(spacing_scale.isNumeric())
config->spacing_scale = spacing_scale.asFloat();
-
- const Json::Value &local_manga_json = json_root["local_manga"];
- if(local_manga_json.isObject()) {
- const Json::Value &directory_json = local_manga_json["directory"];
- if(directory_json.isString())
- config->local_manga.directory = directory_json.asString();
-
- const Json::Value &sort_by_name_json = local_manga_json["sort_by_name"];
- if(sort_by_name_json.isBool())
- config->local_manga.sort_by_name = sort_by_name_json.asBool();
-
- const Json::Value &sort_chapters_by_name_json = local_manga_json["sort_chapters_by_name"];
- if(sort_chapters_by_name_json.isBool())
- config->local_manga.sort_chapters_by_name = sort_chapters_by_name_json.asBool();
- }
}
const Config& get_config() {
diff --git a/src/FileAnalyzer.cpp b/src/FileAnalyzer.cpp
index 6f1c1ee..4e4470e 100644
--- a/src/FileAnalyzer.cpp
+++ b/src/FileAnalyzer.cpp
@@ -21,7 +21,7 @@ namespace QuickMedia {
// https://mimesniff.spec.whatwg.org/
// TODO: Test all of these
- static const std::array<MagicNumber, 30> magic_numbers = {
+ static const std::array<MagicNumber, 31> 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', 'm', 'p', '4', '2'}, 12, ContentType::VIDEO_MP4 },
@@ -34,6 +34,7 @@ namespace QuickMedia {
MagicNumber{ {0x00, 0x00, 0x01, 0xB3}, 4, ContentType::VIDEO_MPEG },
MagicNumber{ {0x1A, 0x45, 0xDF, 0xA3}, 4, ContentType::VIDEO_WEBM },
MagicNumber{ {'F', 'L', 'V', 0x01}, 4, ContentType::VIDEO_FLV },
+ MagicNumber{ {0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF}, 7, ContentType::VIDEO_WMV },
MagicNumber{ {'.', 's', 'n', 'd'}, 4, ContentType::AUDIO_BASIC },
MagicNumber{ {'F', 'O', 'R', 'M', -1, -1, -1, -1, 'A', 'I', 'F', 'F'}, 12, ContentType::AUDIO_AIFF },
MagicNumber{ { 'I', 'D', '3' }, 3, ContentType::AUDIO_MPEG },
@@ -56,7 +57,7 @@ namespace QuickMedia {
};
bool is_content_type_video(ContentType content_type) {
- return content_type >= ContentType::VIDEO_AVI && content_type <= ContentType::VIDEO_FLV;
+ return content_type >= ContentType::VIDEO_AVI && content_type <= ContentType::VIDEO_WMV;
}
bool is_content_type_audio(ContentType content_type) {
@@ -75,6 +76,7 @@ namespace QuickMedia {
case ContentType::VIDEO_MPEG: return "video/mpeg";
case ContentType::VIDEO_WEBM: return "video/webm";
case ContentType::VIDEO_FLV: return "video/x-flv";
+ case ContentType::VIDEO_WMV: return "video/x-ms-asf";
case ContentType::AUDIO_BASIC: return "audio/basic";
case ContentType::AUDIO_AIFF: return "audio/aiff";
case ContentType::AUDIO_MPEG: return "audio/mpeg";
@@ -106,6 +108,7 @@ namespace QuickMedia {
|| strcase_equals(ext, ".flv")
|| strcase_equals(ext, ".vob")
|| strcase_equals(ext, ".ogv")
+ || strcase_equals(ext, ".ogg")
|| strcase_equals(ext, ".avi")
//|| strcase_equals(ext, ".ts")
|| strcase_equals(ext, ".mov")
@@ -124,30 +127,37 @@ namespace QuickMedia {
return 0;
}
- bool video_get_first_frame(const char *filepath, const char *destination_path, int width, int height) {
+ bool video_get_first_frame(const FileAnalyzer &file, const char *destination_path, int width, int height) {
Path destination_path_tmp = destination_path;
destination_path_tmp.append(".ftmp");
- const char *program_args[] = { "ffmpeg", "-y", "-v", "quiet", "-i", filepath, "-vframes", "1", "-f", "singlejpeg", "--", destination_path_tmp.data.c_str(), nullptr };
- std::string ffmpeg_result;
- if(exec_program(program_args, nullptr, nullptr) != 0) {
- fprintf(stderr, "Failed to execute ffmpeg, maybe its not installed?\n");
- return false;
- }
+ 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);
if(width > 0 && height > 0) {
- FileAnalyzer file_analyzer;
- const bool success = file_analyzer.load_file(destination_path_tmp.data.c_str(), false) && create_thumbnail(destination_path_tmp, destination_path, mgl::vec2i(width, height), file_analyzer.get_content_type(), false);
- remove(destination_path_tmp.data.c_str());
- return success;
+ char framesize[128];
+ snprintf(framesize, sizeof(framesize), "%dx%d", width, height);
+
+ const char *program_args[] = { "ffmpeg", "-y", "-v", "quiet", "-ss", middle_seconds_str, "-i", file.get_filepath().c_str(), "-vframes", "1", "-f", "singlejpeg", "-s", framesize, "--", destination_path_tmp.data.c_str(), nullptr };
+ if(exec_program(program_args, nullptr, nullptr) != 0) {
+ fprintf(stderr, "Failed to execute ffmpeg, maybe its not installed?\n");
+ return false;
+ }
} else {
- return rename_atomic(destination_path_tmp.data.c_str(), destination_path) == 0;
+ const char *program_args[] = { "ffmpeg", "-y", "-v", "quiet", "-ss", middle_seconds_str, "-i", file.get_filepath().c_str(), "-vframes", "1", "-f", "singlejpeg", "--", destination_path_tmp.data.c_str(), nullptr };
+ if(exec_program(program_args, nullptr, nullptr) != 0) {
+ fprintf(stderr, "Failed to execute ffmpeg, maybe its not installed?\n");
+ return false;
+ }
}
+
+ return rename_atomic(destination_path_tmp.data.c_str(), destination_path) == 0;
}
// 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", "--", filepath, nullptr };
+ const char *program_args[] = { "ffprobe", "-v", "quiet", "-print_format", "json", "-show_streams", "-show_format", "--", filepath, nullptr };
std::string ffprobe_result;
if(exec_program(program_args, accumulate_string, &ffprobe_result) != 0) {
fprintf(stderr, "Failed to execute ffprobe, maybe its not installed?\n");
@@ -181,20 +191,19 @@ namespace QuickMedia {
if(strcmp(codec_type.asCString(), "video") == 0) {
const Json::Value &width_json = stream_json["width"];
const Json::Value &height_json = stream_json["height"];
- const Json::Value &duration_json = stream_json["duration"];
if(width_json.isNumeric() && height_json.isNumeric())
dimensions = { width_json.asInt(), height_json.asInt() };
- if(duration_json.isString())
- duration_seconds = atof(duration_json.asCString());
- break;
- } else if(strcmp(codec_type.asCString(), "audio") == 0) {
- const Json::Value &duration_json = stream_json["duration"];
- if(duration_json.isString())
- duration_seconds = atof(duration_json.asCString());
- // No break here because if there is video after this, we want it to overwrite this
}
}
+ const Json::Value &format_json = json_root["format"];
+ if(!format_json.isObject())
+ return true;
+
+ const Json::Value &duration_json = format_json["duration"];
+ if(duration_json.isString())
+ duration_seconds = atof(duration_json.asCString());
+
return true;
}
@@ -231,7 +240,7 @@ namespace QuickMedia {
unsigned char magic_number_buffer[MAGIC_NUMBER_BUFFER_SIZE];
size_t num_bytes_read = fread(magic_number_buffer, 1, sizeof(magic_number_buffer), file);
- if(feof(file)) {
+ if(feof(file) || num_bytes_read != sizeof(magic_number_buffer)) {
perror(filepath);
fclose(file);
return false;
@@ -263,10 +272,15 @@ namespace QuickMedia {
duration_seconds = std::nullopt;
}
+ this->filepath = filepath;
loaded = true;
return true;
}
+ const std::string& FileAnalyzer::get_filepath() const {
+ return filepath;
+ }
+
ContentType FileAnalyzer::get_content_type() const {
return content_type;
}
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index e317ce3..99e8f1b 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -2892,8 +2892,24 @@ namespace QuickMedia {
}
}
- video_player = std::make_unique<VideoPlayer>(is_audio_only, get_config().use_system_mpv_config, is_matrix && !is_youtube, video_event_callback, on_window_create, resources_root, video_max_height, plugin_name);
- VideoPlayer::Error err = video_player->load_video(v.c_str(), a.c_str(), window.get_system_handle(), use_youtube_dl, video_title, start_time, media_chapters);
+ VideoPlayer::StartupArgs startup_args;
+ startup_args.path = v;
+ startup_args.audio_path = a;
+ startup_args.parent_window = window.get_system_handle();
+ startup_args.no_video = is_audio_only;
+ startup_args.use_system_mpv_config = get_config().use_system_mpv_config;
+ startup_args.use_system_input_config = false;
+ startup_args.keep_open = is_matrix && !is_youtube;
+ startup_args.resource_root = resources_root;
+ startup_args.monitor_height = video_max_height;
+ startup_args.use_youtube_dl = use_youtube_dl;
+ startup_args.title = video_title;
+ startup_args.start_time = start_time;
+ startup_args.chapters = std::move(media_chapters);
+ startup_args.plugin_name = plugin_name;
+
+ video_player = std::make_unique<VideoPlayer>(std::move(startup_args), video_event_callback, on_window_create);
+ VideoPlayer::Error err = video_player->load_video();
if(err != VideoPlayer::Error::OK) {
std::string err_msg = "Failed to play url: ";
err_msg += video_page->get_url();
diff --git a/src/VideoPlayer.cpp b/src/VideoPlayer.cpp
index 76fa82a..4be0671 100644
--- a/src/VideoPlayer.cpp
+++ b/src/VideoPlayer.cpp
@@ -90,27 +90,20 @@ namespace QuickMedia {
}
}
- VideoPlayer::VideoPlayer(bool no_video, bool use_system_mpv_config, bool keep_open, EventCallbackFunc _event_callback, VideoPlayerWindowCreateCallback _window_create_callback, const std::string &resource_root, int monitor_height, std::string plugin_name) :
+ VideoPlayer::VideoPlayer(StartupArgs startup_args, EventCallbackFunc _event_callback, VideoPlayerWindowCreateCallback _window_create_callback) :
exit_status(0),
- plugin_name(std::move(plugin_name)),
- no_video(no_video),
- use_system_mpv_config(use_system_mpv_config),
- keep_open(keep_open),
- use_youtube_dl(true),
+ startup_args(std::move(startup_args)),
video_process_id(-1),
connect_tries(0),
find_window_tries(0),
- monitor_height(monitor_height),
event_callback(_event_callback),
window_create_callback(_window_create_callback),
window_handle(0),
- parent_window(0),
display(nullptr),
request_id_counter(1),
expected_request_id(0),
request_response_data(Json::nullValue),
- response_data_status(ResponseDataStatus::NONE),
- resource_root(resource_root)
+ response_data_status(ResponseDataStatus::NONE)
{
tmp_chapters_filepath[0] = '\0';
display = XOpenDisplay(NULL);
@@ -118,7 +111,7 @@ namespace QuickMedia {
show_notification("QuickMedia", "Failed to open display to X11 server", Urgency::CRITICAL);
abort();
}
- fprintf(stderr, "Video max height: %d\n", monitor_height);
+ fprintf(stderr, "Video max height: %d\n", startup_args.monitor_height);
}
VideoPlayer::~VideoPlayer() {
@@ -147,9 +140,7 @@ namespace QuickMedia {
return path;
}
- VideoPlayer::Error VideoPlayer::launch_video_process(const char *path, const char *audio_path, mgl::WindowHandle _parent_window, const std::string &title, const std::string &start_time) {
- parent_window = _parent_window;
-
+ VideoPlayer::Error VideoPlayer::launch_video_process() {
int fd[2];
if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
perror("Failed to create socketpair for video player");
@@ -162,13 +153,13 @@ namespace QuickMedia {
fcntl(ipc_socket, F_SETFL, flags | O_NONBLOCK);
const std::string ipc_fd = std::to_string(fd[1]);
- std::string input_conf = "--input-conf=" + resource_root + "input.conf";
+ std::string input_conf = "--input-conf=" + startup_args.resource_root + "input.conf";
std::string cache_dir = "--cache-dir=" + std::move(get_cache_dir().join("media").data);
std::string wid_arg = "--wid=";
- wid_arg += std::to_string(parent_window);
+ wid_arg += std::to_string(startup_args.parent_window);
- std::string video_player_filepath = resource_root + "/video_player/sibs-build/linux_x86_64/"
+ std::string video_player_filepath = startup_args.resource_root + "/video_player/sibs-build/linux_x86_64/"
#ifdef NDEBUG
"release/"
#else
@@ -203,27 +194,29 @@ namespace QuickMedia {
"--osc=yes",
//"--force_all_formats=no",
cache_dir.c_str(),
- input_conf.c_str(),
wid_arg.c_str(),
"--ipc-fd",
ipc_fd.c_str()
});
+
+ if(!startup_args.use_system_input_config)
+ args.push_back(input_conf.c_str());
if(is_running_wayland()) {
args.push_back("--gpu-context=x11egl");
fprintf(stderr, "Wayland detected. Launching mpv in x11egl mode\n");
}
- if(keep_open)
+ if(startup_args.keep_open)
args.push_back("--keep-open=yes");
std::string ytdl_format;
- if(no_video)
+ if(startup_args.no_video)
ytdl_format = "--ytdl-format=bestaudio/best";
else
- ytdl_format = "--ytdl-format=bestvideo[height<=?" + std::to_string(monitor_height) + "]+bestaudio/best";
+ ytdl_format = "--ytdl-format=bestvideo[height<=?" + std::to_string(startup_args.monitor_height) + "]+bestaudio/best";
- if(!use_youtube_dl)
+ if(!startup_args.use_youtube_dl)
args.push_back("--ytdl=no");
else
args.push_back(ytdl_format.c_str());
@@ -233,7 +226,7 @@ namespace QuickMedia {
if(get_file_type(mpris_path) == FileType::REGULAR)
mpris_arg = "--scripts=" + mpris_path.data;
- if(!use_system_mpv_config) {
+ if(!startup_args.use_system_mpv_config) {
args.insert(args.end(), {
"--config=no",
"--profile=gpu-hq",
@@ -246,12 +239,12 @@ namespace QuickMedia {
}
std::string force_media_title_arg;
- if(!title.empty()) {
- force_media_title_arg = "--force-media-title=" + title;
+ if(!startup_args.title.empty()) {
+ force_media_title_arg = "--force-media-title=" + startup_args.title;
args.push_back(force_media_title_arg.c_str());
}
- if(no_video)
+ if(startup_args.no_video)
args.push_back("--video=no");
std::string chapters_file_arg;
@@ -260,18 +253,18 @@ namespace QuickMedia {
args.push_back(chapters_file_arg.c_str());
}
- if(audio_path && audio_path[0] != '\0') {
+ if(!startup_args.audio_path.empty()) {
args.push_back("--audio-file");
- args.push_back(audio_path);
+ args.push_back(startup_args.audio_path.c_str());
}
std::string start_time_arg;
- if(!start_time.empty()) {
- start_time_arg = "--start=" + start_time;
+ if(!startup_args.start_time.empty()) {
+ start_time_arg = "--start=" + startup_args.start_time;
args.push_back(start_time_arg.c_str());
}
- args.insert(args.end(), { "--", path, nullptr });
+ args.insert(args.end(), { "--", startup_args.path.c_str(), nullptr });
if(exec_program_async(args.data(), &video_process_id) != 0) {
close(fd[1]);
@@ -284,18 +277,16 @@ namespace QuickMedia {
return Error::OK;
}
- VideoPlayer::Error VideoPlayer::load_video(const char *path, const char *audio_path, mgl::WindowHandle _parent_window, bool use_youtube_dl, const std::string &title, const std::string &start_time, const std::vector<MediaChapter> &chapters) {
+ VideoPlayer::Error VideoPlayer::load_video() {
// This check is to make sure we dont change window that the video belongs to. This is not a usecase we will have so
// no need to support it for now at least.
- assert(parent_window == 0 || parent_window == _parent_window);
- assert(path);
- this->use_youtube_dl = use_youtube_dl;
- if(!create_tmp_file_with_chapters_data(tmp_chapters_filepath, chapters))
+ assert(!startup_args.path.empty());
+ if(!create_tmp_file_with_chapters_data(tmp_chapters_filepath, startup_args.chapters))
fprintf(stderr, "Warning: failed to create chapters file. Chapters will not be displayed\n");
- fprintf(stderr, "Playing video: %s, audio: %s\n", path ? path : "", audio_path ? audio_path : "");
+ fprintf(stderr, "Playing video: %s, audio: %s\n", startup_args.path.c_str(), startup_args.audio_path.c_str());
if(video_process_id == -1)
- return launch_video_process(path, audio_path, _parent_window, title, start_time);
+ return launch_video_process();
fprintf(stderr, "TODO: Implement VideoPlayer::load_video without restarting the video player\n");
abort();
@@ -341,7 +332,7 @@ namespace QuickMedia {
if(window_handle == 0 && retry_timer.get_elapsed_time_seconds() >= RETRY_TIME_SEC) {
retry_timer.restart();
- std::vector<Window> child_windows = get_child_window(display, parent_window);
+ std::vector<Window> child_windows = get_child_window(display, startup_args.parent_window);
size_t num_children = child_windows.size();
if(num_children == 0) {
++find_window_tries;
diff --git a/src/plugins/LocalAnime.cpp b/src/plugins/LocalAnime.cpp
new file mode 100644
index 0000000..4bc296a
--- /dev/null
+++ b/src/plugins/LocalAnime.cpp
@@ -0,0 +1,42 @@
+#include "../../plugins/LocalAnime.hpp"
+#include "../../include/Config.hpp"
+#include "../../include/Storage.hpp"
+#include "../../include/Notification.hpp"
+
+namespace QuickMedia {
+ static bool validate_local_anime_dir_config_is_set() {
+ if(get_config().local_anime.directory.empty()) {
+ show_notification("QuickMedia", "local_anime.directory config is not set", Urgency::CRITICAL);
+ return false;
+ }
+
+ if(get_file_type(get_config().local_anime.directory) != FileType::DIRECTORY) {
+ show_notification("QuickMedia", "local_anime.directory config is not set to a valid directory", Urgency::CRITICAL);
+ return false;
+ }
+
+ return true;
+ }
+
+ PluginResult LocalAnimeSearchPage::submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) {
+ if(!validate_local_anime_dir_config_is_set())
+ return PluginResult::OK;
+
+ return PluginResult::ERR;
+ }
+
+ PluginResult LocalAnimeSearchPage::lazy_fetch(BodyItems &result_items) {
+ if(!validate_local_anime_dir_config_is_set())
+ return PluginResult::OK;
+
+ return PluginResult::ERR;
+ }
+
+ std::shared_ptr<BodyItem> LocalAnimeSearchPage::get_bookmark_body_item(BodyItem *selected_item) {
+ return nullptr;
+ }
+
+ void LocalAnimeSearchPage::toggle_read(BodyItem *selected_item) {
+ // TODO:
+ }
+} \ No newline at end of file
diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp
index 6b61aed..367c777 100644
--- a/src/plugins/Matrix.cpp
+++ b/src/plugins/Matrix.cpp
@@ -3690,7 +3690,7 @@ namespace QuickMedia {
PluginResult Matrix::upload_file(RoomData *room, const std::string &filepath, UploadInfo &file_info, UploadInfo &thumbnail_info, std::string &err_msg, bool upload_thumbnail) {
FileAnalyzer file_analyzer;
- if(!file_analyzer.load_file(filepath.c_str())) {
+ if(!file_analyzer.load_file(filepath.c_str(), true)) {
err_msg = "Failed to load " + filepath;
return PluginResult::ERR;
}
@@ -3722,7 +3722,7 @@ namespace QuickMedia {
char tmp_filename[] = "/tmp/quickmedia_video_frame_XXXXXX";
int tmp_file = mkstemp(tmp_filename);
if(tmp_file != -1) {
- if(video_get_first_frame(filepath.c_str(), tmp_filename, thumbnail_max_size.x, thumbnail_max_size.y)) {
+ if(video_get_first_frame(file_analyzer, tmp_filename, thumbnail_max_size.x, thumbnail_max_size.y)) {
UploadInfo upload_info_ignored; // Ignore because it wont be set anyways. Thumbnails dont have thumbnails.
PluginResult upload_thumbnail_result = upload_file(room, tmp_filename, thumbnail_info, upload_info_ignored, err_msg, false);
if(upload_thumbnail_result != PluginResult::OK) {