From d199b482ad396410a27823a0506db629f29d268e Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Mon, 25 Nov 2019 21:03:03 -0500 Subject: [PATCH] Adding downloading functionality, left TODO's on where to pick up from --- app/src/main/cpp/CMakeLists.txt | 3 +- app/src/main/cpp/CoverArtRepository.h | 148 +++++++--------- app/src/main/cpp/Demo.cpp | 25 ++- app/src/main/cpp/SongRepository.cpp | 102 +---------- app/src/main/cpp/SongRepository.h | 161 +++++++++++++++++- app/src/main/cpp/model/Song.h | 23 ++- .../example/mear/activities/MainActivity.kt | 11 ++ .../mear/repositories/TrackRepository.kt | 76 +-------- app/src/main/res/menu/popup_menu.xml | 3 + app/src/main/res/values/strings.xml | 1 + 10 files changed, 278 insertions(+), 275 deletions(-) diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index 875bdd1..c72d3f3 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -21,11 +21,9 @@ 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 - SongRepository.cpp Tok.cpp TokenRepository.cpp UserRepository.cpp @@ -49,6 +47,7 @@ set (HEADERS Tok.h TokenRepository.h UserRepository.h + GeneralUtility.h ) set(JSON_BuildTests OFF CACHE INTERNAL "") diff --git a/app/src/main/cpp/CoverArtRepository.h b/app/src/main/cpp/CoverArtRepository.h index a45c7c5..f633a9e 100644 --- a/app/src/main/cpp/CoverArtRepository.h +++ b/app/src/main/cpp/CoverArtRepository.h @@ -21,94 +21,76 @@ namespace repository { template class CoverArtRepository { public: - std::vector retrieveCoverArtRecords(const model::Token&, const std::string&); - std::vector retrieveCoverArtData(const model::Token&, const C&, const std::string&); + // TODO: implement this + std::vector retrieveCoverArtRecords(const model::Token& token, const std::string& uri) { + std::vector vals; + + return vals; + } + + std::vector 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(); + } + + 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 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: - constexpr auto downloadEndpoint() noexcept; - constexpr auto recordsEndpoint() noexcept; + constexpr auto downloadEndpoint() 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 - std::vector CoverArtRepository::retrieveCoverArtRecords(const model::Token& token, - const std::string& uri) { - std::vector vals; - - return vals; - } - - template - std::vector CoverArtRepository::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(); - } - - 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 vals(data.begin(), data.end()); - - return vals; - } - - template - C CoverArtRepository::retrieveCoverArtRecord(const model::Token& token, - const C& cover, - const std::string& uri) { - model::CoverArt cov; - - return cov; - } - - - template - constexpr auto CoverArtRepository::downloadEndpoint() noexcept { - return "api/v1/coverart/download/"; - } - template - constexpr auto CoverArtRepository::recordsEndpoint() noexcept { - return "api/v1/coverart/"; - } - - template - size_t CoverArtRepository::respBodyRetriever(void *ptr, size_t size, size_t nmemb, char *e) { - ((std::string*)e)->append((char*)ptr, size * nmemb); - - return size * nmemb; - } } diff --git a/app/src/main/cpp/Demo.cpp b/app/src/main/cpp/Demo.cpp index bf03509..402eb0d 100644 --- a/app/src/main/cpp/Demo.cpp +++ b/app/src/main/cpp/Demo.cpp @@ -388,6 +388,24 @@ Java_com_example_mear_repositories_TrackRepository_retrieveSong( 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" JNIEXPORT jobject JNICALL @@ -478,14 +496,7 @@ Java_com_example_mear_repositories_CoverArtRepository_retrieveCoverArtImage( 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(data.data()), data.size()); - //out.close(); - return image; } diff --git a/app/src/main/cpp/SongRepository.cpp b/app/src/main/cpp/SongRepository.cpp index 30e6c79..149bc72 100644 --- a/app/src/main/cpp/SongRepository.cpp +++ b/app/src/main/cpp/SongRepository.cpp @@ -8,116 +8,26 @@ #include namespace repository { + /** std::vector SongRepository::fetchSongs(const model::Token& token, const std::string& uri) { - std::string fullUri(uri); - if (fullUri.at(fullUri.size()-1) != '/') { - fullUri.append("/"); - } - - fullUri.append(songRecordEndpoint()); - std::vector 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(), songJson["title"].get(), - songJson["artist"].get(), songJson["album"].get(), - songJson["genre"].get(), songJson["duration"].get(), - songJson["year"].get()); - song.albumArtist = songJson["album_artist"].get(); - song.coverArtId = songJson["coverart_id"].get(); - - songs.push_back(song); - } - } - - return songs; } + */ + /** model::Song SongRepository::retrieveSong(const model::Token& token, const model::Song& sng, 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(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(); - song.title = s["title"].get(); - song.album = s["album"].get(); - song.albumArtist = s["album_artist"].get(); - song.artist = s["artist"].get(); - song.genre = s["genre"].get(); - song.duration = s["duration"].get(); - song.year = s["year"].get(); - song.coverArtId = s["coverart_id"].get(); - - return song; - } - - return sng; } + */ + /** size_t SongRepository::respBodyRetriever(void* ptr, size_t size, size_t nmemb, char *e) { ((std::string*)e)->append((char*)ptr, size * nmemb); return size * nmemb; } + */ } diff --git a/app/src/main/cpp/SongRepository.h b/app/src/main/cpp/SongRepository.h index 45f0016..eacc1da 100644 --- a/app/src/main/cpp/SongRepository.h +++ b/app/src/main/cpp/SongRepository.h @@ -8,25 +8,170 @@ #include #include +#include +#include + #include "model/Song.h" #include "model/Token.h" +#include "GeneralUtility.h" namespace repository { + template class SongRepository { public: - std::vector fetchSongs(const model::Token&, const std::string&); + template + std::vector fetchSongs(const Token& token, const std::string& uri) { + auto fullUri = utility::GeneralUtility::appendForwardSlashToUri(uri); + fullUri.append(songRecordEndpoint()); + std::vector 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(), songJson["title"].get(), + songJson["artist"].get(), + songJson["album"].get(), + songJson["album_artist"].get(), + songJson["genre"].get(), songJson["duration"].get(), + songJson["year"].get(), songJson["coverart_id"].get()); + /** + song.albumArtist = songJson["album_artist"].get(); + song.coverArtId = songJson["coverart_id"].get(); + */ + + songs.push_back(song); + } + } + + return songs; + } + + template + 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(), s["title"].get(), + s["artist"].get(), s["album"].get(), + s["album_artist"].get(), s["genre"].get(), + s["duration"].get(), s["year"].get(), + s["coverart_id"].get()); + /** + song.id = s["id"].get(); + song.title = s["title"].get(); + song.album = s["album"].get(); + song.albumArtist = s["album_artist"].get(); + song.artist = s["artist"].get(); + song.genre = s["genre"].get(); + song.duration = s["duration"].get(); + song.year = s["year"].get(); + song.coverArtId = s["coverart_id"].get(); + */ + + return song; + } + + return sng; + } + + template + 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(data.begin(), data.end())); + + return downloadedSong; + } 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/"; - } } diff --git a/app/src/main/cpp/model/Song.h b/app/src/main/cpp/model/Song.h index c288937..f17e58a 100644 --- a/app/src/main/cpp/model/Song.h +++ b/app/src/main/cpp/model/Song.h @@ -6,6 +6,7 @@ #define MEAR_SONG_H #include +#include namespace model { @@ -20,22 +21,30 @@ public: const int duration, const int year) : id(id), title(title), artist(artist), album(album), genre(genre), duration(duration), 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) { } + Song(const int id, const std::string& title, const std::string& artist, + const std::string& album, const std::string& albumArtist, const std::string& genre, + const int duration, const int year, const int coverArtId) : + id(id), title(title), artist(artist), album(album), albumArtist(albumArtist), + genre(genre), duration(duration), year(year), coverArtId(coverArtId) { } + /** +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; std::string title; std::string artist; - std::string albumArtist; std::string album; + std::string albumArtist; std::string genre; int duration; int year; int coverArtId; + std::vector data; }; } diff --git a/app/src/main/java/com/example/mear/activities/MainActivity.kt b/app/src/main/java/com/example/mear/activities/MainActivity.kt index 5eac42b..e8720cb 100644 --- a/app/src/main/java/com/example/mear/activities/MainActivity.kt +++ b/app/src/main/java/com/example/mear/activities/MainActivity.kt @@ -368,6 +368,17 @@ class MainActivity : BaseServiceActivity() { R.id.action_song_view -> { 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-> { val trk = musicService!!.getCurrentSong() val pc = PlayCountRepository(this).getPlayCount(trk.id) diff --git a/app/src/main/java/com/example/mear/repositories/TrackRepository.kt b/app/src/main/java/com/example/mear/repositories/TrackRepository.kt index 69c93be..a5f11dc 100644 --- a/app/src/main/java/com/example/mear/repositories/TrackRepository.kt +++ b/app/src/main/java/com/example/mear/repositories/TrackRepository.kt @@ -21,6 +21,7 @@ class TrackRepository(var context: Context? = null) { private external fun retrieveSongs(token: Token, uri: String): Array 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 { @@ -32,55 +33,12 @@ class TrackRepository(var context: Context? = null) { return retrieveSong(token, song, uri) } - fun getAll(): List = context!!.database.use { - val tracks = mutableListOf() + fun fetchSongFile(token: Token, song: Song, uri: String): Song { + val downloadedSong = downloadSong(token, song, uri) - select("Track" ) - .parseList(object : MapRowParser{ - override fun parseRow(columns: Map): 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 + return downloadedSong } - fun getTrack(id: Int): Track = context!!.database.use { - select("Track").where("Id = $id") - .parseSingle(object: MapRowParser{ - override fun parseRow(columns: Map): 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{ - override fun parseRow(columns: Map): Int { - - return columns.getValue("TotalSongs").toString().toInt() - } - }) - } fun insertTracks(tracks: List) = context!!.database.use { transaction { @@ -111,30 +69,4 @@ class TrackRepository(var context: Context? = null) { fun delete() = context!!.database.use { delete("Track") } - fun deleteTracks(tracks: List) = context!!.database.use { - tracks.iterator().forEach { - transaction { - delete("Track", "id = {track_id}", "track_id" to it.id) - } - } - } - fun deleteTracksByPaths(trackPaths: List) = 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 { - override fun parseRow(columns: Map): Int { - return columns.getValue("TotalSongs").toString().toInt() - } - }) - } - return 0 - } } \ No newline at end of file diff --git a/app/src/main/res/menu/popup_menu.xml b/app/src/main/res/menu/popup_menu.xml index 643c0ad..987cf37 100644 --- a/app/src/main/res/menu/popup_menu.xml +++ b/app/src/main/res/menu/popup_menu.xml @@ -7,6 +7,9 @@ + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bb4b28a..e7f7bee 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -37,6 +37,7 @@ Settings Songs Play Count + Download About