Doing some refactoring and some skeleton work for downloading songs

This commit is contained in:
kdeng00
2019-11-29 19:54:10 -05:00
parent d199b482ad
commit 1e70f84622
46 changed files with 1859 additions and 1341 deletions
+112
View File
@@ -0,0 +1,112 @@
//
// Created by brahmix on 10/12/19.
//
#ifndef MEAR_APIREPOSITORY_H
#define MEAR_APIREPOSITORY_H
#include <string>
#include "BaseRepository.h"
#include "../model/APIInfo.h"
namespace repository { namespace local {
class APIRepository: public BaseRepository {
public:
APIRepository() {
m_tableName = apiInfoTable();
}
model::APIInfo retrieveAPIInfo(const std::string& path) {
model::APIInfo apiInfo;
try {
const auto dbPath = pathOfDatabase(path);
SQLite::Database db(dbPath, SQLite::OPEN_READONLY);
std::string queryString("SELECT * FROM ");
queryString.append(m_tableName);
queryString.append(" LIMIT 1");
SQLite::Statement query(db, queryString);
auto result = query.executeStep();
apiInfo.uri = query.getColumn(1).getString();
apiInfo.version = query.getColumn(2).getInt();
return apiInfo;
} catch (std::exception& ex) {
auto msg = ex.what();
}
return apiInfo;
}
[[deprecated("use the base class function")]]
bool doesAPIInfoTableExist(const std::string& path) {
try {
const auto dbPath = pathOfDatabase(path);
SQLite::Database db(dbPath, SQLite::OPEN_READONLY);
return db.tableExists(m_tableName);
} catch (std::exception& ex) {
auto msg = ex.what();
}
return false;
}
void createAPiInfoTable(const std::string& path) {
try {
const auto dbPath = pathOfDatabase(path);
SQLite::Database db(dbPath, SQLite::OPEN_READWRITE);
std::string queryString("CREATE TABLE ");
queryString.append(m_tableName);
queryString.append(" (Id INTEGER PRIMARY KEY, Uri TEXT, Version INT)");
db.exec(queryString);
} catch (std::exception& ex) {
auto msg = ex.what();
}
}
void deleteAPIInfo(const model::APIInfo& apiInfo, const std::string& path) {
try {
const auto dbPath = pathOfDatabase(path);
SQLite::Database db(dbPath, SQLite::OPEN_READWRITE);
std::string queryString("DELETE FROM ");
queryString.append(m_tableName);
db.exec(queryString);
} catch (std::exception& ex) {
auto msg = ex.what();
}
}
void saveAPIInfo(const model::APIInfo& apiInfo, const std::string& path) {
try {
const auto dbPath = pathOfDatabase(path);
SQLite::Database db(dbPath, SQLite::OPEN_READWRITE);
std::string queryString("INSERT INTO ");
queryString.append(m_tableName);
queryString.append(" (Uri, Version) VALUES (?, ?)");
SQLite::Statement query(db, queryString);
query.bind(1, apiInfo.uri);
query.bind(2, apiInfo.version);
query.exec();
} catch (std::exception& ex) {
auto msg = ex.what();
}
}
private:
std::string apiInfoTable() noexcept { return "APIInfo"; }
};
}}
#endif //MEAR_APIREPOSITORY_H
@@ -0,0 +1,125 @@
//
// Created by brahmix on 10/6/19.
//
#ifndef MEAR_BASEREPOSITORY_H
#define MEAR_BASEREPOSITORY_H
#include <string>
#include <exception>
#include <SQLiteCpp/Database.h>
#include "../types/ConnType.h"
namespace repository { namespace local {
using type::ConnType;
class BaseRepository {
public:
bool databaseExist(const std::string& appPath) {
try {
const auto dbPath = pathOfDatabase(appPath);
SQLite::Database db(dbPath, SQLite::OPEN_READONLY);
return true;
} catch (std::exception& ex) {
auto msg = ex.what();
}
return false;
}
bool doesTableExist(const std::string& appPath) {
try {
const auto dbPath = pathOfDatabase(appPath);
SQLite::Database db(dbPath, SQLite::OPEN_READONLY);
auto result = db.tableExists(m_tableName);
return result;
} catch (std::exception& ex) {
auto msg = ex.what();
}
return false;
}
bool isTableEmpty(const std::string& appPath) {
try {
const auto dbPath = pathOfDatabase(appPath);
SQLite::Database db(dbPath, SQLite::OPEN_READONLY);
std::string queryStr("SELECT * FROM ");
queryStr.append(m_tableName);
SQLite::Statement query(db, queryStr);
const auto result = query.hasRow();
return result;
} catch (std::exception& ex) {
auto msg = ex.what();
}
return true;
}
void initializedDatabase(const std::string& appPath) {
try {
const auto dbPath = pathOfDatabase(appPath);
SQLite::Database db(dbPath, SQLite::OPEN_CREATE | SQLite::OPEN_READWRITE);
} catch (std::exception& ex) {
auto msg = ex.what();
}
}
void deleteRecord(const std::string& appPath) {
try {
const auto dbPath = pathOfDatabase(appPath);
SQLite::Database db(dbPath, SQLite::OPEN_CREATE | SQLite::OPEN_READWRITE);
} catch (std::exception& ex) {
auto msg = ex.what();
}
}
protected:
std::string pathOfDatabase(const std::string& appPath) {
std::string dbPath(appPath);
auto lastChar = dbPath.at(dbPath.size()-1);
if (lastChar != '/') {
dbPath.append("/");
}
dbPath.append(databaseName());
return dbPath;
}
template<typename Str = std::string>
const Str databaseName() noexcept { return "mear.db3"; }
SQLite::Database getDbConn(const std::string& path, ConnType dbType) {
auto dbPath = pathOfDatabase(path);
switch (dbType) {
case ConnType::ReadOnly:
return SQLite::Database(dbPath, SQLite::OPEN_READONLY);
case ConnType::ReadWrite:
return SQLite::Database(dbPath, SQLite::OPEN_READWRITE);
case ConnType::Create:
return SQLite::Database(dbPath, SQLite::OPEN_CREATE);
default:
break;
}
return SQLite::Database(dbPath);
}
std::string m_tableName;
private:
};
} }
#endif //MEAR_BASEREPOSITORY_H
@@ -0,0 +1,97 @@
//
// Created by brahmix on 11/8/19.
//
#ifndef MEAR_COVERARTREPOSITORY_H
#define MEAR_COVERARTREPOSITORY_H
#include <string>
#include <sstream>
#include <iterator>
#include <vector>
#include "../3rdparty/android/include/curl/curl.h"
#include "../model/CoverArt.h"
#include "../model/Token.h"
using data = char;
namespace repository {
template<typename C>
class CoverArtRepository {
public:
// TODO: implement this
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;
}
private:
constexpr auto downloadEndpoint() noexcept {
return "api/v1/coverart/download/";
}
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;
}
};
}
#endif //MEAR_COVERARTREPOSITORY_H
@@ -0,0 +1,105 @@
//
// Created by brahmix on 10/21/19.
//
#ifndef MEAR_REPEATREPOSITORY_H
#define MEAR_REPEATREPOSITORY_H
#include <string>
#include "BaseRepository.h"
#include "../types/RepeatTypes.h"
namespace repository { namespace local {
class RepeatRepository : public BaseRepository {
public:
RepeatRepository() {
m_tableName = repeatTable();
}
RepeatTypes retrieveRepeatMode(const std::string& path) {
try {
auto db = getDbConn(path, ConnType::ReadOnly);
std::string queryString("SELECT * FROM ");
queryString.append(m_tableName);
queryString.append(" LIMIT 1");
SQLite::Statement query(db, queryString);
const auto result = query.executeStep();
auto repeatType = query.getColumn(1).getInt();
auto val = static_cast<RepeatTypes>(repeatType);
return val;
} catch (std::exception& ex) {
auto msg = ex.what();
}
return RepeatTypes::RepeatOff;
}
void createRepeatTable(const std::string& path) {
try {
auto db = getDbConn(path, ConnType::ReadWrite);
std::string queryString("CREATE TABLE ");
queryString.append(m_tableName);
queryString.append(" (Id INTEGER PRIMARY KEY, RepeatMode INT)");
db.exec(queryString);
queryString = "INSERT INTO " + m_tableName + " (RepeatMode) VALUES(?)";
SQLite::Statement query(db, queryString);
query.bind(1, static_cast<int>(RepeatTypes::RepeatOff));
query.exec();
} catch (std::exception &ex) {
auto msg = ex.what();
}
}
void updateRepeat(const std::string& path) {
try {
auto db = getDbConn(path, ConnType::ReadWrite);
if (isTableEmpty(path)) {
std::string queryString("INSERT INTO ");
queryString.append(m_tableName);
queryString.append(" (RepeatMode) VALUES (?)");
SQLite::Statement query(db, queryString);
query.bind(1, static_cast<int>(RepeatTypes::RepeatOff));
query.exec();
} else {
std::string queryString("UPDATE ");
queryString.append(m_tableName);
queryString.append(" SET RepeatMode = ?");
auto repeatMode = retrieveRepeatMode(path);
SQLite::Statement query(db, queryString);
switch (repeatMode) {
case RepeatTypes::RepeatOff:
query.bind(1, static_cast<int>(RepeatTypes::RepeatSong));
break;
case RepeatTypes::RepeatSong:
query.bind(1, static_cast<int>(RepeatTypes::RepeatOff));
break;
default:
break;
}
query.exec();
}
} catch (std::exception &ex) {
auto msg = ex.what();
}
}
private:
std::string repeatTable() noexcept { return "Repeat"; }
};
}}
#endif //MEAR_REPEATREPOSITORY_H
@@ -0,0 +1,109 @@
//
// Created by brahmix on 10/21/19.
//
#ifndef MEAR_SHUFFLEREPOSITORY_H
#define MEAR_SHUFFLEREPOSITORY_H
#include <string>
#include "BaseRepository.h"
#include "../types/ShuffleTypes.h"
namespace repository { namespace local {
class ShuffleRepository : public BaseRepository {
public:
ShuffleRepository() {
m_tableName = shuffleTable();
}
ShuffleTypes retrieveShuffleMode(const std::string& path) {
try {
auto db = getDbConn(path, ConnType::ReadOnly);
std::string queryString("SELECT * FROM ");
queryString.append(m_tableName);
queryString.append(" LIMIT 1");
SQLite::Statement query(db, queryString);
const auto result = query.executeStep();
auto shuffleType = query.getColumn(1).getInt();
auto val = static_cast<ShuffleTypes>(shuffleType);
return val;
} catch (std::exception &ex) {
auto msg = ex.what();
}
return ShuffleTypes::ShuffleOff;
}
void createShuffleTable(const std::string& path) {
try {
auto db = getDbConn(path, ConnType::ReadWrite);
std::string queryString("CREATE TABLE ");
queryString.append(m_tableName);
queryString.append(" (Id INTEGER PRIMARY KEY, ShuffleMode INT)");
db.exec(queryString);
queryString = "INSERT INTO ";
queryString.append(m_tableName);
queryString.append(" (ShuffleMode) VALUES(?)");
SQLite::Statement query(db, queryString);
query.bind(1, static_cast<int>(ShuffleTypes::ShuffleOff));
query.exec();
} catch (std::exception &ex) {
auto msg = ex.what();
}
}
void updateShuffle(const std::string& path) {
try {
auto db = getDbConn(path, ConnType::ReadWrite);
if (!doesTableExist(path)) {
createShuffleTable(path);
}
if (isTableEmpty(path)) {
constexpr auto queryString = "INSERT INTO ? (ShuffleMode) VALUES(?)";
SQLite::Statement query(db, queryString);
query.bind(1, m_tableName);
query.bind(2, static_cast<int>(ShuffleTypes::ShuffleOff));
query.exec();
} else {
std::string queryString("UPDATE ");
queryString.append(m_tableName);
queryString.append(" SET ShuffleMode = ?");
auto shuffleMode = retrieveShuffleMode(path);
SQLite::Statement query(db, queryString);
switch (shuffleMode) {
case ShuffleTypes::ShuffleOff:
query.bind(1, static_cast<int>(ShuffleTypes::ShuffleOn));
break;
case ShuffleTypes::ShuffleOn:
query.bind(1, static_cast<int>(ShuffleTypes::ShuffleOff));
break;
default:
break;
}
query.exec();
}
} catch (std::exception &ex) {
auto msg = ex.what();
}
}
private:
std::string shuffleTable() noexcept { return "Shuffle"; }
};
}}
#endif //MEAR_SHUFFLEREPOSITORY_H
@@ -0,0 +1,159 @@
//
// Created by brahmix on 10/3/19.
//
#ifndef MEAR_SONGREPOSITORY_H
#define MEAR_SONGREPOSITORY_H
#include <string>
#include <vector>
#include "../3rdparty/android/include/curl/curl.h"
#include "../3rdparty/json/single_include/nlohmann/json.hpp"
#include "../model/Song.h"
#include "../model/Token.h"
#include "../utility/GeneralUtility.h"
namespace repository {
template<class Song = model::Song>
class SongRepository {
public:
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;
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>());
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>());
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:
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 { return "api/v1/song/"; }
constexpr auto songDownloadEndpoint() noexcept { return "api/v1/song/data/"; }
};
}
#endif //MEAR_SONGREPOSITORY_H
@@ -0,0 +1,81 @@
//
// Created by brahmix on 10/6/19.
//
#ifndef MEAR_TOKENREPOSITORY_H
#define MEAR_TOKENREPOSITORY_H
#include <string>
#include "../model/Token.h"
#include "BaseRepository.h"
namespace repository { namespace local {
template<class Token = model::Token>
class TokenRepository: public BaseRepository {
public:
TokenRepository() {
m_tableName = tokenTable();
}
Token retrieveToken(const std::string& path) {
Token token;
try {
auto db = getDbConn(path, ConnType::ReadOnly);
std::string queryString("SELECT * FROM ");
queryString.append(m_tableName);
queryString.append(" LIMIT 1");
SQLite::Statement query(db, queryString);
auto result = query.executeStep();
token.accessToken = std::move(query.getColumn(1).getString());
return token;
} catch (std::exception& ex) {
auto msg = ex.what();
}
return token;
}
void createTokenTable(const std::string& path) {
try {
auto db = getDbConn(path, ConnType::ReadWrite);
std::string queryString("CREATE TABLE ");
queryString.append(m_tableName);
queryString.append(" (Id INTEGER PRIMARY KEY, AccessToken TEXT)");
db.exec(queryString);
} catch (std::exception& ex) {
auto msg = ex.what();
}
}
void saveToken(const Token& token, const std::string& path) {
try {
auto db = getDbConn(path, ConnType::ReadWrite);
std::string queryString("INSERT INTO ");
queryString.append(m_tableName);
queryString.append(" (AccessToken) VALUES (?)");
SQLite::Statement query(db, queryString);
query.bind(1, token.accessToken);
query.exec();
} catch (std::exception& ex) {
auto msg = ex.what();
}
}
private:
std::string tokenTable() noexcept { return "Token"; }
};
}}
#endif //MEAR_TOKENREPOSITORY_H
@@ -0,0 +1,104 @@
//
// Created by brahmix on 10/6/19.
//
#ifndef MEAR_USERREPOSITORY_H
#define MEAR_USERREPOSITORY_H
#include <string>
#include "BaseRepository.h"
#include "../model/User.h"
namespace repository { namespace local {
template<class User = model::User>
class UserRepository : public BaseRepository {
public:
UserRepository() {
m_tableName = userTable();
}
User retrieveUserCredentials(const std::string& appPath) {
User user;
try {
const auto dbPath = pathOfDatabase(appPath);
SQLite::Database db(dbPath, SQLite::OPEN_READONLY);
std::string queryString("SELECT * FROM ");
queryString.append(m_tableName);
queryString.append(" LIMIT 1");
SQLite::Statement query(db, queryString);
auto result = query.executeStep();
auto valZero = query.getColumn(1);
auto valOne = query.getColumn(2);
user.username = valZero.getString();
user.password = valOne.getString();
return user;
} catch (std::exception& ex) {
auto msg = ex.what();
}
return user;
}
void createUserTable(const std::string& appPath) {
try {
const auto dbPath = pathOfDatabase(appPath);
SQLite::Database db(dbPath, SQLite::OPEN_READWRITE);
std::string queryString("CREATE TABLE ");
queryString.append(m_tableName);
queryString.append(" (Id INTEGER PRIMARY KEY, Username TEXT, Password TEXT)");
db.exec(queryString);
} catch (std::exception& ex) {
auto msg = ex.what();
}
}
void deleteUserTable(const std::string& appPath) {
try {
const auto dbPath = pathOfDatabase(appPath);
SQLite::Database db(dbPath, SQLite::OPEN_READWRITE);
std::string queryStr("DELETE FROM ");
queryStr.append(m_tableName);
db.exec(queryStr);
} catch (std::exception& ex) {
auto msg = ex.what();
}
}
void saveUserCred(const User& user, const std::string& appPath) {
try {
const auto dbPath = pathOfDatabase(appPath);
SQLite::Database db(dbPath, SQLite::OPEN_READWRITE);
std::string queryString("INSERT INTO ");
queryString.append(m_tableName);
queryString.append(" (Username, Password) VALUES (?, ?)");
SQLite::Statement query(db, queryString);
query.bind(1, user.username);
query.bind(2, user.password);
query.exec();
} catch (std::exception& ex) {
auto msg = ex.what();
}
}
private:
std::string userTable() noexcept { return "User"; }
};
}}
#endif //MEAR_USERREPOSITORY_H