diff --git a/app/src/main/cpp/Demo.hpp b/app/src/main/cpp/Demo.hpp index 774bf15..8b50611 100644 --- a/app/src/main/cpp/Demo.hpp +++ b/app/src/main/cpp/Demo.hpp @@ -265,7 +265,7 @@ void updateShuffleMode(const std::string& path) { template void downloadSong(Song& song, const Token& token, const Str& path) { - song.filename = song.title; + song.filename = "track"; song.path = path; repository::local::APIRepository apiRepo; auto apiInfo = apiRepo.retrieveAPIInfo(path); @@ -273,16 +273,26 @@ void downloadSong(Song& song, const Token& token, const Str& path) { auto downloadedSong = songRepo.downloadSong(token, song, apiInfo); manager::DirectoryManager dirMgr; if (dirMgr.doesSongExist(song, path)) { - auto a = 0; std::cout << "song already exists\n"; return; } dirMgr.createSongDirectory(song, path); - const auto fullSongPath = dirMgr.fullSongPath(song, path); - std::fstream saveSong(fullSongPath, std::ios::out | std::ios::binary); + downloadedSong.path = dirMgr.fullSongPath(song, path); + std::fstream saveSong(downloadedSong.path, std::ios::out | std::ios::binary); saveSong.write((char*)&downloadedSong.data[0], downloadedSong.data.size()); saveSong.close(); + + repository::local::SongRepository localSongRepo; + if (!localSongRepo.databaseExist(path)) { + localSongRepo.initializedDatabase(path); + } + + if (!localSongRepo.doesTableExist(path)) { + localSongRepo.createSongTable(path); + } + + localSongRepo.saveSong(downloadedSong, path); } @@ -367,6 +377,43 @@ Java_com_example_mear_repositories_TrackRepository_retrieveSongs( return songs; } +extern "C" +JNIEXPORT jobjectArray +JNICALL +Java_com_example_mear_repositories_TrackRepository_retrieveSongsIncludingDownloaded( + JNIEnv *env, + jobject thisOnj, + jobject token, + jstring baseUri, + jstring appPathStr +) { + jclass tokenClass = env->FindClass("com/example/mear/models/Token"); + jclass songClass = env->FindClass( "com/example/mear/models/Song"); + + auto tokField = env->GetFieldID(tokenClass, "accessToken", "Ljava/lang/String;"); + auto tokObj = env->GetObjectField(token, tokField); + std::string tokStr = env->GetStringUTFChars((jstring)tokObj, nullptr); + std::string appPath = env->GetStringUTFChars((jstring)appPathStr, nullptr); + model::Token tk(tokStr); + + const std::string uri = env->GetStringUTFChars(baseUri, nullptr); + env->DeleteLocalRef(baseUri); + + auto allSongs = retrieveSongs(tk, uri); + jobjectArray songs = env->NewObjectArray(allSongs.size(), songClass, nullptr); + auto i = 0; + for (auto& sng: allSongs) { + try { + auto song = songToObj(env, sng); + env->SetObjectArrayElement(songs, i++, song); + } catch (std::exception& ex) { + auto msg = ex.what(); + } + } + + return songs; +} + extern "C" JNIEXPORT jbyteArray diff --git a/app/src/main/cpp/manager/DirectoryManager.h b/app/src/main/cpp/manager/DirectoryManager.h index 8331cb9..716b9af 100644 --- a/app/src/main/cpp/manager/DirectoryManager.h +++ b/app/src/main/cpp/manager/DirectoryManager.h @@ -27,7 +27,23 @@ namespace manager { s.append("/"); s.append(song.album); s.append("/"); - s.append(song.filename); + + if (song.disc == 0) { + s.append("disc1"); + } else { + s.append("disc"); + s.append(std::to_string(song.disc)); + } + + s.append("/"); + s.append("track"); + if (song.track < 10) { + s.append("0"); + s.append(std::to_string(song.track)); + } else { + s.append(std::to_string(song.track)); + } + s.append(".mp3"); return s; @@ -45,6 +61,19 @@ namespace manager { return ss.c_str(); } + template + Str albumDiscPath(const Song& song, const Str& path) { + std::string songPath = albumPath(song, path); + songPath.append("/disc"); + if (song.disc == 0) { + songPath.append("1"); + } else { + songPath.append(std::to_string(song.disc)); + } + + return songPath.c_str(); + } + template Str artistPath(const Song& song, const Str& path) { Str s = utility::GeneralUtility::appendForwardSlashToUri(path); @@ -56,9 +85,7 @@ namespace manager { template B doesSongExist(const Song& song, const Str& path) { - std::string s = albumPath(song, path); - s.append("/"); - s.append(song.filename); + std::string s = fullSongPath(song, path); struct stat buffer; return (stat (s.c_str(), &buffer) == 0); @@ -85,6 +112,9 @@ namespace manager { if (!albumDirectoryExists(song, path)) { auto status = mkdir(albumPath(song, path), 0777); } + if (!albumDiscDirectoryExists(song, path)) { + auto status = mkdir(albumDiscPath(song, path), 0777); + } } template @@ -98,6 +128,16 @@ namespace manager { albumPath.append(song.albumArtist); albumPath.append("/"); albumPath.append(song.album); + /** + if (song.disc == 0) { + albumPath.append("/"); + albumPath.append("disc1"); + } else { + albumPath.append("/"); + albumPath.append("disc"); + albumPath.append(std::to_string(song.disc)); + } + */ DIR* dir = opendir(albumPath.c_str()); if (dir) { @@ -110,6 +150,28 @@ namespace manager { return true; } + template + B albumDiscDirectoryExists(const Song& song, const Str& path) { + std::string albumDistPath = albumPath(song, path); + albumDistPath.append("/disc"); + if (song.disc == 0) { + albumDistPath.append("1"); + } else { + albumDistPath.append(std::to_string(song.disc)); + } + + DIR *dir = opendir(albumDistPath.c_str()); + + if (dir) { + closedir(dir); + return true; + } else if (ENOENT == errno) { + return false; + } + + return true; + } + template B artistDirectoryExists(const Song song, const Str& path) { auto artistPath = utility::GeneralUtility::appendForwardSlashToUri(path); diff --git a/app/src/main/cpp/repository/SongRepository.h b/app/src/main/cpp/repository/SongRepository.h index 1786370..7b9ffa2 100644 --- a/app/src/main/cpp/repository/SongRepository.h +++ b/app/src/main/cpp/repository/SongRepository.h @@ -128,11 +128,11 @@ namespace repository { } template - Song downloadSong(const Token &token, const Song song, const API& uri) { + Song downloadSong(const Token &token, const Song& song, const API& uri) { auto fullUri = utility::GeneralUtility::appendForwardSlashToUri(uri.uri); fullUri.append(songDownloadEndpoint()); fullUri.append(std::to_string(song.id)); - Song downloadedSong; + Song downloadedSong = song; CURL *curl = curl_easy_init(); struct curl_slist *chunk = nullptr; @@ -151,6 +151,7 @@ namespace repository { auto res = curl_easy_perform(curl); curl_easy_cleanup(curl); downloadedSong.data = std::move(std::vector(data.begin(), data.end())); + /** downloadedSong.filename = "track"; if (song.track < 10) { downloadedSong.filename.append("0"); @@ -159,6 +160,7 @@ namespace repository { downloadedSong.filename.append(std::to_string(song.track)); } downloadedSong.filename.append(".mp3"); + */ return downloadedSong; } @@ -186,7 +188,33 @@ namespace repository { std::vector downloadedSongs; try { // TODO: left off here + const auto dbPath = pathOfDatabase(appPath); + SQLite::Database db(dbPath, SQLite::OPEN_READONLY); + std::string queryString("SELECT * FROM "); + queryString.append(m_tableName); + + SQLite::Statement query(db, queryString); + + while (query.executeStep()) { + Song song; + song.id = query.getColumn(0).getInt(); + song.title = query.getColumn(1).getString(); + song.artist = query.getColumn(2).getString(); + song.album = query.getColumn(3).getString(); + song.albumArtist = query.getColumn(4).getString(); + song.genre = query.getColumn(5).getString(); + song.year = query.getColumn(6).getInt(); + song.duration = query.getColumn(7).getInt(); + song.track = query.getColumn(8).getInt(); + song.disc = query.getColumn(9).getInt(); + song.filename = query.getColumn(10).getString(); + song.path = query.getColumn(11).getString(); + song.coverArtId = query.getColumn(12).getInt(); + song.downloaded = true; + + downloadedSongs.push_back(song); + } } catch (std::exception& ex) { auto msg = ex.what(); } diff --git a/app/src/main/java/com/example/mear/activities/SettingsActivity.kt b/app/src/main/java/com/example/mear/activities/SettingsActivity.kt index 328f870..c0e6284 100644 --- a/app/src/main/java/com/example/mear/activities/SettingsActivity.kt +++ b/app/src/main/java/com/example/mear/activities/SettingsActivity.kt @@ -16,7 +16,6 @@ import kotlinx.android.synthetic.main.content_settings.* import com.example.mear.adapters.SettingsAdapter import com.example.mear.R import com.example.mear.management.MusicFiles -import com.example.mear.management.TrackManager import com.example.mear.ui.popups.AboutPopup import com.example.mear.repositories.TrackRepository import kotlinx.android.synthetic.main.popup_layout.* @@ -56,23 +55,9 @@ class SettingsActivity : AppCompatActivity() { return allSongs!! } - private fun loadTracks(allSongs: MutableList) { - try { - TrackRepository(this).delete() - val trackMgr = TrackManager(allSongs) - trackMgr.configureTracks(this) - } - catch (ex: Exception) { - val exMsg = ex.message - } - } - private fun updateLibrary() { - loadTracks(loadSongPaths()) - } - class AboutListener(var layout: LinearLayout?, val ctx: Context): View.OnClickListener { override fun onClick(v: View?) { val ss = 10 diff --git a/app/src/main/java/com/example/mear/management/TrackManager.kt b/app/src/main/java/com/example/mear/management/TrackManager.kt deleted file mode 100644 index 9ed1fda..0000000 --- a/app/src/main/java/com/example/mear/management/TrackManager.kt +++ /dev/null @@ -1,314 +0,0 @@ -package com.example.mear.management - -import android.content.Context -import android.media.MediaMetadataRetriever -import android.widget.Toast - -import java.io.FileInputStream -import kotlinx.coroutines.* - -import com.example.mear.constants.Filenames -import com.example.mear.constants.SongSearch -import com.example.mear.models.Track -import com.example.mear.repositories.PlayCountRepository -import com.example.mear.repositories.TrackRepository -import com.example.mear.util.ConvertByteArray -import org.jetbrains.anko.custom.async -import kotlin.Exception - -class TrackManager(var allSongPath: MutableList?) { - - private var allTracks: MutableList? = null - private var ctx: Context? = null - private var songCount: Int? = null - private var songIndex: Int? = null - private var moreSongs = true - var allTracksAdded = false - - fun initializeContext(context: Context) { - ctx = context - } - - fun configureTracks(ctx: Context): Int { - var id = 0 - try { - allTracks = mutableListOf() - for (musicPath in allSongPath!!) { - var mmr = MediaMetadataRetriever() - mmr.setDataSource(musicPath) - val trackTitle = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE) - val trackArtist = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST) - val trackAlbum = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM) - var trackLength: Int? - val trackLenghStr = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION) - trackLength = (trackLenghStr.toInt()/1000) - - if (trackTitle == null || trackArtist == null || trackAlbum == null || - trackLenghStr == null) { - } - - val track = Track(id, trackTitle, trackArtist, trackAlbum, trackLength, - ByteArray(0), musicPath) - dumpToDatabase(ctx, track) - if (mmr.embeddedPicture!=null) { - saveTrackCoverToDisk(ctx, id, mmr.embeddedPicture) - } - id++ - } - } - catch (ex: Exception) { - val exMsg = ex.message - } - TrackRepository(ctx).createSongCount(id) - return id.dec() - } - fun initializeLibrary() { - var initTrack = initialTracks() - addToDatabase(initTrack) - } - fun addSongs() { - var initTracks = initialTracks() - addToDatabase(initTracks) - - if (moreSongs) { - GlobalScope.launch { - val allSongCount = allSongPath!!.size -1 - for (i in songIndex!! .. (allSongCount)) { - val songPath = allSongPath!![i] - val track = configureTrack(songPath, i) - songCount = songCount!!.inc() - addToDatabase(track) - } - } - } - } - fun addnewSongs(trackPaths: List, resumeIndex: Int) { - var resumeIndex = resumeIndex - - try { - //launchB - for (tPath in trackPaths) { - val track = configureTrack(tPath, resumeIndex) - addToDatabase(track) - resumeIndex = resumeIndex.inc() - } - //} - } - catch (ex: Exception) { - val exMsg = ex.message - } - } - fun addTracks() { - var initTracks = initialTracks() - addToDatabase(initTracks) - - if (moreSongs) { - GlobalScope.launch { - val remainingTracks = remainingTracks() - addToDatabase(remainingTracks) - allTracksAdded = true - } - } - } - fun addTracksKx() { - var initTracks = initialTracks() - addToDatabase(initTracks) - - if (moreSongs) { - GlobalScope.launch { - addRemainingTracks() - } - } - } - fun addRemainingTracks() = runBlocking { - val remainder = allSongPath!!.size - songCount!! - - if (remainder > 10) { - val rangeAmount = remainder / 10 - val rg = addRanges(rangeAmount) - - val remainingT = remainingTracks(rg) - addToDatabase(remainingT) - } - } - - private fun addRanges(rangeAmount: Int): MutableList { - var ranges = mutableListOf() - try { - var rangeCounter = 0 - val songAmount = allSongPath!!.size - while (songIndex!! < songAmount) { - val startIndex = songIndex - var endIndex = songIndex!! + rangeAmount - if ((songIndex!! + rangeAmount) > songAmount ) { - endIndex = (songAmount - songIndex!!) + songIndex!! - } - - val range = AddMusicRange(startIndex!!, endIndex) - ranges.add(range) - - songIndex = endIndex + 1 - } - } - catch (ex: Exception) { - val exMsg = ex.message - } - - return ranges - } - private fun initialTracks(): MutableList { - var tracks = mutableListOf() - - try { - var initSongSearch = SongSearch.INITIAL_SEARCH_AMOUNT -1 - - if (allSongPath!!!!.size < SongSearch.INITIAL_SEARCH_AMOUNT) { - initSongSearch = allSongPath!!.size - moreSongs = false - } - - for (i in 0.. initSongSearch) { - var songPath = allSongPath!![i] - val track = configureTrack(songPath, i) - tracks.add(track) - } - - songCount = tracks.size - songIndex = songCount!! - } - catch (ex: Exception) { - val exMsg = ex.message - } - - return tracks - } - private fun remainingTracks(): MutableList { - val tracks = mutableListOf() - try { - for (i in songIndex!! .. (allSongPath!!.size - 1)) { - val songPath = allSongPath!![i] - val track = configureTrack(songPath, i) - if (track.id != Int.MAX_VALUE) { - tracks.add(track) - } - } - - songCount = songCount!!.plus(tracks.size) - } - catch (ex: Exception) { - val exMsg = ex.message - } - - return tracks - } - private fun remainingTracks(rangeobjects: AddMusicRange): MutableList { - val tracks = mutableListOf() - try { - val startIndex = rangeobjects.startIndex - var endIndex = rangeobjects.endIndex - if (endIndex == allSongPath!!.size) { - endIndex-- - } - - for (songPathIndex in startIndex.. endIndex) { - val songPath = allSongPath!![songPathIndex] - val track = configureTrack(songPath, songPathIndex) - if (track.id != Int.MAX_VALUE) { - tracks.add(track) - } - } - - songCount = songCount!!.plus(tracks.size) - } - catch (ex: Exception) { - val exMsg = ex.message - } - - return tracks - } - suspend private fun remainingTracks(obj: MutableList): MutableList { - val tracks = mutableListOf() - var rangeRecords = mutableListOf() - try { - for (rgObj in obj) { - coroutineScope { - launch { - val remainingTracks = remainingTracks(rgObj) - tracks.addAll(remainingTracks) - rangeRecords.add(true) - } - } - } - runBlocking { - while (rangeRecords.size != obj.size) { - delay(10) - } - } - - } - catch (ex: java.lang.Exception) { - val exMsg = ex.message - } - - return tracks - } - - private fun configureTrack(songPath: String, id: Int): Track { - try { - val metaData = MediaMetadataRetriever() - val fp = FileInputStream(songPath) - metaData.setDataSource(fp.fd) - - val title = metaData.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE) - val artist = metaData.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST) - val album = metaData.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM) - val duration = (metaData.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION).toInt()) / 1000 - - fp.close() - metaData.release() - - return Track(id, title, artist, album, duration, ByteArray(0), songPath) - } - catch (ex: Exception) { - val exMsg = ex.message - } - - return Track() - } - - private fun addToDatabase(tracks: List) { - TrackRepository(ctx!!).insertTracks(tracks) - PlayCountRepository(ctx!!).insertPlayCounts(tracks) - TrackRepository(ctx!!).createSongCount(songCount!!) - } - private fun addToDatabase(track: Track) { - try { - TrackRepository(ctx!!).insertTrack(track) - PlayCountRepository(ctx!!).insertPlayCount(track) - //TrackRepository(ctx!!).createSongCount(songCount!!) - } - catch (ex: Exception) { - val exMsg = ex.message - } - } - private fun addToDatabase(ctx: Context, tracks: List) { - TrackRepository(ctx).insertTracks(tracks) - PlayCountRepository(ctx).insertPlayCounts(tracks) - } - private fun dumpToDatabase(ctx: Context, track: Track) { - TrackRepository(ctx).insertTrack(track) - PlayCountRepository(ctx).insertPlayCount(track) - } - private fun saveTrackCoverToDisk(context: Context, id: Int, trackCover: ByteArray) { - val filename = "${Filenames.TRACK_COVERS}$id.bmp" - val fileContents = trackCover - var img = ConvertByteArray(trackCover).convertToBmp() - - context.openFileOutput(filename, Context.MODE_PRIVATE).use { - it.write(fileContents) - } - } - - data class AddMusicRange(val startIndex: Int, val endIndex: Int) { - } -} \ No newline at end of file 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 bdea87e..73ceebe 100644 --- a/app/src/main/java/com/example/mear/repositories/TrackRepository.kt +++ b/app/src/main/java/com/example/mear/repositories/TrackRepository.kt @@ -19,6 +19,7 @@ class TrackRepository(var context: Context? = null) { private external fun retrieveSongs(token: Token, uri: String): Array + private external fun retrieveSongsIncludingDownloaded(token: Token, uri: String, path: String): Array private external fun retrieveSong(token: Token, song: Song, uri: String): Song @@ -29,6 +30,10 @@ class TrackRepository(var context: Context? = null) { return retrieveSongs(token, uri) } + fun fetchSongsIncludingDownloaded(token: Token, uri: String, path: String): Array { + return retrieveSongsIncludingDownloaded(token, uri, path) + } + fun fetchSong(token: Token, song: Song, uri: String): Song { return retrieveSong(token, song, uri) @@ -42,35 +47,4 @@ class TrackRepository(var context: Context? = null) { downloadSong(token, song, path) val s = 4 } - - - fun insertTracks(tracks: List) = context!!.database.use { - transaction { - for (track in tracks) { - insert("Track", - "Id" to track.id, "Title" to track.title, "Album" to track.album, - "Artist" to track.artist, "Duration" to track.length, - "FilePath" to track.songPath) - } - } - } - - fun insertTrack(track: Track) = context!!.database.use { - insert("Track", - "Id" to track.id, - "Title" to track.title, - "Album" to track.album, - "Artist" to track.artist, - "Duration" to track.length, - "FilePath" to track.songPath) - } - fun createSongCount(songCount: Int) = context!!.database.use { - delete("TrackCount") - insert("TrackCount", - "Id" to 0, - "TotalSongs" to songCount) - } - fun delete() = context!!.database.use { - delete("Track") - } } \ No newline at end of file