Adding downloading functionality, left TODO's on where to pick up from
This commit is contained in:
@@ -21,11 +21,9 @@ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -O3 -std=c++17 -DNDEBUG")
|
|||||||
set (SOURCES
|
set (SOURCES
|
||||||
APIRepository.cpp
|
APIRepository.cpp
|
||||||
BaseRepository.cpp
|
BaseRepository.cpp
|
||||||
CoverArtRepository.cpp
|
|
||||||
Demo.cpp
|
Demo.cpp
|
||||||
RepeatRepository.cpp
|
RepeatRepository.cpp
|
||||||
ShuffleRepository.cpp
|
ShuffleRepository.cpp
|
||||||
SongRepository.cpp
|
|
||||||
Tok.cpp
|
Tok.cpp
|
||||||
TokenRepository.cpp
|
TokenRepository.cpp
|
||||||
UserRepository.cpp
|
UserRepository.cpp
|
||||||
@@ -49,6 +47,7 @@ set (HEADERS
|
|||||||
Tok.h
|
Tok.h
|
||||||
TokenRepository.h
|
TokenRepository.h
|
||||||
UserRepository.h
|
UserRepository.h
|
||||||
|
GeneralUtility.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(JSON_BuildTests OFF CACHE INTERNAL "")
|
set(JSON_BuildTests OFF CACHE INTERNAL "")
|
||||||
|
|||||||
@@ -21,94 +21,76 @@ namespace repository {
|
|||||||
template<typename C>
|
template<typename C>
|
||||||
class CoverArtRepository {
|
class CoverArtRepository {
|
||||||
public:
|
public:
|
||||||
std::vector<C> retrieveCoverArtRecords(const model::Token&, const std::string&);
|
// TODO: implement this
|
||||||
std::vector<data> retrieveCoverArtData(const model::Token&, const C&, const std::string&);
|
std::vector<C> retrieveCoverArtRecords(const model::Token& token, const std::string& uri) {
|
||||||
|
std::vector<std::string> vals;
|
||||||
|
|
||||||
|
return vals;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<data> 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;
|
||||||
|
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);
|
||||||
|
|
||||||
|
auto res = curl_easy_perform(curl);
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
|
||||||
|
std::vector<char> vals(data.begin(), data.end());
|
||||||
|
|
||||||
|
return vals;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: implement this
|
||||||
|
C retrieveCoverArtRecord(const model::Token& token, const C& cover,
|
||||||
|
const std::string& uri) {
|
||||||
|
C cov;
|
||||||
|
|
||||||
|
return cov;
|
||||||
|
}
|
||||||
|
|
||||||
C retrieveCoverArtRecord(const model::Token&, const C&, const std::string&);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
constexpr auto downloadEndpoint() noexcept;
|
constexpr auto downloadEndpoint() noexcept {
|
||||||
constexpr auto recordsEndpoint() noexcept;
|
return "api/v1/coverart/download/";
|
||||||
|
}
|
||||||
|
|
||||||
static size_t respBodyRetriever(void*, size_t, size_t, char*);
|
constexpr auto recordsEndpoint() noexcept {
|
||||||
|
return "api/v1/coverart/";
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t respBodyRetriever(void *ptr, size_t size,
|
||||||
|
size_t nmemb, char *e) {
|
||||||
|
((std::string*)e)->append((char*)ptr, size * nmemb);
|
||||||
|
|
||||||
|
return size * nmemb;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
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());
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -388,6 +388,24 @@ Java_com_example_mear_repositories_TrackRepository_retrieveSong(
|
|||||||
return fetchedSongObj;
|
return fetchedSongObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT jobject
|
||||||
|
JNICALL
|
||||||
|
Java_com_example_mear_repositories_TrackRepositories_downloadSong(
|
||||||
|
JNIEnv *env,
|
||||||
|
jobject thisObj,
|
||||||
|
jobject tokenObj,
|
||||||
|
jobject songObj,
|
||||||
|
jstring uriStr
|
||||||
|
) {
|
||||||
|
// TODO: left off here
|
||||||
|
model::Song downloadedSong;
|
||||||
|
auto downloadedSongObj = songToObj(env, downloadedSong);
|
||||||
|
|
||||||
|
return downloadedSongObj;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
JNIEXPORT jobject
|
JNIEXPORT jobject
|
||||||
JNICALL
|
JNICALL
|
||||||
@@ -478,14 +496,7 @@ Java_com_example_mear_repositories_CoverArtRepository_retrieveCoverArtImage(
|
|||||||
auto data = coverArtRepo.retrieveCoverArtData(token, cover, env->GetStringUTFChars(apiUri, nullptr));
|
auto data = coverArtRepo.retrieveCoverArtData(token, cover, env->GetStringUTFChars(apiUri, nullptr));
|
||||||
|
|
||||||
jbyteArray image = env->NewByteArray(data.size());
|
jbyteArray image = env->NewByteArray(data.size());
|
||||||
//for (auto& b: data) {
|
|
||||||
env->SetByteArrayRegion(image, 0, data.size(), (jbyte*) data.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;
|
return image;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,116 +8,26 @@
|
|||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
namespace repository {
|
namespace repository {
|
||||||
|
/**
|
||||||
std::vector<model::Song> SongRepository::fetchSongs(const model::Token& token,
|
std::vector<model::Song> SongRepository::fetchSongs(const model::Token& token,
|
||||||
const std::string& uri)
|
const std::string& uri)
|
||||||
{
|
{
|
||||||
std::string fullUri(uri);
|
|
||||||
if (fullUri.at(fullUri.size()-1) != '/') {
|
|
||||||
fullUri.append("/");
|
|
||||||
}
|
|
||||||
|
|
||||||
fullUri.append(songRecordEndpoint());
|
|
||||||
std::vector<model::Song> songs;
|
|
||||||
|
|
||||||
CURL *curl;
|
|
||||||
CURLcode res;
|
|
||||||
struct curl_slist *chunk = nullptr;
|
|
||||||
curl = curl_easy_init();
|
|
||||||
|
|
||||||
if (!curl) {
|
|
||||||
return songs;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
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.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return songs;
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
model::Song SongRepository::retrieveSong(const model::Token& token, const model::Song& sng,
|
model::Song SongRepository::retrieveSong(const model::Token& token, const model::Song& sng,
|
||||||
const std::string& baseUri) {
|
const std::string& baseUri) {
|
||||||
std::string uri(baseUri);
|
|
||||||
uri.append(songRecordEndpoint());
|
|
||||||
uri.append(std::to_string(sng.id));
|
|
||||||
model::Song song;
|
|
||||||
|
|
||||||
CURL *curl;
|
|
||||||
CURLcode res;
|
|
||||||
struct curl_slist *chunk = nullptr;
|
|
||||||
curl = curl_easy_init();
|
|
||||||
|
|
||||||
if (!curl) {
|
|
||||||
return sng;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto resp = std::make_unique<char[]>(10000);
|
|
||||||
|
|
||||||
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);
|
|
||||||
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());
|
|
||||||
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>();
|
|
||||||
song.year = s["year"].get<int>();
|
|
||||||
song.coverArtId = s["coverart_id"].get<int>();
|
|
||||||
|
|
||||||
return song;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sng;
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
size_t SongRepository::respBodyRetriever(void* ptr, size_t size, size_t nmemb, char *e)
|
size_t SongRepository::respBodyRetriever(void* ptr, size_t size, size_t nmemb, char *e)
|
||||||
{
|
{
|
||||||
((std::string*)e)->append((char*)ptr, size * nmemb);
|
((std::string*)e)->append((char*)ptr, size * nmemb);
|
||||||
return size * nmemb;
|
return size * nmemb;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,25 +8,170 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
#include "model/Song.h"
|
#include "model/Song.h"
|
||||||
#include "model/Token.h"
|
#include "model/Token.h"
|
||||||
|
#include "GeneralUtility.h"
|
||||||
|
|
||||||
namespace repository {
|
namespace repository {
|
||||||
|
|
||||||
|
template<class Song = model::Song>
|
||||||
class SongRepository {
|
class SongRepository {
|
||||||
public:
|
public:
|
||||||
std::vector<model::Song> fetchSongs(const model::Token&, const std::string&);
|
template<typename Token = model::Token>
|
||||||
|
std::vector<Song> fetchSongs(const Token& token, const std::string& uri) {
|
||||||
|
auto fullUri = utility::GeneralUtility::appendForwardSlashToUri<std::string>(uri);
|
||||||
|
fullUri.append(songRecordEndpoint());
|
||||||
|
std::vector<Song> songs;
|
||||||
|
|
||||||
model::Song retrieveSong(const model::Token&, const model::Song&, const std::string&);
|
CURL *curl;
|
||||||
|
CURLcode res;
|
||||||
|
struct curl_slist *chunk = nullptr;
|
||||||
|
curl = curl_easy_init();
|
||||||
|
|
||||||
|
if (!curl) {
|
||||||
|
return songs;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
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.c_str());
|
||||||
|
for (auto& songJson: songsJson) {
|
||||||
|
Song song(songJson["id"].get<int>(), songJson["title"].get<std::string>(),
|
||||||
|
songJson["artist"].get<std::string>(),
|
||||||
|
songJson["album"].get<std::string>(),
|
||||||
|
songJson["album_artist"].get<std::string>(),
|
||||||
|
songJson["genre"].get<std::string>(), songJson["duration"].get<int>(),
|
||||||
|
songJson["year"].get<int>(), songJson["coverart_id"].get<int>());
|
||||||
|
/**
|
||||||
|
song.albumArtist = songJson["album_artist"].get<std::string>();
|
||||||
|
song.coverArtId = songJson["coverart_id"].get<int>();
|
||||||
|
*/
|
||||||
|
|
||||||
|
songs.push_back(song);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return songs;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Token = model::Token>
|
||||||
|
Song retrieveSong(const Token& token, const Song& sng,
|
||||||
|
const std::string& baseUri) {
|
||||||
|
auto uri = utility::GeneralUtility::appendForwardSlashToUri(baseUri);
|
||||||
|
uri.append(songRecordEndpoint());
|
||||||
|
uri.append(std::to_string(sng.id));
|
||||||
|
|
||||||
|
CURL *curl;
|
||||||
|
CURLcode res;
|
||||||
|
struct curl_slist *chunk = nullptr;
|
||||||
|
curl = curl_easy_init();
|
||||||
|
|
||||||
|
if (!curl) {
|
||||||
|
return sng;
|
||||||
|
}
|
||||||
|
|
||||||
|
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, uri.c_str());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, respBodyRetriever);
|
||||||
|
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.c_str());
|
||||||
|
Song song(s["id"].get<int>(), s["title"].get<std::string>(),
|
||||||
|
s["artist"].get<std::string>(), s["album"].get<std::string>(),
|
||||||
|
s["album_artist"].get<std::string>(), s["genre"].get<std::string>(),
|
||||||
|
s["duration"].get<int>(), s["year"].get<int>(),
|
||||||
|
s["coverart_id"].get<int>());
|
||||||
|
/**
|
||||||
|
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>();
|
||||||
|
song.year = s["year"].get<int>();
|
||||||
|
song.coverArtId = s["coverart_id"].get<int>();
|
||||||
|
*/
|
||||||
|
|
||||||
|
return song;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sng;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Token = model::Token>
|
||||||
|
Song downloadSong(const Token &token, const Song song, const std::string& uri) {
|
||||||
|
auto fullUri = utility::GeneralUtility::appendForwardSlashToUri(uri);
|
||||||
|
fullUri.append(songDownloadEndpoint());
|
||||||
|
fullUri.append(std::to_string(song.id));
|
||||||
|
Song downloadedSong;
|
||||||
|
|
||||||
|
CURL *curl = curl_easy_init();
|
||||||
|
struct curl_slist *chunk = nullptr;
|
||||||
|
|
||||||
|
std::string data;
|
||||||
|
const auto authHeader = utility::GeneralUtility::authHeader(token);
|
||||||
|
chunk = curl_slist_append(chunk, authHeader.c_str());
|
||||||
|
chunk = curl_slist_append(chunk, "Content-type: Keep-alive");
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
auto res = curl_easy_perform(curl);
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
downloadedSong.data = std::move(std::vector<char>(data.begin(), data.end()));
|
||||||
|
|
||||||
|
return downloadedSong;
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
static size_t respBodyRetriever(void*, size_t, size_t, char*);
|
static size_t respBodyRetriever(void *ptr, size_t size, size_t nmemb, char *e) {
|
||||||
|
((std::string*)e)->append((char*)ptr, size * nmemb);
|
||||||
|
return size * nmemb;
|
||||||
|
}
|
||||||
|
|
||||||
constexpr auto songRecordEndpoint() noexcept;
|
constexpr auto songRecordEndpoint() noexcept {
|
||||||
|
return "api/v1/song/";
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto songDownloadEndpoint() noexcept {
|
||||||
|
return "api/v1/song/data/";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr auto SongRepository::songRecordEndpoint() noexcept {
|
|
||||||
return "api/v1/song/";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#define MEAR_SONG_H
|
#define MEAR_SONG_H
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
namespace model {
|
namespace model {
|
||||||
@@ -20,22 +21,30 @@ public:
|
|||||||
const int duration, const int year) :
|
const int duration, const int year) :
|
||||||
id(id), title(title), artist(artist), album(album),
|
id(id), title(title), artist(artist), album(album),
|
||||||
genre(genre), duration(duration), year(year) { }
|
genre(genre), duration(duration), year(year) { }
|
||||||
Song(const int id, const std::string&& title, const std::string&& artist,
|
Song(const int id, const std::string& title, const std::string& artist,
|
||||||
const std::string&& album, const std::string&& genre,
|
const std::string& album, const std::string& albumArtist, const std::string& genre,
|
||||||
const int duration, const int year) :
|
const int duration, const int year, const int coverArtId) :
|
||||||
id(id), title(std::move(title)), artist(std::move(artist)),
|
id(id), title(title), artist(artist), album(album), albumArtist(albumArtist),
|
||||||
album(std::move(album)), genre(std::move(genre)), duration(duration),
|
genre(genre), duration(duration), year(year), coverArtId(coverArtId) { }
|
||||||
year(year) { }
|
/**
|
||||||
|
Song(const int id, const std::string&& title, const std::string&& artist,
|
||||||
|
const std::string&& album, const std::string&& genre,
|
||||||
|
const int duration, const int year) :
|
||||||
|
id(id), title(std::move(title)), artist(std::move(artist)),
|
||||||
|
album(std::move(album)), genre(std::move(genre)), duration(duration),
|
||||||
|
year(year) { }
|
||||||
|
*/
|
||||||
|
|
||||||
int id;
|
int id;
|
||||||
std::string title;
|
std::string title;
|
||||||
std::string artist;
|
std::string artist;
|
||||||
std::string albumArtist;
|
|
||||||
std::string album;
|
std::string album;
|
||||||
|
std::string albumArtist;
|
||||||
std::string genre;
|
std::string genre;
|
||||||
int duration;
|
int duration;
|
||||||
int year;
|
int year;
|
||||||
int coverArtId;
|
int coverArtId;
|
||||||
|
std::vector<char> data;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -368,6 +368,17 @@ class MainActivity : BaseServiceActivity() {
|
|||||||
R.id.action_song_view -> {
|
R.id.action_song_view -> {
|
||||||
startActivity(Intent(this, IcarusSongActivity::class.java))
|
startActivity(Intent(this, IcarusSongActivity::class.java))
|
||||||
}
|
}
|
||||||
|
R.id.action_song_download -> {
|
||||||
|
// TODO: implement download functionality
|
||||||
|
val appPath = appDirectory()
|
||||||
|
val apiRepo = APIRepository()
|
||||||
|
val tokenRepo = TokenRepository()
|
||||||
|
val trackRepo = TrackRepository()
|
||||||
|
val song = musicService!!.getCurrentSong()
|
||||||
|
|
||||||
|
val token = tokenRepo.retrieveToken(appPath)
|
||||||
|
val apiInfo = apiRepo.retrieveRecord(appPath)
|
||||||
|
}
|
||||||
R.id.action_song_play_count-> {
|
R.id.action_song_play_count-> {
|
||||||
val trk = musicService!!.getCurrentSong()
|
val trk = musicService!!.getCurrentSong()
|
||||||
val pc = PlayCountRepository(this).getPlayCount(trk.id)
|
val pc = PlayCountRepository(this).getPlayCount(trk.id)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class TrackRepository(var context: Context? = null) {
|
|||||||
private external fun retrieveSongs(token: Token, uri: String): Array<Song>
|
private external fun retrieveSongs(token: Token, uri: String): Array<Song>
|
||||||
|
|
||||||
private external fun retrieveSong(token: Token, song: Song, uri: String): Song
|
private external fun retrieveSong(token: Token, song: Song, uri: String): Song
|
||||||
|
private external fun downloadSong(token: Token, song: Song, uri: String): Song
|
||||||
|
|
||||||
|
|
||||||
fun fetchSongs(token: Token, uri: String): Array<Song> {
|
fun fetchSongs(token: Token, uri: String): Array<Song> {
|
||||||
@@ -32,55 +33,12 @@ class TrackRepository(var context: Context? = null) {
|
|||||||
return retrieveSong(token, song, uri)
|
return retrieveSong(token, song, uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAll(): List<Track> = context!!.database.use {
|
fun fetchSongFile(token: Token, song: Song, uri: String): Song {
|
||||||
val tracks = mutableListOf<Track>()
|
val downloadedSong = downloadSong(token, song, uri)
|
||||||
|
|
||||||
select("Track" )
|
return downloadedSong
|
||||||
.parseList(object : MapRowParser<Track>{
|
|
||||||
override fun parseRow(columns: Map<String, Any?>): Track {
|
|
||||||
val id = columns.getValue("Id")
|
|
||||||
val title = columns.getValue("Title")
|
|
||||||
val artist = columns.getValue("Artist").toString()
|
|
||||||
val album = columns.getValue("Album").toString()
|
|
||||||
val duration = columns.getValue("Duration").toString().toInt()
|
|
||||||
val songPath = columns.getValue("FilePath").toString()
|
|
||||||
|
|
||||||
val track = Track(id.toString().toInt(), title.toString(),artist,album,
|
|
||||||
duration, ByteArray(0),songPath)
|
|
||||||
tracks.add(track)
|
|
||||||
|
|
||||||
return track
|
|
||||||
}
|
|
||||||
})
|
|
||||||
tracks
|
|
||||||
}
|
}
|
||||||
fun getTrack(id: Int): Track = context!!.database.use {
|
|
||||||
select("Track").where("Id = $id")
|
|
||||||
.parseSingle(object: MapRowParser<Track>{
|
|
||||||
override fun parseRow(columns: Map<String, Any?>): Track {
|
|
||||||
val id = columns.getValue("Id").toString().toInt()
|
|
||||||
val title = columns.getValue("Title").toString()
|
|
||||||
val artist = columns.getValue("Artist").toString()
|
|
||||||
var album = columns.getValue("Album").toString()
|
|
||||||
val duration = columns.getValue("Duration").toString().toInt()
|
|
||||||
var filePath = columns.getValue("FilePath").toString()
|
|
||||||
|
|
||||||
val track = Track(id, title, artist, album, duration,
|
|
||||||
ByteArray(0), filePath)
|
|
||||||
|
|
||||||
return track
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fun getSongCount(): Int = context!!.database.use {
|
|
||||||
select("TrackCount").limit(1)
|
|
||||||
.parseSingle(object : MapRowParser<Int>{
|
|
||||||
override fun parseRow(columns: Map<String, Any?>): Int {
|
|
||||||
|
|
||||||
return columns.getValue("TotalSongs").toString().toInt()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fun insertTracks(tracks: List<Track>) = context!!.database.use {
|
fun insertTracks(tracks: List<Track>) = context!!.database.use {
|
||||||
transaction {
|
transaction {
|
||||||
@@ -111,30 +69,4 @@ class TrackRepository(var context: Context? = null) {
|
|||||||
fun delete() = context!!.database.use {
|
fun delete() = context!!.database.use {
|
||||||
delete("Track")
|
delete("Track")
|
||||||
}
|
}
|
||||||
fun deleteTracks(tracks: List<Track>) = context!!.database.use {
|
|
||||||
tracks.iterator().forEach {
|
|
||||||
transaction {
|
|
||||||
delete("Track", "id = {track_id}", "track_id" to it.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fun deleteTracksByPaths(trackPaths: List<String>) = context!!.database.use {
|
|
||||||
trackPaths.iterator().forEach {
|
|
||||||
transaction {
|
|
||||||
delete("Track", "FilePath = {file_path}", "file_path" to it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getLibraryCount(): Int? {
|
|
||||||
context!!.database.use {
|
|
||||||
select("TrackCount").limit(1)
|
|
||||||
.parseList (object : MapRowParser<Int> {
|
|
||||||
override fun parseRow(columns: Map<String, Any?>): Int {
|
|
||||||
return columns.getValue("TotalSongs").toString().toInt()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,9 @@
|
|||||||
<item android:id="@+id/action_song_view"
|
<item android:id="@+id/action_song_view"
|
||||||
android:title="@string/song_view"
|
android:title="@string/song_view"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
<item android:id="@+id/action_song_download"
|
||||||
|
android:title="@string/song_download"
|
||||||
|
app:showAsAction="never" />
|
||||||
<item android:id="@+id/action_song_play_count"
|
<item android:id="@+id/action_song_play_count"
|
||||||
android:title="@string/song_play_count"
|
android:title="@string/song_play_count"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
<string name="settings">Settings</string>
|
<string name="settings">Settings</string>
|
||||||
<string name="song_view">Songs</string>
|
<string name="song_view">Songs</string>
|
||||||
<string name="song_play_count">Play Count</string>
|
<string name="song_play_count">Play Count</string>
|
||||||
|
<string name="song_download">Download</string>
|
||||||
|
|
||||||
<!-- Popups -->
|
<!-- Popups -->
|
||||||
<string name="popup_about_header">About</string>
|
<string name="popup_about_header">About</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user