aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugins/Soundcloud.hpp7
-rw-r--r--src/Program.cpp1
-rw-r--r--src/QuickMedia.cpp3
-rw-r--r--src/plugins/Soundcloud.cpp104
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;