From c4ab196719e99e6ab9e323175124cf1d9aafa36e Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 9 Aug 2019 22:00:08 +0200 Subject: Next video in list to play should be next in playlist (if available) --- plugins/Youtube.hpp | 3 ++ src/QuickMedia.cpp | 1 - src/VideoPlayer.cpp | 4 +-- src/plugins/Youtube.cpp | 79 +++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 78 insertions(+), 9 deletions(-) diff --git a/plugins/Youtube.hpp b/plugins/Youtube.hpp index 6fc6039..16f5870 100644 --- a/plugins/Youtube.hpp +++ b/plugins/Youtube.hpp @@ -12,5 +12,8 @@ namespace QuickMedia { int get_search_delay() const override { return 500; } bool search_suggestion_is_search() const override { return true; } Page get_page_after_search() const override { return Page::VIDEO_CONTENT; } + private: + std::string last_related_media_playlist_id; + BodyItems last_playlist_data; }; } \ No newline at end of file diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index a7dc7ff..a57c522 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -397,7 +397,6 @@ namespace QuickMedia { std::unique_ptr video_player; auto play_video = [this, &video_player, &play_next_video, &on_window_create, &video_player_ui_window, &ui_resize]() { - printf("Playing video: %s\n", content_url.c_str()); watched_videos.insert(content_url); video_player = std::make_unique([this, &play_next_video, &video_player_ui_window, &ui_resize](const char *event_name) { if(strcmp(event_name, "end-file") == 0) { diff --git a/src/VideoPlayer.cpp b/src/VideoPlayer.cpp index 65bbb67..96b9ee2 100644 --- a/src/VideoPlayer.cpp +++ b/src/VideoPlayer.cpp @@ -165,8 +165,6 @@ namespace QuickMedia { VideoPlayer::Error VideoPlayer::read_ipc_func() { assert(connected_to_ipc); - assert(event_callback); - Json::Value json_root; Json::CharReaderBuilder json_builder; std::unique_ptr json_reader(json_builder.newCharReader()); @@ -189,7 +187,7 @@ namespace QuickMedia { if(json_reader->parse(buffer + start, buffer + i, &json_root, &json_errors)) { const Json::Value &event = json_root["event"]; const Json::Value &request_id_json = json_root["request_id"]; - if(event.isString()) + if(event.isString() && event_callback) event_callback(event.asCString()); else if(expected_request_id != 0 && request_id_json.isNumeric() && request_id_json.asUInt() == expected_request_id) { request_response_data = json_root["data"]; diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp index 56e8574..0f0ca05 100644 --- a/src/plugins/Youtube.cpp +++ b/src/plugins/Youtube.cpp @@ -127,11 +127,44 @@ namespace QuickMedia { return result == 0 ? SuggestionResult::OK : SuggestionResult::ERR; } + static std::string get_playlist_id_from_url(const std::string &url) { + std::string playlist_id = url; + size_t list_index = playlist_id.find("&list="); + if(list_index == std::string::npos) + return playlist_id; + return playlist_id.substr(list_index); + } + + static std::string remove_index_from_playlist_url(const std::string &url) { + std::string result = url; + size_t index = result.rfind("&index="); + if(index == std::string::npos) + return result; + return result.substr(0, index); + } + + // TODO: Make this faster by using string search instead of parsing html. + // TODO: If the result is a play BodyItems Youtube::get_related_media(const std::string &url) { BodyItems result_items; + struct ItemData { + BodyItems &result_items; + size_t index = 0; + }; + ItemData item_data { result_items, 0 }; + + std::string modified_url = remove_index_from_playlist_url(url); + std::string playlist_id = get_playlist_id_from_url(modified_url); + if(playlist_id == last_related_media_playlist_id) { + result_items.reserve(last_playlist_data.size()); + for(auto &data : last_playlist_data) { + result_items.push_back(std::make_unique(*data)); + } + return result_items; + } std::string website_data; - if(download_to_string(url, website_data) != DownloadResult::OK) + if(download_to_string(modified_url, website_data) != DownloadResult::OK) return result_items; QuickMediaHtmlSearch html_search; @@ -139,19 +172,55 @@ namespace QuickMedia { if(result != 0) goto cleanup; + if(!playlist_id.empty()) { + result = quickmedia_html_find_nodes_xpath(&html_search, "//a", + [](QuickMediaHtmlNode *node, void *userdata) { + auto *item_data = (ItemData*)userdata; + const char *node_class = quickmedia_html_node_get_attribute_value(node, "class"); + const char *href = quickmedia_html_node_get_attribute_value(node, "href"); + if(node_class && href && contains(node_class, "playlist-video")) { + auto item = std::make_unique(""); + item->url = std::string("https://www.youtube.com") + remove_index_from_playlist_url(href); + item_data->result_items.push_back(std::move(item)); + } + }, &item_data); + + result = quickmedia_html_find_nodes_xpath(&html_search, "//li", + [](QuickMediaHtmlNode *node, void *userdata) { + auto *item_data = (ItemData*)userdata; + if(item_data->index >= item_data->result_items.size()) + return; + + // TODO: Also add title for related media. This data is in @data-title + const char *data_thumbnail_url = quickmedia_html_node_get_attribute_value(node, "data-thumbnail-url"); + if(data_thumbnail_url && contains(data_thumbnail_url, "ytimg.com")) { + item_data->result_items[item_data->index]->thumbnail_url = data_thumbnail_url; + ++item_data->index; + } + }, &item_data); + } + + // We want non-playlist videos every when there is a playlist, since we want to play non-playlist videos after + // playing all playlist videos result = quickmedia_html_find_nodes_xpath(&html_search, "//ul[class=\"video-list\"]//div[class=\"content-wrapper\"]/a", [](QuickMediaHtmlNode *node, void *userdata) { - auto *result_items = (BodyItems*)userdata; + auto *item_data = (ItemData*)userdata; const char *href = quickmedia_html_node_get_attribute_value(node, "href"); - // TODO: Also add title for related media + // TODO: Also add title for related media and thumbnail if(href && begins_with(href, "/watch?v=")) { auto item = std::make_unique(""); item->url = std::string("https://www.youtube.com") + href; - result_items->push_back(std::move(item)); + item_data->result_items.push_back(std::move(item)); } - }, &result_items); + }, &item_data); cleanup: + last_playlist_data.clear(); + last_playlist_data.reserve(result_items.size()); + for(auto &data : result_items) { + last_playlist_data.push_back(std::make_unique(*data)); + } + last_related_media_playlist_id = playlist_id; quickmedia_html_search_deinit(&html_search); return result_items; } -- cgit v1.2.3