Skip to content

Commit

Permalink
Use callback multiple times instead
Browse files Browse the repository at this point in the history
  • Loading branch information
kraxarn committed Jul 6, 2023
1 parent 4549be9 commit cc1b1f6
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 36 deletions.
8 changes: 6 additions & 2 deletions lib/include/lib/spotify/api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
#include "lib/spotify/callback.hpp"
#include "lib/spotify/request.hpp"
#include "lib/spotify/queue.hpp"
#include "lib/spotify/pagination.hpp"
#include "lib/spotify/page.hpp"
#include "lib/httpclient.hpp"
#include "lib/datetime.hpp"

Expand Down Expand Up @@ -278,7 +278,11 @@ namespace lib
void playlist_tracks(const lib::spt::playlist &playlist,
lib::callback<std::vector<lib::spt::track>> &callback);

auto playlist_tracks(const lib::spt::playlist &playlist) -> lib::spt::pagination<lib::spt::track> *;
/**
* @note Experimental
*/
void playlist_tracks(const lib::spt::playlist &playlist,
const std::function<bool(const lib::result<lib::spt::page<lib::spt::track>> &)> &callback);

void add_to_playlist(const std::string &playlist_id,
const std::vector<std::string> &track_uris,
Expand Down
68 changes: 68 additions & 0 deletions lib/include/lib/spotify/page.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#pragma once

#include "thirdparty/json.hpp"

namespace lib
{
namespace spt
{
/**
* Page of items
* @tparam T Item type
*/
template<typename T>
class page
{
public:
page() = default;

/**
* Next page exists
*/
auto has_next() const -> bool
{
return !next.empty();
};

/**
* Items on this page
*/
std::vector<T> items;

/**
* Maximum number of items per page
*/
int limit = 0;

/**
* Number of offset items
*/
int offset = 0;

/**
* Total number of items
*/
int total = 0;

/**
* URL for next page
*/
std::string next;
};

template<typename T>
void from_json(const nlohmann::json &json, lib::spt::page<T> &page)
{
json.at("items").get_to(page.items);
json.at("limit").get_to(page.limit);
json.at("offset").get_to(page.offset);
json.at("total").get_to(page.total);

const auto &next = json.at("next");
if (next.is_string())
{
next.get_to(page.next);
}
}
}
}
57 changes: 57 additions & 0 deletions lib/include/lib/spotify/request.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "lib/spotify/error.hpp"
#include "lib/spotify/util.hpp"
#include "lib/spotify/deviceselect.hpp"
#include "lib/spotify/page.hpp"

namespace lib
{
Expand Down Expand Up @@ -47,6 +48,62 @@ namespace lib
});
}

/**
* Get a paged list of items contained in specified key
* @tparam T Type of item to fetch
* @param url Initial URL to fetch from
* @param key Key to fetch items from
* @param callback Callback with page index, returning true to fetch the next page
*/
template<typename T>
void get_page(const std::string &url, const std::string &key,
const std::function<bool(const lib::result<lib::spt::page<T>> &)> &callback)
{
const auto api_url = lib::strings::starts_with(url, "https://")
? lib::spt::to_relative_url(url)
: url;

lib::log::debug("GET: {}", api_url);

get<nlohmann::json>(api_url, [this, key, callback](const lib::result<nlohmann::json> &result)
{
if (!result.success())
{
const auto message = parse_error_message(result.message());
callback(lib::result<lib::spt::page<T>>::fail(message));
return;
}

const auto &json = result.value();
if (!key.empty() && !json.contains(key))
{
const auto message = lib::fmt::format("No such key: {}", key);
callback(lib::result<lib::spt::page<T>>::fail(message));
return;
}

lib::spt::page<T> page;
try
{
page = key.empty() ? json : json.at(key);
}
catch (const std::exception &exception)
{
const std::string message = exception.what();
callback(lib::result<lib::spt::page<T>>::fail(message));
return;
}

if (!callback(lib::result<lib::spt::page<T>>::ok(page))
|| !page.has_next())
{
return;
}

get_page(page.next, key, callback);
});
}

/**
* POST request without body
*/
Expand Down
6 changes: 3 additions & 3 deletions lib/src/spotifyapi/playlists.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ void lib::spt::api::playlist_tracks(const lib::spt::playlist &playlist,
get_items(url, callback);
}

