diff options
-rw-r--r-- | plugins/Soundcloud.hpp | 7 | ||||
-rw-r--r-- | src/Program.cpp | 1 | ||||
-rw-r--r-- | src/QuickMedia.cpp | 3 | ||||
-rw-r--r-- | src/plugins/Soundcloud.cpp | 104 |
4 files changed, 103 insertions, 12 deletions
diff --git a/plugins/Soundcloud.hpp b/plugins/Soundcloud.hpp index 0f397a1..873de5a 100644 --- a/plugins/Soundcloud.hpp +++ b/plugins/Soundcloud.hpp @@ -4,6 +4,8 @@ #include "../include/AsyncTask.hpp" namespace QuickMedia { + class SoundcloudPlaylist; + class SoundcloudPage : public Page { public: SoundcloudPage(Program *program) : Page(program) {} @@ -44,10 +46,13 @@ namespace QuickMedia { class SoundcloudPlaylistPage : public SoundcloudPage { public: - SoundcloudPlaylistPage(Program *program, const std::string &playlist_name) : SoundcloudPage(program), playlist_name(playlist_name) {} + SoundcloudPlaylistPage(Program *program, SoundcloudPlaylist *playlist, const std::string &playlist_name) : SoundcloudPage(program), playlist(playlist), playlist_name(playlist_name), track_offset(0) {} const char* get_title() const override { return playlist_name.c_str(); } + PluginResult get_page(const std::string &str, int page, BodyItems &result_items) override; private: + SoundcloudPlaylist *playlist; std::string playlist_name; + size_t track_offset; }; class SoundcloudAudioPage : public VideoPage { diff --git a/src/Program.cpp b/src/Program.cpp index feb543f..a8189ae 100644 --- a/src/Program.cpp +++ b/src/Program.cpp @@ -34,6 +34,7 @@ public: } ~CurrentThreadProgram() { + std::lock_guard<std::mutex> lock(thread_current_program_mutex); thread_current_program.erase(std::this_thread::get_id()); } diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index f8339a0..c81e0c5 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -1923,6 +1923,9 @@ namespace QuickMedia { page_loop_render(window, tabs, selected_tab, tab_associated_data[selected_tab], json_chapters, ui_tabs); window.display(); + if(!tabs[selected_tab].body->items.empty() && tabs[selected_tab].body->is_last_item_fully_visible()) + on_bottom_reached(); + if(go_to_previous_page) { go_to_previous_page = false; goto page_end; diff --git a/src/plugins/Soundcloud.cpp b/src/plugins/Soundcloud.cpp index e0a4d24..9a5fe64 100644 --- a/src/plugins/Soundcloud.cpp +++ b/src/plugins/Soundcloud.cpp @@ -10,6 +10,7 @@ namespace QuickMedia { class SoundcloudPlaylist : public BodyItemExtra { public: BodyItems tracks; + std::vector<int64_t> tracks_to_load; }; // Return empty string if transcoding files are not found @@ -23,8 +24,8 @@ namespace QuickMedia { return ""; } - static std::string duration_to_descriptive_string(int64_t seconds) { - seconds /= 1000; + static std::string duration_to_descriptive_string(int64_t milliseconds) { + time_t seconds = milliseconds / 1000; time_t minutes = seconds / 60; time_t hours = minutes / 60; @@ -66,14 +67,40 @@ namespace QuickMedia { static std::shared_ptr<BodyItem> parse_collection_item(const Json::Value &item_json) { std::string title; + int num_playlists = 0; + const Json::Value &playlist_count_json = item_json["playlist_count"]; + if(playlist_count_json.isInt()) + num_playlists = playlist_count_json.asInt(); + + int num_tracks = 0; + const Json::Value &track_count_json = item_json["track_count"]; + if(track_count_json.isInt()) + num_tracks = track_count_json.asInt(); + + const Json::Value &user_json = item_json["user"]; + if(user_json.isObject()) { + const Json::Value &username_json = user_json["username"]; + if(username_json.isString()) + title = username_json.asString(); + + const Json::Value &track_count_json = user_json["track_count"]; + if(track_count_json.isInt()) + num_tracks = track_count_json.asInt(); + } + const Json::Value &title_json = item_json["title"]; const Json::Value &username_json = item_json["username"]; - if(title_json.isString()) - title = title_json.asString(); - else if(username_json.isString()) - title = username_json.asString(); - else + if(title_json.isString()) { + if(!title.empty()) + title += " - "; + title += title_json.asString(); + } else if(username_json.isString()) { + if(!title.empty()) + title += " - "; + title += username_json.asString(); + } else { return nullptr; + } auto body_item = BodyItem::create(std::move(title)); std::string description; @@ -82,6 +109,7 @@ namespace QuickMedia { if(media_json.isObject()) body_item->url = get_best_transcoding_audio_url(media_json); + bool is_playlist = false; if(body_item->url.empty()) { const Json::Value &tracks_json = item_json["tracks"]; if(tracks_json.isArray()) { @@ -91,13 +119,20 @@ namespace QuickMedia { continue; auto track = parse_collection_item(track_json); - if(track) + if(track) { playlist->tracks.push_back(std::move(track)); + } else { + const Json::Value &track_id_json = track_json["id"]; + if(track_id_json.isInt64()) + playlist->tracks_to_load.push_back(track_id_json.asInt64()); + } } - description = "Playlist with " + std::to_string(playlist->tracks.size()) + " track" + (playlist->tracks.size() == 1 ? "" : "s"); + num_tracks = tracks_json.size(); + description = "Playlist with " + std::to_string(num_tracks) + " track" + (num_tracks == 1 ? "" : "s"); body_item->extra = std::move(playlist); body_item->url = "track"; + is_playlist = true; } } @@ -134,6 +169,20 @@ namespace QuickMedia { body_item->thumbnail_mask_type = ThumbnailMaskType::CIRCLE; } + if(username_json.isString()) { + if(!description.empty()) + description += '\n'; + description += "Artist"; + if(num_playlists > 0) + description += " • " + std::to_string(num_playlists) + " playlist" + (num_playlists == 1 ? "" : "s"); + if(num_tracks > 0) + description += " • " + std::to_string(num_tracks) + " track" + (num_tracks == 1 ? "" : "s"); + } else if(!is_playlist) { + if(!description.empty()) + description += '\n'; + description += "Track"; + } + std::string duration_str = collection_item_get_duration(item_json); if(!duration_str.empty()) { if(!description.empty()) @@ -183,9 +232,10 @@ namespace QuickMedia { return PluginResult::ERR; if(url == "track") { + SoundcloudPlaylist *playlist = static_cast<SoundcloudPlaylist*>(submit_body_item->extra.get()); auto body = create_body(); - body->items = static_cast<SoundcloudPlaylist*>(submit_body_item->extra.get())->tracks; - result_tabs.push_back(Tab{std::move(body), std::make_unique<SoundcloudPlaylistPage>(program, title), nullptr}); + body->items = playlist->tracks; + result_tabs.push_back(Tab{std::move(body), std::make_unique<SoundcloudPlaylistPage>(program, playlist, title), nullptr}); } else if(url.find("/stream/users/") != std::string::npos) { std::string query_url = url + "?client_id=" + client_id + "&limit=20&offset=0&linked_partitioning=1&app_version=1616689516&app_locale=en"; @@ -352,6 +402,38 @@ namespace QuickMedia { return parse_user_page(json_root, result_items, next_href); } + PluginResult SoundcloudPlaylistPage::get_page(const std::string&, int, BodyItems &result_items) { + std::string ids_param; + const size_t tracks_load_end = std::min(track_offset + 10, playlist->tracks_to_load.size()); + for(size_t i = track_offset; i < tracks_load_end; ++i) { + if(!ids_param.empty()) + ids_param += "%2C"; + ids_param += std::to_string(playlist->tracks_to_load[i]); + } + track_offset = tracks_load_end; + + if(ids_param.empty()) + return PluginResult::OK; + + Json::Value json_root; + DownloadResult result = download_json(json_root, "https://api-v2.soundcloud.com/tracks?ids=" + ids_param + "&client_id=" + client_id + "&app_version=1616689516&app_locale=en", {}, true); + if(result != DownloadResult::OK) return download_result_to_plugin_result(result); + + if(!json_root.isArray()) + return PluginResult::ERR; + + for(const Json::Value &item_json : json_root) { + if(!item_json.isObject()) + continue; + + auto track_item = parse_collection_item(item_json); + if(track_item) + result_items.push_back(std::move(track_item)); + } + + return PluginResult::OK; + } + std::string SoundcloudAudioPage::url_get_playable_url(const std::string &url) { std::string query_url = url + "?client_id=" + client_id; |