Added functionality to retrieve the song cover's art from Icarus

This commit is contained in:
kdeng00
2019-11-08 22:36:16 -05:00
parent 76a6a763c6
commit 9bab2a7798
20 changed files with 431 additions and 72 deletions
+3
View File
@@ -21,6 +21,7 @@ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -O3 -std=c++17 -DNDEBUG")
set (SOURCES
APIRepository.cpp
BaseRepository.cpp
CoverArtRepository.cpp
Demo.cpp
RepeatRepository.cpp
ShuffleRepository.cpp
@@ -33,6 +34,8 @@ set (SOURCES
set (HEADERS
APIRepository.h
BaseRepository.h
model/CoverArt.h
CoverArtRepository.h
Demo.h
model/APIInfo.h
model/Song.h
+86
View File
@@ -0,0 +1,86 @@
//
// Created by brahmix on 11/8/19.
//
#include "CoverArtRepository.h"
#include <curl/curl.h>
namespace repository {
/**
template<typename C>
std::vector<C> CoverArtRepository<C>::retrieveCoverArtRecords(const model::Token& token,
const std::string& uri) {
std::vector<std::string> vals;
return vals;
}
template<typename C>
std::vector<data> CoverArtRepository<C>::retrieveCoverArtData(const model::Token& token,
const C& cover,
const std::string& uri) {
std::string fullUri(uri);
if (fullUri.at(fullUri.size() - 1) != '/') {
fullUri.append("/");
}
fullUri.append(downloadEndpoint());
fullUri.append(std::to_string(cover.id));
CURL *curl;
CURLcode res;
struct curl_slist *chunk = nullptr;
curl = curl_easy_init();
std::vector<data> vals;
if (!curl) {
return vals;
}
std::string data;
std::string authHeader("Authorization: Bearer ");
authHeader.append(token.accessToken);
constexpr auto keepAliveHeader = "Content-type: Keep-alive";
chunk = curl_slist_append(chunk, authHeader.c_str());
chunk = curl_slist_append(chunk, keepAliveHeader);
curl_easy_setopt(curl, CURLOPT_URL, fullUri.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, respBodyRetriever);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
std::copy(data.begin(), data.end(), vals);
return vals;
}
template<typename C>
C CoverArtRepository<C>::retrieveCoverArtRecord(const model::Token& token,
const C& cover,
const std::string& uri) {
model::CoverArt cov;
return cov;
}
template<typename C>
constexpr auto CoverArtRepository<C>::downloadEndpoint() noexcept {
return "api/v1/coverart/download";
}
template<typename C>
constexpr auto CoverArtRepository<C>::recordsEndpoint() noexcept {
return "api/v1/coverart/";
}
template<typename C>
size_t CoverArtRepository<C>::respBodyRetriever(void *ptr, size_t size, size_t nmemb, char *e) {
((std::string*)e)->append((char*)ptr, size * nmemb);
return size * nmemb;
}
*/
}
+122
View File
@@ -0,0 +1,122 @@
//
// Created by brahmix on 11/8/19.
//
#ifndef MEAR_COVERARTREPOSITORY_H
#define MEAR_COVERARTREPOSITORY_H
#include <string>
#include <sstream>
#include <iterator>
#include <vector>
#include <curl/curl.h>
#include "model/CoverArt.h"
#include "model/Token.h"
using data = char;
namespace repository {
template<typename C>
class CoverArtRepository {
public:
std::vector<C> retrieveCoverArtRecords(const model::Token&, const std::string&);
std::vector<data> retrieveCoverArtData(const model::Token&, const C&, const std::string&);
C retrieveCoverArtRecord(const model::Token&, const C&, const std::string&);
private:
constexpr auto downloadEndpoint() noexcept;
constexpr auto recordsEndpoint() noexcept;
static size_t respBodyRetriever(void*, size_t, size_t, char*);
};
template<typename C>
std::vector<C> CoverArtRepository<C>::retrieveCoverArtRecords(const model::Token& token,
const std::string& uri) {
std::vector<std::string> vals;
return vals;
}
template<typename C>
std::vector<data> CoverArtRepository<C>::retrieveCoverArtData(const model::Token& token,
const C& cover,
const std::string& uri) {
std::string fullUri(uri);
if (fullUri.at(fullUri.size() - 1) != '/') {
fullUri.append("/");
}
fullUri.append(downloadEndpoint());
fullUri.append(std::to_string(cover.id));
CURL *curl;
CURLcode res;
struct curl_slist *chunk = nullptr;
curl = curl_easy_init();
if (!curl) {
return std::vector<data>();
}
std::string data;
std::string authHeader("Authorization: Bearer ");
authHeader.append(token.accessToken);
constexpr auto keepAliveHeader = "Content-type: Keep-alive";
chunk = curl_slist_append(chunk, authHeader.c_str());
chunk = curl_slist_append(chunk, keepAliveHeader);
curl_easy_setopt(curl, CURLOPT_URL, fullUri.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, respBodyRetriever);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
std::stringstream buff(data);
std::vector<char> vals(data.begin(), data.end());
//vals.reserve(data.size());
//vals.assign(data.begin(), data.end());
/**
vals.assign(std::istream_iterator<unsigned char>(buff),
std::istream_iterator<unsigned char>());
*/
//std::copy(data.begin(), data.end(), &vals);
return vals;
}
template<typename C>
C CoverArtRepository<C>::retrieveCoverArtRecord(const model::Token& token,
const C& cover,
const std::string& uri) {
model::CoverArt cov;
return cov;
}
template<typename C>
constexpr auto CoverArtRepository<C>::downloadEndpoint() noexcept {
return "api/v1/coverart/download/";
}
template<typename C>
constexpr auto CoverArtRepository<C>::recordsEndpoint() noexcept {
return "api/v1/coverart/";
}
template<typename C>
size_t CoverArtRepository<C>::respBodyRetriever(void *ptr, size_t size, size_t nmemb, char *e) {
((std::string*)e)->append((char*)ptr, size * nmemb);
return size * nmemb;
}
}
#endif //MEAR_COVERARTREPOSITORY_H
+63
View File
@@ -24,6 +24,7 @@
#include <sys/mman.h>
#include "APIRepository.h"
#include "CoverArtRepository.h"
#include "RepeatRepository.h"
#include "ShuffleRepository.h"
#include "SongRepository.h"
@@ -51,6 +52,7 @@ jobject songToObj(JNIEnv *env, const model::Song& song)
jmethodID songId = env->GetMethodID( songClass, "setId", "(I)V" );
jmethodID songTitle = env->GetMethodID( songClass, "setTitle", "(Ljava/lang/String;)V" );
jmethodID songAlbum = env->GetMethodID( songClass, "setAlbum", "(Ljava/lang/String;)V" );
jmethodID songAlbumArtist = env->GetMethodID(songClass, "setAlbumArtist", "(Ljava/lang/String;)V");
jmethodID songArtist = env->GetMethodID( songClass, "setArtist", "(Ljava/lang/String;)V" );
jmethodID songGenre = env->GetMethodID( songClass, "setGenre", "(Ljava/lang/String;)V" );
jmethodID songDuration = env->GetMethodID( songClass, "setDuration", "(I)V" );
@@ -60,6 +62,7 @@ jobject songToObj(JNIEnv *env, const model::Song& song)
jint id = song.id;
jstring title = env->NewStringUTF(song.title.c_str());
jstring album = env->NewStringUTF(song.album.c_str());
jstring albumArtist = env->NewStringUTF(song.albumArtist.c_str());
jstring artist = env->NewStringUTF(song.artist.c_str());
jstring genre = env->NewStringUTF(song.genre.c_str());
jint duration = song.duration;
@@ -69,6 +72,7 @@ jobject songToObj(JNIEnv *env, const model::Song& song)
env->CallVoidMethod( songObj, songId, id );
env->CallVoidMethod(songObj, songTitle, title);
env->CallVoidMethod(songObj, songAlbum, album);
env->CallVoidMethod(songObj, songAlbumArtist, albumArtist);
env->CallVoidMethod(songObj, songArtist, artist);
env->CallVoidMethod(songObj, songGenre, genre);
env->CallVoidMethod(songObj, songDuration, duration);
@@ -113,6 +117,9 @@ model::Token fetchToken(const model::User& user, const std::string& apiUri)
model::Token retrieveSavedToken(const std::string& path)
{
repository::local::TokenRepository tokenRepo;
if (!tokenRepo.databaseExist(path)) {
tokenRepo.initializedDatabase(path);
}
auto token = tokenRepo.retrieveToken(path);
return token;
@@ -122,6 +129,9 @@ model::Token retrieveSavedToken(const std::string& path)
model::User retrieveCredentials(const std::string& dataPath)
{
repository::local::UserRepository userRepo;
if (!userRepo.databaseExist(dataPath)) {
userRepo.initializedDatabase(dataPath);
}
auto user = userRepo.retrieveUserCredentials(dataPath);
return user;
@@ -131,6 +141,9 @@ model::User retrieveCredentials(const std::string& dataPath)
int retrieveRepeatMode(const std::string& path)
{
repository::local::RepeatRepository repeatRepo;
if (!repeatRepo.databaseExist(path)) {
repeatRepo.initializedDatabase(path);
}
if (!repeatRepo.doesTableExist(path)) {
repeatRepo.createRepeatTable(path);
}
@@ -143,6 +156,9 @@ int retrieveRepeatMode(const std::string& path)
int retrieveShuffleMode(const std::string& path)
{
repository::local::ShuffleRepository shuffleRepo;
if (!shuffleRepo.databaseExist(path)) {
shuffleRepo.initializedDatabase(path);
}
if (!shuffleRepo.doesTableExist(path)) {
shuffleRepo.createShuffleTable(path);
}
@@ -236,12 +252,18 @@ void saveToken(const model::Token& token, const std::string& path)
void updateRepeatMode(const std::string& path)
{
repository::local::RepeatRepository repeatRepo;
if (!repeatRepo.databaseExist(path)) {
repeatRepo.initializedDatabase(path);
}
repeatRepo.updateRepeat(path);
}
void updateShuffleMode(const std::string& path)
{
repository::local::ShuffleRepository shuffleRepo;
if (!shuffleRepo.databaseExist(path)) {
shuffleRepo.initializedDatabase(path);
}
shuffleRepo.updateShuffle(path);
}
@@ -418,6 +440,47 @@ Java_com_example_mear_repositories_UserRepository_logUser(
}
extern "C"
JNIEXPORT jbyteArray
JNICALL
Java_com_example_mear_repositories_CoverArtRepository_retrieveCoverArtImage(
JNIEnv *env,
jobject thisObj,
jobject tokenObj,
jobject coverArt,
jstring apiUri
) {
jclass coverArtClass = env->GetObjectClass(coverArt);
auto idField = env->GetFieldID(coverArtClass, "id", "I");
auto titleField = env->GetFieldID(coverArtClass, "title", "Ljava/lang/String;");
auto id = env->GetIntField(coverArt, idField);
auto title = (jstring)env->GetObjectField(coverArt, titleField);
jclass tokenClass = env->GetObjectClass(tokenObj);
auto accessTokenId = env->GetFieldID(tokenClass, "accessToken", "Ljava/lang/String;");
auto accessTokenVal = (jstring) env->GetObjectField(tokenObj, accessTokenId);
model::Token token;
token.accessToken = env->GetStringUTFChars(accessTokenVal, nullptr);
model::CoverArt cover(id, env->GetStringUTFChars(title, nullptr));
repository::CoverArtRepository<model::CoverArt> coverArtRepo;
auto data = coverArtRepo.retrieveCoverArtData(token, cover, env->GetStringUTFChars(apiUri, nullptr));
jbyteArray image = env->NewByteArray(data.size());
//for (auto& b: data) {
env->SetByteArrayRegion(image, 0, data.size(), (jbyte*) data.data());
//}
//constexpr auto testImagePath = "/data/data/com.example.mear/image.png";
//std::ofstream out(testImagePath, std::ios::out | std::ios::binary);
//out.write(reinterpret_cast<char*>(data.data()), data.size());
//out.close();
return image;
}
extern "C"
JNIEXPORT jint
JNICALL
+1
View File
@@ -10,6 +10,7 @@
#include <jni.h>
#include "model/APIInfo.h"
#include "model/CoverArt.h"
#include "model/Song.h"
#include "model/Token.h"
#include "model/User.h"
+22 -11
View File
@@ -15,7 +15,8 @@ namespace repository {
if (fullUri.at(fullUri.size()-1) != '/') {
fullUri.append("/");
}
fullUri.append("api/v1/song");
//fullUri.append("api/v1/song");
fullUri.append(songRecordEndpoint());
std::vector<model::Song> songs;
CURL *curl;
@@ -27,29 +28,31 @@ namespace repository {
return songs;
}
constexpr auto EXPECTED_CHAR_AMOUNT = 100000;
auto resp = std::make_unique<char[]>(EXPECTED_CHAR_AMOUNT);
std::string resp;
std::string authInfo("Authorization: Bearer ");
authInfo.append(token.accessToken);
constexpr auto contentType = "Content-type: Keep-alive";
chunk = curl_slist_append(chunk, authInfo.c_str());
chunk = curl_slist_append(chunk, contentType);
curl_easy_setopt(curl, CURLOPT_URL, fullUri.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, respBodyRetriever);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, resp.get());
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if (res == CURLE_OK) {
auto songsJson = nlohmann::json::parse(resp.get());
auto songsJson = nlohmann::json::parse(resp.c_str());
for (auto& songJson: songsJson) {
model::Song song(songJson["id"].get<int>(), songJson["title"].get<std::string>(),
songJson["artist"].get<std::string>(), songJson["album"].get<std::string>(),
songJson["genre"].get<std::string>(), songJson["duration"].get<int>(),
songJson["year"].get<int>());
song.albumArtist = songJson["album_artist"].get<std::string>();
song.coverArtId = songJson["coverart_id"].get<int>();
songs.push_back(song);
@@ -63,13 +66,13 @@ namespace repository {
model::Song SongRepository::retrieveSong(const model::Token& token, const model::Song& sng,
const std::string& baseUri) {
std::string uri(baseUri);
uri.append("/api/v1/song/");
uri.append(songRecordEndpoint());
uri.append(std::to_string(sng.id));
model::Song song;
CURL *curl;
CURLcode res;
struct curl_slist *chunk = NULL;
struct curl_slist *chunk = nullptr;
curl = curl_easy_init();
if (!curl) {
@@ -80,7 +83,9 @@ namespace repository {
std::string authInfo("Authorization: Bearer ");
authInfo.append(token.accessToken);
constexpr auto contentType = "Content-type: Keep-alive";
chunk = curl_slist_append(chunk, authInfo.c_str());
chunk = curl_slist_append(chunk, contentType);
curl_easy_setopt(curl, CURLOPT_URL, uri.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
@@ -96,6 +101,7 @@ namespace repository {
song.id = s["id"].get<int>();
song.title = s["title"].get<std::string>();
song.album = s["album"].get<std::string>();
song.albumArtist = s["album_artist"].get<std::string>();
song.artist = s["artist"].get<std::string>();
song.genre = s["genre"].get<std::string>();
song.duration = s["duration"].get<int>();
@@ -111,9 +117,14 @@ namespace repository {
size_t SongRepository::respBodyRetriever(void* ptr, size_t size, size_t nmemb, char *e)
{
std::memcpy(e, ptr, nmemb);
e[nmemb] = '\0';
return nmemb;
((std::string*)e)->append((char*)ptr, size * nmemb);
return size * nmemb;
}
/**
constexpr auto SongRepository::songRecordEndpoint() noexcept {
return "api/v1/song";
}
*/
}
+6
View File
@@ -20,7 +20,13 @@ namespace repository {
model::Song retrieveSong(const model::Token&, const model::Song&, const std::string&);
private:
static size_t respBodyRetriever(void*, size_t, size_t, char*);
constexpr auto songRecordEndpoint() noexcept;
};
constexpr auto SongRepository::songRecordEndpoint() noexcept {
return "api/v1/song/";
}
}
+22 -43
View File
@@ -4,6 +4,7 @@
#include "Tok.h"
#include <algorithm>
#include <memory>
#include <cstring>
@@ -13,6 +14,7 @@
namespace manager {
model::Token Tok::fetchTokenTrans(const model::User &user, const std::string& uri) {
CURL *curl;
struct curl_slist *chunk = nullptr;
CURLcode res;
curl = curl_easy_init();
@@ -21,71 +23,49 @@ namespace manager {
return model::Token("none");
}
auto resp = std::make_unique<char[]>(10000);
std::string resp;
const auto loginUri = fetchLoginUri(uri);
const auto obj = userJsonString(user);
constexpr auto contentType = "Content-type: Keep-alive";
chunk = curl_slist_append(chunk , contentType);
curl_easy_setopt(curl, CURLOPT_URL, loginUri.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, obj.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, respBodyRetriever);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, resp.get());
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if (res == CURLE_OK) {
auto s = nlohmann::json::parse(resp.get());
auto s = nlohmann::json::parse(resp);
const auto tokenStr = std::move(s["token"].get<std::string>());
return model::Token(s["token"].get<std::string>());
return model::Token(std::move(tokenStr));
}
return model::Token("failure");
}
std::string Tok::fetchToken(const model::User &user, const std::string& uri) {
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if (!curl) {
return "none";
}
auto resp = std::make_unique<char[]>(10000);
const auto loginUri = fetchLoginUri(uri);
const auto obj = userJsonString(user);
curl_easy_setopt(curl, CURLOPT_URL, loginUri.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, obj.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, respBodyRetriever);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, resp.get());
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if (res == CURLE_OK) {
auto s = nlohmann::json::parse(resp.get());
return s["token"].get<std::string>();
}
return "failure";
constexpr auto Tok::loginEndpoint() noexcept
{
return "api/v1/login";
}
std::string Tok::fetchLoginUri(const std::string& baseUri)
std::string Tok::fetchLoginUri(const std::string& uriBase) noexcept
{
std::string uri(baseUri);
uri.append("/api/v1/login");
std::string uri(uriBase);
if (uri.at(uri.size() - 1) != '/') {
uri.append("/");
}
uri.append(loginEndpoint());
return uri;
}
std::string Tok::userJsonString(const model::User& user)
{
nlohmann::json usr;
@@ -98,9 +78,8 @@ namespace manager {
size_t Tok::respBodyRetriever(void* ptr, size_t size, size_t nmemb, char *e)
{
std::memcpy(e, ptr, nmemb);
e[nmemb] = '\0';
((std::string*)e)->append((char*)ptr, size * nmemb);
return nmemb;
return size * nmemb;
}
}
+2 -3
View File
@@ -16,10 +16,9 @@ namespace manager {
class Tok {
public:
model::Token fetchTokenTrans(const model::User&, const std::string&);
std::string fetchToken(const model::User&, const std::string&);
private:
std::string fetchLoginUri(const std::string&);
std::string fetchLoginUri(const std::string&) noexcept;
constexpr auto loginEndpoint() noexcept;
std::string userJsonString(const model::User&);
static size_t respBodyRetriever(void*, size_t, size_t, char*);
+2 -1
View File
@@ -26,7 +26,8 @@ namespace repository { namespace local {
SQLite::Statement query(db, queryString);
auto result = query.executeStep();
model::Token token(query.getColumn(1).getString());
//const std::string t(query.getColumn(1).getString());
token.accessToken = std::move(query.getColumn(1).getString());
return token;
} catch (std::exception& ex) {
+22
View File
@@ -0,0 +1,22 @@
//
// Created by brahmix on 11/8/19.
//
#ifndef MEAR_COVERART_H
#define MEAR_COVERART_H
#include <string>
namespace model {
class CoverArt {
public:
CoverArt() = default;
CoverArt(const int id, const std::string& title) :
id(id), title(title) { }
int id;
std::string title;
};
}
#endif //MEAR_COVERART_H
+1
View File
@@ -30,6 +30,7 @@ public:
int id;
std::string title;
std::string artist;
std::string albumArtist;
std::string album;
std::string genre;
int duration;
+1 -1
View File
@@ -15,7 +15,7 @@ namespace model {
Token() = default;
Token(const std::string& accessToken) : accessToken(accessToken) { }
Token(const std::string&& accessToken) :
accessToken(std::move(accessToken)) { }
accessToken(accessToken) { }
std::string accessToken;
};