aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2021-06-14 07:22:05 +0200
committerdec05eba <dec05eba@protonmail.com>2021-06-14 07:23:14 +0200
commitd37b3a7aac87e5f60c49202c824d985e53b7b544 (patch)
tree165fec322b0166b48e298ebcae0e5015578d6a4a /src
parent977547ff7f0b609291da56df32e5642d10c530cd (diff)
Rework around mpv issue: reload video if frozen after seek. Add f5 to reload video, readd video cache
Diffstat (limited to 'src')
-rw-r--r--src/QuickMedia.cpp289
-rw-r--r--src/VideoPlayer.cpp49
2 files changed, 173 insertions, 165 deletions
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index c81d59d..c6a1d2f 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -2376,6 +2376,76 @@ namespace QuickMedia {
return url.substr(timestamp_start, timestamp_end - timestamp_start);
}
+ bool Program::video_download_if_non_streamable(std::string &video_url, std::string &audio_url, bool &is_audio_only, bool &has_embedded_audio, PageType previous_page) {
+ Path video_cache_dir = get_cache_dir().join("media");
+ Path video_path = video_cache_dir;
+ SHA256 sha256;
+ sha256.add(video_url.data(), video_url.size());
+ video_path.join(sha256.getHash());
+
+ if(get_file_type(video_path) == FileType::REGULAR) {
+ fprintf(stderr, "%s is found in cache. Playing from cache...\n", video_url.c_str());
+ video_url = std::move(video_path.data);
+ audio_url.clear();
+ if(no_video) {
+ is_audio_only = true;
+ has_embedded_audio = false;
+ } else {
+ is_audio_only = false;
+ has_embedded_audio = true;
+ }
+ } else {
+ TaskResult video_is_not_streamble_result = run_task_with_loading_screen([video_url]() {
+ return video_url_is_non_streamable_mp4(video_url.c_str());
+ });
+
+ if(video_is_not_streamble_result == TaskResult::TRUE) {
+ fprintf(stderr, "%s is detected to be a non-streamable mp4 file, downloading it before playing it...\n", video_url.c_str());
+ if(create_directory_recursive(video_cache_dir) != 0) {
+ show_notification("QuickMedia", "Failed to create video cache directory", Urgency::CRITICAL);
+ current_page = previous_page;
+ go_to_previous_page = true;
+ return false;
+ }
+
+ TaskResult download_file_result = run_task_with_loading_screen([&video_path, video_url]() {
+ return download_to_file(video_url, video_path.data, {}, true) == DownloadResult::OK;
+ });
+ switch(download_file_result) {
+ case TaskResult::TRUE: {
+ video_url = std::move(video_path.data);
+ audio_url.clear();
+ if(no_video) {
+ is_audio_only = true;
+ has_embedded_audio = false;
+ } else {
+ is_audio_only = false;
+ has_embedded_audio = true;
+ }
+ break;
+ }
+ case TaskResult::FALSE: {
+ show_notification("QuickMedia", "Failed to download " + video_url, Urgency::CRITICAL);
+ current_page = previous_page;
+ go_to_previous_page = true;
+ return false;
+ }
+ case TaskResult::CANCEL: {
+ current_page = previous_page;
+ go_to_previous_page = true;
+ return false;
+ }
+ }
+ } else if(video_is_not_streamble_result == TaskResult::CANCEL) {
+ current_page = previous_page;
+ go_to_previous_page = true;
+ return false;
+ }
+ }
+
+ return true;
+ }
+
#define CLEANMASK(mask) ((mask) & (ShiftMask|ControlMask|Mod1Mask|Mod4Mask|Mod5Mask))
void Program::video_content_page(Page *parent_page, VideoPage *video_page, std::string video_title, bool download_if_streaming_fails, BodyItems &next_play_items, int play_index, int *parent_body_page, const std::string &parent_page_search) {
@@ -2406,127 +2476,77 @@ namespace QuickMedia {
std::function<void(const char*)> video_event_callback;
bool go_to_previous_page = false;
- auto load_video_error_check = [this, &video_title, &video_tasks, &channel_url, previous_page, &go_to_previous_page, &time_watched_timer, &video_loaded, video_page, &video_event_callback, &on_window_create, &video_player_window, is_youtube, is_matrix, download_if_streaming_fails](bool resume_video) mutable {
+ std::string video_url;
+ std::string audio_url;
+
+ bool in_seeking = false;
+ sf::Clock seeking_start_timer;
+ const float seeking_restart_timeout_sec = 4.0f; // TODO: Test if this timeout is good on slow hardware such as pinephone and slow internet
+
+ auto load_video_error_check = [this, &in_seeking, &video_url, &audio_url, &video_title, &video_tasks, &channel_url, previous_page, &go_to_previous_page, &time_watched_timer, &video_loaded, video_page, &video_event_callback, &on_window_create, &video_player_window, is_youtube, is_matrix, download_if_streaming_fails](std::string start_time = "", bool reuse_media_source = false) mutable {
video_player.reset();
channel_url.clear();
video_loaded = false;
+ in_seeking = false;
video_player_window = None;
-
- std::string new_title;
- TaskResult load_result = run_task_with_loading_screen([video_page, &new_title, &channel_url]() {
- return video_page->load(new_title, channel_url) == PluginResult::OK;
- });
-
- if(!new_title.empty())
- video_title = std::move(new_title);
-
- if(load_result == TaskResult::CANCEL) {
- current_page = previous_page;
- go_to_previous_page = true;
- return;
- } else if(load_result == TaskResult::FALSE) {
- show_notification("QuickMedia", "Failed to load media", Urgency::CRITICAL);
- current_page = previous_page;
- go_to_previous_page = true;
- return;
- }
bool is_audio_only = no_video;
bool has_embedded_audio = true;
const int largest_monitor_height = get_largest_monitor_height(disp);
- std::string video_url = video_page->get_video_url(largest_monitor_height, has_embedded_audio);
- std::string audio_url;
- if(video_url.empty() || no_video) {
- video_url = video_page->get_audio_url();
- if(video_url.empty()) {
- video_url = video_page->get_url();
- has_embedded_audio = true;
- } else {
- is_audio_only = true;
- has_embedded_audio = false;
- }
- } else if(!has_embedded_audio) {
- audio_url = video_page->get_audio_url();
- }
-
- bool video_url_is_local = false;
- if(!is_youtube && download_if_streaming_fails) {
- Path video_cache_dir = get_cache_dir().join("media");
- Path video_path = video_cache_dir;
- SHA256 sha256;
- sha256.add(video_url.data(), video_url.size());
- video_path.join(sha256.getHash());
- if(get_file_type(video_path) == FileType::REGULAR) {
- fprintf(stderr, "%s is found in cache. Playing from cache...\n", video_url.c_str());
- video_url = std::move(video_path.data);
- video_url_is_local = true;
- audio_url.clear();
- if(no_video) {
+
+ if(!reuse_media_source) {
+ std::string new_title;
+ TaskResult load_result = run_task_with_loading_screen([video_page, &new_title, &channel_url]() {
+ return video_page->load(new_title, channel_url) == PluginResult::OK;
+ });
+
+ if(!new_title.empty())
+ video_title = std::move(new_title);
+
+ if(load_result == TaskResult::CANCEL) {
+ current_page = previous_page;
+ go_to_previous_page = true;
+ return;
+ } else if(load_result == TaskResult::FALSE) {
+ show_notification("QuickMedia", "Failed to load media", Urgency::CRITICAL);
+ current_page = previous_page;
+ go_to_previous_page = true;
+ return;
+ }
+
+ video_url.clear();
+ audio_url.clear();
+ if(!no_video)
+ video_url = video_page->get_video_url(largest_monitor_height, has_embedded_audio);
+
+ if(video_url.empty() || no_video) {
+ video_url = video_page->get_audio_url();
+ if(video_url.empty()) {
+ video_url = video_page->get_url();
+ has_embedded_audio = true;
+ } else {
is_audio_only = true;
has_embedded_audio = false;
- } else {
- is_audio_only = false;
- has_embedded_audio = true;
}
- } else {
- TaskResult video_is_not_streamble_result = run_task_with_loading_screen([video_url]() {
- return video_url_is_non_streamable_mp4(video_url.c_str());
- });
- if(video_is_not_streamble_result == TaskResult::TRUE) {
- fprintf(stderr, "%s is detected to be a non-streamable mp4 file, downloading it before playing it...\n", video_url.c_str());
- if(create_directory_recursive(video_cache_dir) != 0) {
- show_notification("QuickMedia", "Failed to create video cache directory", Urgency::CRITICAL);
- current_page = previous_page;
- go_to_previous_page = true;
- return;
- }
+ } else if(!has_embedded_audio) {
+ audio_url = video_page->get_audio_url();
+ }
- TaskResult download_file_result = run_task_with_loading_screen([&video_path, video_url]() {
- return download_to_file(video_url, video_path.data, {}, true) == DownloadResult::OK;
- });
- switch(download_file_result) {
- case TaskResult::TRUE: {
- video_url = std::move(video_path.data);
- video_url_is_local = true;
- audio_url.clear();
- if(no_video) {
- is_audio_only = true;
- has_embedded_audio = false;
- } else {
- is_audio_only = false;
- has_embedded_audio = true;
- }
- break;
- }
- case TaskResult::FALSE: {
- show_notification("QuickMedia", "Failed to download " + video_url, Urgency::CRITICAL);
- current_page = previous_page;
- go_to_previous_page = true;
- return;
- }
- case TaskResult::CANCEL: {
- current_page = previous_page;
- go_to_previous_page = true;
- return;
- }
- }
- } else if(video_is_not_streamble_result == TaskResult::CANCEL) {
- current_page = previous_page;
- go_to_previous_page = true;
+ if(!is_youtube && download_if_streaming_fails) {
+ if(!video_download_if_non_streamable(video_url, audio_url, is_audio_only, has_embedded_audio, previous_page))
return;
- }
}
}
- std::string start_time;
- if(is_youtube)
+ const bool is_resume_go_back = !start_time.empty();
+ if(is_youtube && start_time.empty())
start_time = youtube_url_extract_timestamp(video_page->get_url());
time_watched_timer.restart();
watched_videos.insert(video_page->get_url());
- video_player = std::make_unique<VideoPlayer>(is_audio_only, use_system_mpv_config, resume_video, is_matrix && !is_youtube, video_event_callback, on_window_create, resources_root, largest_monitor_height);
- VideoPlayer::Error err = video_player->load_video(video_url.c_str(), audio_url.c_str(), window.getSystemHandle(), plugin_name, video_title, start_time);
+ video_player = std::make_unique<VideoPlayer>(is_audio_only, use_system_mpv_config, is_matrix && !is_youtube, video_event_callback, on_window_create, resources_root, largest_monitor_height);
+ VideoPlayer::Error err = video_player->load_video(video_url.c_str(), audio_url.c_str(), window.getSystemHandle(), is_youtube, video_title, start_time);
if(err != VideoPlayer::Error::OK) {
std::string err_msg = "Failed to play url: ";
err_msg += video_page->get_url();
@@ -2537,15 +2557,17 @@ namespace QuickMedia {
if(video_page->autoplay_next_item())
return;
- std::string url = video_page->get_url();
- video_tasks = AsyncTask<BodyItems>([video_page, url]() {
- BodyItems related_videos = video_page->get_related_media(url);
- video_page->mark_watched();
- return related_videos;
- });
+ if(!is_resume_go_back) {
+ std::string url = video_page->get_url();
+ video_tasks = AsyncTask<BodyItems>([video_page, url]() {
+ BodyItems related_videos = video_page->get_related_media(url);
+ video_page->mark_watched();
+ return related_videos;
+ });
+ }
// TODO: Make this also work for other video plugins
- if(strcmp(plugin_name, "youtube") != 0 || resume_video)
+ if(strcmp(plugin_name, "youtube") != 0 || is_resume_go_back)
return;
std::string video_id;
@@ -2580,29 +2602,35 @@ namespace QuickMedia {
}
};
- video_event_callback = [this, &time_watched_timer, &video_loaded](const char *event_name) mutable {
+ video_event_callback = [&time_watched_timer, &video_loaded, &in_seeking, &seeking_start_timer](const char *event_name) mutable {
+ if(strcmp(event_name, "seek") == 0) {
+ in_seeking = true;
+ seeking_start_timer.restart();
+ }
+
if(strcmp(event_name, "pause") == 0) {
//double time_remaining = 9999.0;
//if(video_player->get_time_remaining(&time_remaining) == VideoPlayer::Error::OK && time_remaining <= 1.0)
// end_of_file = true;
} else if(strcmp(event_name, "playback-restart") == 0) {
//video_player->set_paused(false);
+ in_seeking = false;
} else if(strcmp(event_name, "file-loaded") == 0) {
time_watched_timer.restart();
- if(!video_loaded)
- video_player->set_property("no-resume-playback", true);
video_loaded = true;
+ in_seeking = false;
} else if(strcmp(event_name, "video-reconfig") == 0 || strcmp(event_name, "audio-reconfig") == 0) {
if(!video_loaded) {
video_loaded = true;
time_watched_timer.restart();
}
+ in_seeking = false;
}
//fprintf(stderr, "event name: %s\n", event_name);
};
- load_video_error_check(false);
+ load_video_error_check();
sf::Event event;
@@ -2647,11 +2675,13 @@ namespace QuickMedia {
window_set_fullscreen(disp, window.getSystemHandle(), WindowFullscreenState::TOGGLE);
} else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::C && event.key.control) {
save_video_url_to_clipboard();
+ } else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::F5) {
+ load_video_error_check();
}
}
handle_window_close();
- if(video_player_window && XCheckTypedWindowEvent(disp, video_player_window, KeyPress, &xev)/* && xev.xkey.subwindow == video_player_window*/) {
+ if(video_player && video_player_window && XCheckTypedWindowEvent(disp, video_player_window, KeyPress, &xev)/* && xev.xkey.subwindow == video_player_window*/) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
KeySym pressed_keysym = XKeycodeToKeysym(disp, xev.xkey.keycode, 0);
@@ -2669,6 +2699,11 @@ namespace QuickMedia {
window_set_fullscreen(disp, window.getSystemHandle(), WindowFullscreenState::TOGGLE);
} else if(pressed_keysym == XK_s && pressing_ctrl) {
video_page_download_video(video_page->get_url(), !is_matrix || is_youtube, video_player_window);
+ } else if(pressed_keysym == XK_F5) {
+ in_seeking = false;
+ double resume_start_time = 0.0;
+ video_player->get_time_in_file(&resume_start_time);
+ load_video_error_check(std::to_string((int)resume_start_time));
} else if(pressed_keysym == XK_r && pressing_ctrl) {
bool cancelled = false;
if(video_tasks.valid()) {
@@ -2726,19 +2761,11 @@ namespace QuickMedia {
}
bool page_changed = false;
- page_loop(tabs, 1, [this, &page_changed](const std::vector<Tab> &new_tabs) {
+ double resume_start_time = 0.0;
+ page_loop(tabs, 1, [this, &page_changed, &resume_start_time](const std::vector<Tab> &new_tabs) {
if(!page_changed && new_tabs.size() == 1 && new_tabs[0].page->get_type() == PageTypez::VIDEO) {
- window.setMouseCursorVisible(true);
- if(video_player) {
- video_player->quit_and_save_watch_later();
- while(true) {
- VideoPlayer::Error update_err = video_player->update();
- if(update_err != VideoPlayer::Error::OK || !window.isOpen() || current_page == PageType::EXIT)
- break;
- std::this_thread::sleep_for(std::chrono::milliseconds(20));
- }
- video_player.reset();
- }
+ video_player->get_time_in_file(&resume_start_time);
+ video_player.reset();
page_changed = true;
}
});
@@ -2750,7 +2777,7 @@ namespace QuickMedia {
if(!video_player) {
current_page = PageType::VIDEO_CONTENT;
- load_video_error_check(true);
+ load_video_error_check(resume_start_time > 0.1 ? std::to_string((int)resume_start_time) : "");
} else {
XMapWindow(disp, video_player_window);
XSync(disp, False);
@@ -2770,6 +2797,16 @@ namespace QuickMedia {
cursor_visible = true;
}
+ // TODO: Remove the need for this. This is needed right now because mpv sometimes gets stuck when playing youtube videos after seeking too much
+ if(is_youtube && video_player && in_seeking && seeking_start_timer.getElapsedTime().asSeconds() >= seeking_restart_timeout_sec) {
+ in_seeking = false;
+ double resume_start_time = 0.0;
+ if(video_player->get_time_in_file(&resume_start_time) == VideoPlayer::Error::OK) {
+ // TODO: Set the second argument to false if the video url is no longer valid (or always?)
+ load_video_error_check(std::to_string((int)resume_start_time), true);
+ }
+ }
+
VideoPlayer::Error update_err = video_player ? video_player->update() : VideoPlayer::Error::OK;
if(update_err == VideoPlayer::Error::FAIL_TO_CONNECT_TIMEOUT) {
show_notification("QuickMedia", "Failed to connect to mpv ipc after 10 seconds", Urgency::CRITICAL);
@@ -2863,7 +2900,7 @@ namespace QuickMedia {
break;
}
- load_video_error_check(false);
+ load_video_error_check();
} else if(update_err != VideoPlayer::Error::OK) {
show_notification("QuickMedia", "Failed to play the video (error code " + std::to_string((int)update_err) + ")", Urgency::CRITICAL);
current_page = previous_page;
diff --git a/src/VideoPlayer.cpp b/src/VideoPlayer.cpp
index 0df4108..fe4643e 100644
--- a/src/VideoPlayer.cpp
+++ b/src/VideoPlayer.cpp
@@ -20,11 +20,10 @@ const int MAX_RETRIES_CONNECT = 1000;
const int READ_TIMEOUT_MS = 200;
namespace QuickMedia {
- VideoPlayer::VideoPlayer(bool no_video, bool use_system_mpv_config, bool resume_playback, bool keep_open, EventCallbackFunc _event_callback, VideoPlayerWindowCreateCallback _window_create_callback, const std::string &resource_root, int monitor_height) :
+ 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) :
exit_status(0),
no_video(no_video),
use_system_mpv_config(use_system_mpv_config),
- resume_playback(resume_playback),
keep_open(keep_open),
video_process_id(-1),
connected_to_ipc(false),
@@ -67,7 +66,7 @@ namespace QuickMedia {
XCloseDisplay(display);
}
- VideoPlayer::Error VideoPlayer::launch_video_process(const char *path, const char *audio_path, sf::WindowHandle _parent_window, const std::string &plugin_name, const std::string &title, const std::string &start_time) {
+ VideoPlayer::Error VideoPlayer::launch_video_process(const char *path, const char *audio_path, sf::WindowHandle _parent_window, bool is_youtube, const std::string &title, const std::string &start_time) {
parent_window = _parent_window;
if(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1) {
@@ -77,10 +76,6 @@ namespace QuickMedia {
return Error::FAIL_TO_CREATE_SOCKET;
}
- Path cookies_filepath;
- if(get_cookies_filepath(cookies_filepath, plugin_name) != 0)
- fprintf(stderr, "Failed to create %s cookies filepath\n", plugin_name.c_str());
-
const std::string parent_window_str = std::to_string(parent_window);
std::vector<const char*> args;
@@ -91,19 +86,12 @@ namespace QuickMedia {
std::string input_conf = "--input-conf=" + resource_root + "input.conf";
- Path mpv_watch_later_dir = get_storage_dir().join("mpv").join("watch_later");
- if(create_directory_recursive(mpv_watch_later_dir) != 0)
- fprintf(stderr, "Failed to create %s watch later directory\n", mpv_watch_later_dir.data.c_str());
- std::string watch_later_dir = "--watch-later-directory=" + mpv_watch_later_dir.data;
-
std::string ytdl_format;
if(no_video)
ytdl_format = "--ytdl-format=bestaudio/best";
else
ytdl_format = "--ytdl-format=bestvideo[height<=?" + std::to_string(monitor_height) + "]+bestaudio/best";
- std::string cookies_file_arg = "--cookies-file=" + cookies_filepath.data;
-
// TODO: Resume playback if the last video played matches the first video played next time QuickMedia is launched
args.insert(args.end(), {
"mpv",
@@ -112,13 +100,11 @@ namespace QuickMedia {
"--no-terminal",
"--save-position-on-quit=yes",
"--profile=pseudo-gui", // For gui when playing audio, requires a version of mpv that isn't ancient
- watch_later_dir.c_str(),
ytdl_format.c_str(),
+ "--no-resume-playback",
// TODO: Disable hr seek on low power devices?
"--hr-seek=yes",
- "--cache=no",
- "--cookies",
- cookies_file_arg.c_str(),
+ //"--cache=no",
input_conf.c_str(),
wid_arg.c_str()
});
@@ -131,10 +117,8 @@ namespace QuickMedia {
if(keep_open)
args.push_back("--keep-open=yes");
- if(resume_playback)
- args.push_back("--resume-playback");
- else
- args.push_back("--no-resume-playback");
+ if(is_youtube)
+ args.push_back("--no-ytdl");
if(!use_system_mpv_config) {
args.insert(args.end(), {
@@ -161,7 +145,7 @@ namespace QuickMedia {
}
std::string start_time_arg;
- if(!resume_playback && !start_time.empty()) {
+ if(!start_time.empty()) {
start_time_arg = "--start=" + start_time;
args.push_back(start_time_arg.c_str());
}
@@ -184,16 +168,16 @@ namespace QuickMedia {
return Error::OK;
}
- VideoPlayer::Error VideoPlayer::load_video(const char *path, const char *audio_path, sf::WindowHandle _parent_window, const std::string &plugin_name, const std::string &title, const std::string &start_time) {
+ VideoPlayer::Error VideoPlayer::load_video(const char *path, const char *audio_path, sf::WindowHandle _parent_window, bool is_youtube, const std::string &title, const std::string &start_time) {
// 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);
fprintf(stderr, "Playing video: %s, audio: %s\n", path ? path : "", audio_path ? audio_path : "");
if(video_process_id == -1)
- return launch_video_process(path, audio_path, _parent_window, plugin_name, title, start_time);
+ return launch_video_process(path, audio_path, _parent_window, is_youtube, title, start_time);
- // TODO: When these are used, add audio_path, title and start_time. Also handle plugin_name
+ // TODO: When these are used, add audio_path, title and start_time. Also handle is_youtube
Json::Value command_data(Json::arrayValue);
command_data.append("loadfile");
command_data.append(path);
@@ -399,19 +383,6 @@ namespace QuickMedia {
return err;
}
- VideoPlayer::Error VideoPlayer::quit_and_save_watch_later() {
- Json::Value command_data(Json::arrayValue);
- command_data.append("quit-watch-later");
- Json::Value command(Json::objectValue);
- command["command"] = command_data;
-
- Json::StreamWriterBuilder builder;
- builder["commentStyle"] = "None";
- builder["indentation"] = "";
- const std::string cmd_str = Json::writeString(builder, command) + "\n";
- return send_command(cmd_str.c_str(), cmd_str.size());
- }
-
VideoPlayer::Error VideoPlayer::send_command(const char *cmd, size_t size) {
if(!connected_to_ipc)
return Error::FAIL_NOT_CONNECTED;