From 47d594f0676a644e7c072331a009ceb46de8f62e Mon Sep 17 00:00:00 2001 From: dec05eba Date: Mon, 19 Jun 2023 02:18:47 +0200 Subject: Lbry: fix videos not working --- TODO | 9 ++++++- plugins/Lbry.hpp | 3 ++- plugins/LocalAnime.hpp | 3 ++- src/plugins/Lbry.cpp | 62 ++++++++++++++++++++++++++++++++++++++++------ src/plugins/LocalAnime.cpp | 8 ++++-- 5 files changed, 73 insertions(+), 12 deletions(-) diff --git a/TODO b/TODO index b52b212..397461c 100644 --- a/TODO +++ b/TODO @@ -269,4 +269,11 @@ Add ability to create room, commands for changing nickname, roomnickname, avatar Replace pepper emoji with gimp pepper. Consider adding typing notification to matrix. Make it possible to switch between list and grid view. -Automatically manage cache - remove old cache files. \ No newline at end of file +Automatically manage cache - remove old cache files. +Run ~/.config/quickmedia/script.sh or something like that for every body item and/or specific actions. This can be used to filter out items, + edit items, text to speech, filter out imageboard threads, youtube videos, etc. + Make sure that filtering out matrix messages doesn't break replies and also doesn't notify user when the filtering out message mentions you. + This is similar to muting/ignore in matrix so maybe implement that at the same time? or was ignore server side, I forgot. + To make this work properly the script needs to be sent more information than the body item text only. It also needs plugin name, + current tab (url, name), body item url, etc. +Ctrl+H to show/hide dot files in file manager. \ No newline at end of file diff --git a/plugins/Lbry.hpp b/plugins/Lbry.hpp index 0eeaf86..4228544 100644 --- a/plugins/Lbry.hpp +++ b/plugins/Lbry.hpp @@ -1,6 +1,7 @@ #pragma once #include "Page.hpp" +#include "../include/M3U8.hpp" namespace QuickMedia { class LbrySearchPage : public Page { @@ -44,6 +45,6 @@ namespace QuickMedia { PluginResult load(const SubmitArgs &args, VideoInfo &video_info, std::string &err_str) override; private: std::string title; - std::string streaming_url; + std::vector streams; }; } \ No newline at end of file diff --git a/plugins/LocalAnime.hpp b/plugins/LocalAnime.hpp index 6edf1c8..d0194ba 100644 --- a/plugins/LocalAnime.hpp +++ b/plugins/LocalAnime.hpp @@ -36,7 +36,7 @@ namespace QuickMedia { : LazyFetchPage(program), parent_search_page(nullptr), fetch_home_page(true) {} LocalAnimeSearchPage(Program *program, std::vector anime_items, LocalAnimeSearchPage *parent_search_page = nullptr) : LazyFetchPage(program), parent_search_page(parent_search_page), anime_items(std::move(anime_items)) {} - const char* get_title() const override { return "Search"; } + const char* get_title() const override { return title.empty() ? "Search" : title.c_str(); } bool search_is_filter() override { return true; } PluginResult submit(const SubmitArgs &args, std::vector &result_tabs) override; PluginResult lazy_fetch(BodyItems &result_items) override; @@ -44,6 +44,7 @@ namespace QuickMedia { bool reseek_to_body_item_by_url() override { return true; } LocalAnimeSearchPage *parent_search_page; + std::string title; private: std::vector anime_items; bool fetch_home_page = false; diff --git a/src/plugins/Lbry.cpp b/src/plugins/Lbry.cpp index a5806ab..1ce23c0 100644 --- a/src/plugins/Lbry.cpp +++ b/src/plugins/Lbry.cpp @@ -70,9 +70,6 @@ namespace QuickMedia { body_item->userdata = search_type_channel; is_channel = true; } else if(strcmp(value_type_json.asCString(), "stream") == 0) { - body_item->url = canonical_url_json.asString(); - body_item->userdata = search_type_video; - // Skip livestreams for now as they are pretty broken on lbry. // Livestream requests work by doing GET https://api.live.odysee.com/v1/odysee/live/ // then get stream url with .data.url. If that is missing then there is no livestream going on. What to do then? @@ -80,6 +77,25 @@ namespace QuickMedia { const Json::Value &stream_type_json = value_json["stream_type"]; if(!stream_type_json.isString() || strcmp(stream_type_json.asCString(), "video") != 0) return nullptr; + + if(!name_json.isString()) + return nullptr; + + const Json::Value &video_source_json = value_json["source"]; + if(!video_source_json.isObject()) + return nullptr; + + const Json::Value &media_type_json = video_source_json["media_type"]; + if(!media_type_json.isString() || !strstr(media_type_json.asCString(), "video/")) + return nullptr; + + const Json::Value &hash_json = video_source_json["hash"]; + const Json::Value &sd_hash_json = video_source_json["sd_hash"]; + if(!hash_json.isString() || !sd_hash_json.isString()) + return nullptr; + + body_item->url = "https://player.odycdn.com/api/v4/streams/tc/" + name_json.asString() + "/" + hash_json.asString() + "/" + sd_hash_json.asString() + "/master.m3u8"; + body_item->userdata = search_type_video; } body_item->thumbnail_size = { 220, 130 }; @@ -347,7 +363,8 @@ namespace QuickMedia { return get_page("", 0, result_items); } - static PluginResult video_get_stream_url(Page *page, const std::string &video_url, std::string &streaming_url, std::string &err_str) { + static PluginResult video_get_stream_url(Page *page, const std::string &video_url, std::vector &streams, std::string &err_str) { +#if 0 std::string url = "https://api.na-backend.odysee.com/api/v1/proxy?m=get"; Json::Value request_params_json(Json::objectValue); @@ -391,6 +408,14 @@ namespace QuickMedia { streaming_url = streaming_url_json.asString(); return PluginResult::OK; +#else + std::string website_data; + DownloadResult result = download_to_string(video_url.c_str(), website_data, {}, true); + if(result != DownloadResult::OK) return download_result_to_plugin_result(result); + + streams = m3u8_get_streams(website_data); + return PluginResult::OK; +#endif } // TODO: Support |max_height|. This can be done by gettin video source hash and checking for sd_hash and then resolution. @@ -404,7 +429,30 @@ namespace QuickMedia { std::string LbryVideoPage::get_video_url(int max_height, bool &has_embedded_audio, std::string &ext) { has_embedded_audio = true; ext = ".mp4"; // TODO: Check if this is always correct - return streaming_url; + + if(streams.empty()) + return url; + + std::sort(streams.begin(), streams.end(), [](const M3U8Stream &a, const M3U8Stream &b) { + return a.height > b.height; + }); + + size_t selected_stream = 0; + for(size_t i = 0; i < streams.size(); ++i) { + if(streams[i].height <= max_height) { + selected_stream = i; + break; + } + } + + std::string video_url = url; + size_t end_section = video_url.rfind('/'); + if(end_section != std::string::npos && end_section > 0 && end_section != video_url.size() - 1) { + end_section += 1; + video_url.replace(end_section, video_url.size() - end_section, streams[selected_stream].url); + } + + return video_url; } std::string LbryVideoPage::get_audio_url(std::string&) { @@ -412,10 +460,10 @@ namespace QuickMedia { } PluginResult LbryVideoPage::load(const SubmitArgs &args, VideoInfo &video_info, std::string &err_str) { - streaming_url.clear(); + streams.clear(); video_info.title = args.title; //title = this->title; video_info.duration = 0.0; - return video_get_stream_url(this, url, streaming_url, err_str); + return video_get_stream_url(this, url, streams, err_str); } } \ No newline at end of file diff --git a/src/plugins/LocalAnime.cpp b/src/plugins/LocalAnime.cpp index 3682ea5..a8cebab 100644 --- a/src/plugins/LocalAnime.cpp +++ b/src/plugins/LocalAnime.cpp @@ -330,11 +330,15 @@ namespace QuickMedia { LocalAnimeBodyItemData *item_data = static_cast(args.extra.get()); if(std::holds_alternative(*item_data->anime_item)) { const LocalAnime &anime = std::get(*item_data->anime_item); - result_tabs.push_back(Tab{ create_body(false, true), std::make_unique(program, anime.items, this), create_search_bar("Search...", SEARCH_DELAY_FILTER) }); + auto page = std::make_unique(program, anime.items, this); + page->title = anime.name; + result_tabs.push_back(Tab{ create_body(false, true), std::move(page), create_search_bar("Search...", SEARCH_DELAY_FILTER) }); return PluginResult::OK; } else if(std::holds_alternative(*item_data->anime_item)) { const LocalAnimeSeason &season = std::get(*item_data->anime_item); - result_tabs.push_back(Tab{ create_body(false, true), std::make_unique(program, season.episodes, this), create_search_bar("Search...", SEARCH_DELAY_FILTER) }); + auto page = std::make_unique(program, season.episodes, this); + page->title = title + " - " + season.name; + result_tabs.push_back(Tab{ create_body(false, true), std::move(page), create_search_bar("Search...", SEARCH_DELAY_FILTER) }); return PluginResult::OK; } else if(std::holds_alternative(*item_data->anime_item)) { const LocalAnimeEpisode &episode = std::get(*item_data->anime_item); -- cgit v1.2.3