auto lib::spt::api::playlist_tracks(const lib::spt::playlist &playlist) -> lib::spt::pagination<lib::spt::track> *
void lib::spt::api::playlist_tracks(const lib::spt::playlist &playlist,
const std::function<bool(const lib::result<lib::spt::page<lib::spt::track>> &)> &callback)
{
// TODO: pagination is just a temporary object, and thus need heap allocation, find better solution
const auto url = lib::fmt::format("playlists/{}/tracks?market=from_token&limit=50", playlist.id);
return new lib::spt::pagination<lib::spt::track>(url, request);
request.get_page<lib::spt::track>(url, std::string(), callback);
}

void lib::spt::api::add_to_playlist(const std::string &playlist_id,
Expand Down
60 changes: 32 additions & 28 deletions src/list/tracks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -493,11 +493,15 @@ auto List::Tracks::getSelectedTrackIds() const -> std::vector<std::string>
}

void List::Tracks::load(const std::vector<lib::spt::track> &tracks,
const std::string &selectedId, const std::string &addedAt)
const std::string &selectedId, const std::string &addedAt, bool clearItems)
{
clear();
trackItems.clear();
playingTrackItem = nullptr;
if (clearItems)
{
clear();
trackItems.clear();
playingTrackItem = nullptr;
}

auto fieldWidth = static_cast<int>(std::to_string(tracks.size()).size());
auto current = getCurrent();
auto anyHasDate = false;
Expand Down Expand Up @@ -570,14 +574,15 @@ void List::Tracks::load(const std::vector<lib::spt::track> &tracks,
});
}

void List::Tracks::load(const std::vector<lib::spt::track> &tracks)
void List::Tracks::load(const std::vector<lib::spt::track> &tracks, bool clearItems)
{
load(tracks, std::string());
load(tracks, std::string(), clearItems);
}

void List::Tracks::load(const std::vector<lib::spt::track> &tracks, const std::string &selectedId)
void List::Tracks::load(const std::vector<lib::spt::track> &tracks,
const std::string &selectedId, bool clearItems)
{
load(tracks, selectedId, std::string());
load(tracks, selectedId, std::string(), clearItems);
}

void List::Tracks::load(const lib::spt::playlist &playlist)
Expand Down Expand Up @@ -640,27 +645,26 @@ void List::Tracks::refreshPlaylist(const lib::spt::playlist &playlist)

if (lib::developer_mode::is_experiment_enabled(lib::experiment::new_paging))
{
auto *pagination = spotify.playlist_tracks(playlist);
pagination->success([this, mainWindow, playlistUri, &pagination](const std::vector<lib::spt::track> &tracks)
{
if (playlistUri != mainWindow->history()->currentUri())
spotify.playlist_tracks(playlist,
[this, mainWindow, playlistUri](const lib::result<lib::spt::page<lib::spt::track>> &result) -> bool
{
delete pagination;
return;
}

load(tracks); // TODO: Clears
setEnabled(true);
delete pagination;
});
pagination->error([&pagination](const std::string &message)
{
StatusMessage::error(QString("Failed to fetch page: %1")
.arg(QString::fromStdString(message)));

delete pagination;
});
pagination->next();
if (playlistUri != mainWindow->history()->currentUri())
{
return false;
}

if (!result.success())
{
StatusMessage::error(QString("Failed to load playlist: %1")
.arg(QString::fromStdString(result.message())));
return false;
}

const auto &page = result.value();
load(page.items, page.offset == 0);
setEnabled(true);
return page.has_next();
});
return;
}

Expand Down
7 changes: 4 additions & 3 deletions src/list/tracks.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,20 @@ namespace List
/**
* Load tracks directly, without cache, but select an item
*/
void load(const std::vector<lib::spt::track> &tracks, const std::string &selectedId);
void load(const std::vector<lib::spt::track> &tracks, const std::string &selectedId,
bool clearItems = true);

/**
* Load tracks directly, without cache,
* but select an item and provide a fallback added date
*/
void load(const std::vector<lib::spt::track> &tracks, const std::string &selectedId,
const std::string &addedAt);
const std::string &addedAt, bool clearItems = true);

/**
* Load tracks directly, without cache
*/
void load(const std::vector<lib::spt::track> &tracks);
void load(const std::vector<lib::spt::track> &tracks, bool clearItems = true);

/**
* Load playlist first from cache, then refresh it
Expand Down

0 comments on commit cc1b1f6

Please sign in to comment.