diff --git a/android/src/main/res/values/res.xml b/android/src/main/res/values/res.xml deleted file mode 100644 index 81dab6b7..00000000 --- a/android/src/main/res/values/res.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - @color/colorPrimary - @android:color/white - @android:color/white - @android:color/black - @color/colorPrimary - @color/colorAccent - - - @color/colorPrimary - @android:color/white - @android:color/black - @android:color/white - @color/chevronBgColor - #da6c6c - #da6c6c - #da6c6c - @color/colorPrimary - - - - - @color/colorPrimary - @android:color/white - @android:color/black - @android:color/white - #da6c6c - @color/colorPrimary - - - @color/colorPrimary - @android:color/black - @android:color/white - @android:color/white - @color/grey - #da6c6c - #da6c6c - #da6c6c - #da6c6c - - \ No newline at end of file diff --git a/common/data-models/build.gradle.kts b/common/data-models/build.gradle.kts index 178552a7..967da38e 100644 --- a/common/data-models/build.gradle.kts +++ b/common/data-models/build.gradle.kts @@ -45,5 +45,11 @@ kotlin { implementation("co.touchlab:stately-iso-collections:$statelyIsoVersion") } } + androidMain { + dependencies { + api("com.github.K1rakishou:Fuck-Storage-Access-Framework:v1.1") + api("androidx.documentfile:documentfile:1.0.1") + } + } } } diff --git a/common/data-models/src/androidMain/kotlin/com.shabinder.common.models/AndroidMediaFile.kt b/common/data-models/src/androidMain/kotlin/com.shabinder.common.models/AndroidMediaFile.kt new file mode 100644 index 00000000..e0752650 --- /dev/null +++ b/common/data-models/src/androidMain/kotlin/com.shabinder.common.models/AndroidMediaFile.kt @@ -0,0 +1,8 @@ +package com.shabinder.common.models + +import com.github.k1rakishou.fsaf.file.AbstractFile + +// Use Storage Access Framework `SAF` +actual data class File( + val documentFile: AbstractFile? +) \ No newline at end of file diff --git a/common/data-models/src/androidMain/kotlin/com.shabinder.common.models/AndroidPlatformActions.kt b/common/data-models/src/androidMain/kotlin/com.shabinder.common.models/AndroidPlatformActions.kt index 21131944..46732ac7 100644 --- a/common/data-models/src/androidMain/kotlin/com.shabinder.common.models/AndroidPlatformActions.kt +++ b/common/data-models/src/androidMain/kotlin/com.shabinder.common.models/AndroidPlatformActions.kt @@ -8,7 +8,7 @@ actual interface PlatformActions { const val SharedPreferencesKey = "configurations" } - val imageCacheDir: String + val imageCacheDir: java.io.File val sharedPreferences: SharedPreferences? @@ -18,7 +18,7 @@ actual interface PlatformActions { } actual val StubPlatformActions = object: PlatformActions { - override val imageCacheDir: String = "" + override val imageCacheDir = java.io.File("/") override val sharedPreferences: SharedPreferences? = null diff --git a/common/data-models/src/androidMain/kotlin/com.shabinder.common.models/SpotiFlyerBaseDir.kt b/common/data-models/src/androidMain/kotlin/com.shabinder.common.models/SpotiFlyerBaseDir.kt new file mode 100644 index 00000000..6603b994 --- /dev/null +++ b/common/data-models/src/androidMain/kotlin/com.shabinder.common.models/SpotiFlyerBaseDir.kt @@ -0,0 +1,18 @@ +package com.shabinder.common.models + +import android.net.Uri +import com.github.k1rakishou.fsaf.manager.base_directory.BaseDirectory +import java.io.File + +class SpotiFlyerBaseDir( + private val getDirType: ()-> ActiveBaseDirType, + private val getJavaFile: ()-> File?, + private val getSAFUri: ()-> Uri? +): BaseDirectory() { + + override fun currentActiveBaseDirType(): ActiveBaseDirType = getDirType() + + override fun getDirFile(): File? = getJavaFile() + + override fun getDirUri(): Uri? = getSAFUri() +} \ No newline at end of file diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/DownloadObject.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/DownloadObject.kt index 7d004ed6..080c790d 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/DownloadObject.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/DownloadObject.kt @@ -32,12 +32,12 @@ data class TrackDetails( var comment: String? = null, var lyrics: String? = null, var trackUrl: String? = null, - var albumArtPath: String, + var albumArtPath: String, // UriString in Android var albumArtURL: String, var source: Source, val progress: Int = 2, val downloaded: DownloadStatus = DownloadStatus.NotDownloaded, - var outputFilePath: String, + var outputFilePath: String, // UriString in Android var videoID: String? = null, ) : Parcelable diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/File.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/File.kt new file mode 100644 index 00000000..995a30ed --- /dev/null +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/File.kt @@ -0,0 +1,3 @@ +package com.shabinder.common.models + +expect class File \ No newline at end of file diff --git a/common/data-models/src/desktopMain/kotlin/com/shabinder/common/models/DesktopMediaFile.kt b/common/data-models/src/desktopMain/kotlin/com/shabinder/common/models/DesktopMediaFile.kt new file mode 100644 index 00000000..9fc04540 --- /dev/null +++ b/common/data-models/src/desktopMain/kotlin/com/shabinder/common/models/DesktopMediaFile.kt @@ -0,0 +1,3 @@ +package com.shabinder.common.models + +actual typealias File = java.io.File \ No newline at end of file diff --git a/common/data-models/src/iosMain/kotlin/com.shabinder.common.models/IOSMediaFile.kt b/common/data-models/src/iosMain/kotlin/com.shabinder.common.models/IOSMediaFile.kt new file mode 100644 index 00000000..3b6f93f3 --- /dev/null +++ b/common/data-models/src/iosMain/kotlin/com.shabinder.common.models/IOSMediaFile.kt @@ -0,0 +1,5 @@ +package com.shabinder.common.models + +actual data class File( + val path: String +) \ No newline at end of file diff --git a/common/data-models/src/jsMain/kotlin/com.shabinder.common.models/WebMediaFile.kt b/common/data-models/src/jsMain/kotlin/com.shabinder.common.models/WebMediaFile.kt new file mode 100644 index 00000000..3b6f93f3 --- /dev/null +++ b/common/data-models/src/jsMain/kotlin/com.shabinder.common.models/WebMediaFile.kt @@ -0,0 +1,5 @@ +package com.shabinder.common.models + +actual data class File( + val path: String +) \ No newline at end of file diff --git a/common/dependency-injection/build.gradle.kts b/common/dependency-injection/build.gradle.kts index d030b2d9..7724fa72 100644 --- a/common/dependency-injection/build.gradle.kts +++ b/common/dependency-injection/build.gradle.kts @@ -41,7 +41,6 @@ kotlin { dependencies { implementation(compose.materialIconsExtended) implementation(Extras.mp3agic) - implementation("com.github.shabinder:storage-chooser:2.0.4.45") // implementation(files("$rootDir/libs/mobile-ffmpeg.aar")) } } diff --git a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AudioTagging.kt b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AudioTagging.kt index 042d26b4..5bb3fbe3 100644 --- a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AudioTagging.kt +++ b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AudioTagging.kt @@ -99,9 +99,8 @@ fun Mp3File.setId3v1Tags(track: TrackDetails): Mp3File { } @Suppress("BlockingMethodInNonBlockingContext") -suspend fun Mp3File.setId3v2TagsAndSaveFile(track: TrackDetails) { +suspend fun Mp3File.setId3v2TagsAndSaveFile(track: TrackDetails,tempMp3Path:String) { val id3v2Tag = ID3v24Tag().apply { - artist = track.artists.joinToString(", ") title = track.title album = track.albumName @@ -118,9 +117,9 @@ suspend fun Mp3File.setId3v2TagsAndSaveFile(track: TrackDetails) { fis.close() id3v2Tag.setAlbumImage(bytesArray, "image/jpeg") this.id3v2Tag = id3v2Tag - saveFile(track.outputFilePath) + saveFile(tempMp3Path) } catch (e: java.io.FileNotFoundException) { - Log.e("Error", "Couldn't Write Cached Mp3 Album Art, error: ${e.stackTrace}") + Log.e("Error", "Couldn't Write Cached Mp3 Album Art, Downloading And Trying Again, error: ${e.message}") try { // Image Still Not Downloaded! // Lets Download Now and Write it into Album Art @@ -130,7 +129,7 @@ suspend fun Mp3File.setId3v2TagsAndSaveFile(track: TrackDetails) { is DownloadResult.Success -> { id3v2Tag.setAlbumImage(it.byteArray, "image/jpeg") this.id3v2Tag = id3v2Tag - saveFile(track.outputFilePath) + saveFile(tempMp3Path) } is DownloadResult.Progress -> {} // Nothing for Now , no progress bar to show } @@ -143,6 +142,7 @@ suspend fun Mp3File.setId3v2TagsAndSaveFile(track: TrackDetails) { } fun Mp3File.saveFile(filePath: String) { + Log.d("Mp3 File Save",filePath) save(filePath.substringBeforeLast('.') + ".new.mp3") val m4aFile = File(filePath) m4aFile.delete() diff --git a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/worker/ForegroundService.kt b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/worker/ForegroundService.kt index 8576851d..78ff1919 100644 --- a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/worker/ForegroundService.kt +++ b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/worker/ForegroundService.kt @@ -18,33 +18,29 @@ package com.shabinder.common.di.worker import android.annotation.SuppressLint import android.app.DownloadManager -import android.app.DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.app.PendingIntent.FLAG_CANCEL_CURRENT import android.app.Service -import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import android.content.IntentFilter -import android.net.Uri import android.os.Build import android.os.IBinder import android.os.PowerManager import android.util.Log import androidx.annotation.RequiresApi import androidx.core.app.NotificationCompat -import androidx.core.net.toUri import co.touchlab.kermit.Kermit import com.shabinder.common.di.* +import com.shabinder.common.di.providers.getData import com.shabinder.common.di.utils.ParallelExecutor import com.shabinder.common.models.DownloadResult import com.shabinder.common.models.DownloadStatus +import com.shabinder.common.models.Status import com.shabinder.common.models.TrackDetails import com.shabinder.downloader.models.formats.Format -import com.shabinder.common.models.Status import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -124,8 +120,7 @@ class ForegroundService : Service(), CoroutineScope { val downloadObjects: ArrayList? = ( it.getParcelableArrayListExtra("object") ?: it.extras?.getParcelableArrayList( - "object" - ) + "object") ) downloadObjects?.let { list -> @@ -212,13 +207,11 @@ class ForegroundService : Service(), CoroutineScope { is DownloadResult.Error -> { launch { logger.d(tag) { it.message } - /*logger.d(tag) { "${track.title} Requesting Download thru Android DM" } - downloadUsingDM(url, track.outputFilePath, track)*/ removeFromNotification("Downloading ${track.title}") failed++ + updateNotification() + sendTrackBroadcast(Status.FAILED.name,track) } - updateNotification() - sendTrackBroadcast(Status.FAILED.name,track) } is DownloadResult.Progress -> { @@ -248,14 +241,10 @@ class ForegroundService : Service(), CoroutineScope { } logger.d(tag) { "${track.title} Download Completed" } downloaded++ - } catch ( - e: Exception - ) { - // Try downloading using android DM + } catch (e: Exception) { + // Download Failed logger.d(tag) { "${track.title} Download Failed! Error:Fetch!!!!" } failed++ - /*logger.d(tag) { "${track.title} Requesting Download thru Android DM" } - downloadUsingDM(url, track.outputFilePath, track)*/ } removeFromNotification("Downloading ${track.title}") } @@ -263,54 +252,6 @@ class ForegroundService : Service(), CoroutineScope { } } - /** - * If Custom Downloader Fails , Android Download Manager To RESCUE!! - **/ - private fun downloadUsingDM(url: String, outputDir: String, track: TrackDetails) { - launch { - val uri = Uri.parse(url) - val request = DownloadManager.Request(uri).apply { - setAllowedNetworkTypes( - DownloadManager.Request.NETWORK_WIFI or - DownloadManager.Request.NETWORK_MOBILE - ) - setAllowedOverRoaming(false) - setTitle(track.title) - setDescription("Spotify Downloader Working Up here...") - setDestinationUri(File(outputDir).toUri()) - setNotificationVisibility(VISIBILITY_VISIBLE_NOTIFY_COMPLETED) - } - - // Start Download - val downloadID = downloadManager.enqueue(request) - logger.d("DownloadManager") { "Download Request Sent" } - - val onDownloadComplete: BroadcastReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - // Fetching the download id received with the broadcast - val id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1) - // Checking if the received broadcast is for our enqueued download by matching download id - if (downloadID == id) { - allTracksStatus[track.title] = DownloadStatus.Converting - launch { dir.saveFileWithMetadata(byteArrayOf(), track){}; converted++ } - // Unregister this broadcast Receiver - this@ForegroundService.unregisterReceiver(this) - } - } - } - registerReceiver(onDownloadComplete, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) - } - } - - /** - * This is the method that can be called to update the Notification - */ - private fun updateNotification() { - val mNotificationManager: NotificationManager = - getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - mNotificationManager.notify(notificationId, getNotification()) - } - private fun releaseWakeLock() { logger.d(tag) { "Releasing Wake Lock" } try { @@ -337,34 +278,18 @@ class ForegroundService : Service(), CoroutineScope { service.createNotificationChannel(channel) } - /** - * Cleaning All Residual Files except Mp3 Files - **/ - private fun cleanFiles(dir: File) { - logger.d(tag) { "Starting Cleaning in ${dir.path} " } - val fList = dir.listFiles() - fList?.let { - for (file in fList) { - if (file.isDirectory) { - cleanFiles(file) - } else if (file.isFile) { - if (file.path.toString().substringAfterLast(".") != "mp3") { - logger.d(tag) { "Cleaning ${file.path}" } - file.delete() - } - } - } - } - } - + /* + * Time To Wrap UP + * - `Clean Up` and `Stop this Foreground Service` + * */ private fun killService() { launch { logger.d(tag) { "Killing Self" } messageList = mutableListOf("Cleaning And Exiting", "", "", "", "") downloadService.close() updateNotification() - cleanFiles(File(dir.defaultDir())) - // TODO cleanFiles(File(dir.imageCacheDir())) + dir.defaultDir().documentFile?.let { cleanFiles(it,dir.fileManager,logger) } + cleanFiles(File(dir.imageCachePath + "Tracks/"),logger) messageList = mutableListOf("", "", "", "", "") releaseWakeLock() serviceJob.cancel() @@ -391,6 +316,9 @@ class ForegroundService : Service(), CoroutineScope { } } + /* + * Create A New Notification with all the updated data + * */ private fun getNotification(): Notification = NotificationCompat.Builder(this, channelId).run { setSmallIcon(R.drawable.ic_download_arrow) setContentTitle("Total: $total Completed:$converted Failed:$failed") @@ -418,6 +346,15 @@ class ForegroundService : Service(), CoroutineScope { updateNotification() } + /** + * This is the method that can be called to update the Notification + */ + private fun updateNotification() { + val mNotificationManager: NotificationManager = + getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + mNotificationManager.notify(notificationId, getNotification()) + } + private fun sendTrackBroadcast(action: String, track: TrackDetails) { val intent = Intent().apply { setAction(action) diff --git a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/worker/Utils.kt b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/worker/Utils.kt new file mode 100644 index 00000000..e20f1de1 --- /dev/null +++ b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/worker/Utils.kt @@ -0,0 +1,50 @@ +package com.shabinder.common.di.worker + +import co.touchlab.kermit.Kermit +import com.github.k1rakishou.fsaf.FileManager +import com.github.k1rakishou.fsaf.file.AbstractFile +import java.io.File + +/** + * Cleaning All Residual Files except Mp3 Files + **/ +fun cleanFiles(dir: File,logger: Kermit) { + try { + logger.d("File Cleaning") { "Starting Cleaning in ${dir.path} " } + val fList = dir.listFiles() + fList?.let { + for (file in fList) { + if (file.isDirectory) { + cleanFiles(file, logger) + } else if (file.isFile) { + if (file.path.toString().substringAfterLast(".") != "mp3") { + logger.d("Files Cleaning") { "Cleaning ${file.path}" } + file.delete() + } + } + } + } + } catch (e:Exception) { e.printStackTrace() } +} +/** + * Cleaning All Residual Files except Mp3 Files + **/ +fun cleanFiles(directory: AbstractFile,fm: FileManager,logger: Kermit) { + try { + logger.d("Files Cleaning") { "Starting Cleaning in ${directory.getFullPath()} " } + val fList = fm.listFiles(directory) + for (file in fList) { + if (fm.isDirectory(file)) { + cleanFiles(file, fm, logger) + } else if (fm.isFile(file)) { + if (file.getFullPath().substringAfterLast(".") != "mp3" + || + fm.getLength(file) == 0L + ) { + logger.d("Files Cleaning") { "Cleaning ${file.getFullPath()}" } + fm.delete(file) + } + } + } + } catch (e:Exception) { e.printStackTrace() } +} \ No newline at end of file diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/FetchPlatformQueryResult.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/FetchPlatformQueryResult.kt index ef1b6f27..63110d0a 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/FetchPlatformQueryResult.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/FetchPlatformQueryResult.kt @@ -21,13 +21,10 @@ import com.shabinder.common.di.providers.GaanaProvider import com.shabinder.common.di.providers.SpotifyProvider import com.shabinder.common.di.providers.YoutubeMp3 import com.shabinder.common.di.providers.YoutubeMusic +import com.shabinder.common.di.providers.YoutubeProvider import com.shabinder.common.models.PlatformQueryResult -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.async -import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext class FetchPlatformQueryResult( val gaanaProvider: GaanaProvider, diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/GaanaProvider.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/GaanaProvider.kt index 69959a54..fbebe579 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/GaanaProvider.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/GaanaProvider.kt @@ -18,8 +18,8 @@ package com.shabinder.common.di.providers import co.touchlab.kermit.Kermit import com.shabinder.common.di.Dir -import com.shabinder.common.di.finalOutputDir import com.shabinder.common.di.gaana.GaanaRequests +import com.shabinder.common.di.getNameURL import com.shabinder.common.models.DownloadStatus import com.shabinder.common.models.PlatformQueryResult import com.shabinder.common.models.TrackDetails @@ -136,7 +136,7 @@ class GaanaProvider( title = it.track_title, artists = it.artist.map { artist -> artist?.name.toString() }, durationSec = it.duration, - albumArtPath = dir.imageCacheDir() + (it.artworkLink.substringBeforeLast('/').substringAfterLast('/')) + ".jpeg", + albumArtPath = dir.imageCachePath + getNameURL(it.artworkLink), albumName = it.album_title, year = it.release_date, comment = "Genres:${it.genre?.map { genre -> genre?.name }?.reduceOrNull { acc, s -> acc + s }}", @@ -144,16 +144,15 @@ class GaanaProvider( downloaded = it.downloaded ?: DownloadStatus.NotDownloaded, source = Source.Gaana, albumArtURL = it.artworkLink.replace("http:","https:"), - outputFilePath = dir.finalOutputDir(it.track_title, type, subFolder, dir.defaultDir()/*,".m4a"*/) + outputFilePath = dir.finalOutputPath(it.track_title, type, subFolder /*,".m4a"*/) ) } private fun GaanaTrack.updateStatusIfPresent(folderType: String, subFolder: String) { if (dir.isPresent( - dir.finalOutputDir( + dir.finalOutputFile( track_title, folderType, subFolder, - dir.defaultDir() ) ) ) { // Download Already Present!! diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/SpotifyProvider.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/SpotifyProvider.kt index ab40a511..ad029703 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/SpotifyProvider.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/SpotifyProvider.kt @@ -17,14 +17,10 @@ package com.shabinder.common.di.providers import co.touchlab.kermit.Kermit -import co.touchlab.stately.ensureNeverFrozen -import co.touchlab.stately.freeze import com.shabinder.common.di.Dir import com.shabinder.common.di.TokenStore import com.shabinder.common.di.createHttpClient -import com.shabinder.common.di.finalOutputDir -import com.shabinder.common.di.kotlinxSerializer -import com.shabinder.common.di.ktorHttpClient +import com.shabinder.common.di.getNameURL import com.shabinder.common.di.spotify.SpotifyRequests import com.shabinder.common.di.spotify.authenticateSpotify import com.shabinder.common.models.NativeAtomicReference @@ -35,12 +31,8 @@ import com.shabinder.common.models.spotify.Image import com.shabinder.common.models.spotify.Source import com.shabinder.common.models.spotify.Track import io.ktor.client.HttpClient -import io.ktor.client.features.auth.* -import io.ktor.client.features.auth.providers.* import io.ktor.client.features.defaultRequest -import io.ktor.client.features.json.JsonFeature import io.ktor.client.request.header -import kotlin.native.concurrent.SharedImmutable class SpotifyProvider( private val tokenStore: TokenStore, @@ -224,28 +216,28 @@ class SpotifyProvider( } private fun List.toTrackDetailsList(type: String, subFolder: String) = this.map { + val albumArtLink = it.album?.images?.firstOrNull()?.url.toString() TrackDetails( title = it.name.toString(), artists = it.artists?.map { artist -> artist?.name.toString() } ?: listOf(), durationSec = (it.duration_ms / 1000).toInt(), - albumArtPath = dir.imageCacheDir() + (it.album?.images?.firstOrNull()?.url.toString()).substringAfterLast('/') + ".jpeg", + albumArtPath = dir.imageCachePath + getNameURL(albumArtLink), albumName = it.album?.name, year = it.album?.release_date, comment = "Genres:${it.album?.genres?.joinToString()}", trackUrl = it.href, downloaded = it.downloaded, source = Source.Spotify, - albumArtURL = it.album?.images?.firstOrNull()?.url.toString(), - outputFilePath = dir.finalOutputDir(it.name.toString(), type, subFolder, dir.defaultDir()/*,".m4a"*/) + albumArtURL = albumArtLink, + outputFilePath = dir.finalOutputPath(it.name.toString(), type, subFolder/*,".m4a"*/) ) } private fun Track.updateStatusIfPresent(folderType: String, subFolder: String) { if (dir.isPresent( - dir.finalOutputDir( + dir.finalOutputFile( name.toString(), folderType, subFolder, - dir.defaultDir() ) ) ) { // Download Already Present!! diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/YoutubeProvider.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeProvider.kt similarity index 89% rename from common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/YoutubeProvider.kt rename to common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeProvider.kt index 37573ce5..9d8af706 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/YoutubeProvider.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeProvider.kt @@ -14,9 +14,11 @@ * * along with this program. If not, see . */ -package com.shabinder.common.di +package com.shabinder.common.di.providers import co.touchlab.kermit.Kermit +import com.shabinder.common.di.Dir +import com.shabinder.common.di.getNameURL import com.shabinder.common.di.utils.removeIllegalChars import com.shabinder.common.models.DownloadStatus import com.shabinder.common.models.PlatformQueryResult @@ -106,15 +108,14 @@ class YoutubeProvider( title = it.title ?: "N/A", artists = listOf(it.author ?: "N/A"), durationSec = it.lengthSeconds, - albumArtPath = dir.imageCacheDir() + it.videoId + ".jpeg", + albumArtPath = dir.imageCachePath + getNameURL(coverUrl), source = Source.YouTube, - albumArtURL = "https://i.ytimg.com/vi/${it.videoId}/hqdefault.jpg", + albumArtURL = coverUrl, downloaded = if (dir.isPresent( - dir.finalOutputDir( + dir.finalOutputFile( itemName = it.title ?: "N/A", type = folderType, subFolder = subFolder, - dir.defaultDir() ) ) ) @@ -122,7 +123,7 @@ class YoutubeProvider( else { DownloadStatus.NotDownloaded }, - outputFilePath = dir.finalOutputDir(it.title ?: "N/A", folderType, subFolder, dir.defaultDir()/*,".m4a"*/), + outputFilePath = dir.finalOutputPath(it.title ?: "N/A", folderType, subFolder/*,".m4a"*/), videoID = it.videoId ) } @@ -160,15 +161,14 @@ class YoutubeProvider( title = name, artists = listOf(detail.author ?: "N/A"), durationSec = detail.lengthSeconds, - albumArtPath = dir.imageCacheDir() + "$searchId.jpeg", + albumArtPath = dir.imageCachePath + getNameURL(coverUrl), source = Source.YouTube, - albumArtURL = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg", + albumArtURL = coverUrl, downloaded = if (dir.isPresent( - dir.finalOutputDir( + dir.finalOutputFile( itemName = name, type = folderType, subFolder = subFolder, - defaultDir = dir.defaultDir() ) ) ) @@ -176,7 +176,7 @@ class YoutubeProvider( else { DownloadStatus.NotDownloaded }, - outputFilePath = dir.finalOutputDir(name, folderType, subFolder, dir.defaultDir()/*,".m4a"*/), + outputFilePath = dir.finalOutputPath(name, folderType, subFolder /*,".m4a"*/), videoID = searchId ) ) diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/utils/Utils.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/utils/Utils.kt index 88fcc775..f1c7734f 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/utils/Utils.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/utils/Utils.kt @@ -90,6 +90,7 @@ fun removeIllegalChars(fileName: String): String { name = fileName.replace(c, '_') } name = name.replace("\\s".toRegex(), "_") + name = name.replace("/".toRegex(), "_") name = name.replace("\\)".toRegex(), "") name = name.replace("\\(".toRegex(), "") name = name.replace("\\[".toRegex(), "") diff --git a/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/DesktopActual.kt b/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/DesktopActual.kt index f03e6fcd..788aed87 100644 --- a/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/DesktopActual.kt +++ b/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/DesktopActual.kt @@ -17,6 +17,7 @@ package com.shabinder.common.di import com.shabinder.common.di.providers.YoutubeMp3 +import com.shabinder.common.di.providers.getData import com.shabinder.common.di.utils.ParallelExecutor import com.shabinder.common.models.AllPlatforms import com.shabinder.common.models.DownloadResult diff --git a/common/dependency-injection/src/iosMain/kotlin/com.shabinder.common.di/IOSActual.kt b/common/dependency-injection/src/iosMain/kotlin/com.shabinder.common.di/IOSActual.kt index 41a8475e..a4dee917 100644 --- a/common/dependency-injection/src/iosMain/kotlin/com.shabinder.common.di/IOSActual.kt +++ b/common/dependency-injection/src/iosMain/kotlin/com.shabinder.common.di/IOSActual.kt @@ -1,5 +1,6 @@ package com.shabinder.common.di +import com.shabinder.common.di.providers.getData import com.shabinder.common.di.utils.ParallelExecutor import com.shabinder.common.models.AllPlatforms import com.shabinder.common.models.DownloadResult diff --git a/common/main/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMain.kt b/common/main/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMain.kt index babb9bcd..81df8e87 100644 --- a/common/main/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMain.kt +++ b/common/main/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMain.kt @@ -25,7 +25,6 @@ import com.shabinder.common.main.integration.SpotiFlyerMainImpl import com.shabinder.common.models.Consumer import com.shabinder.common.models.DownloadRecord import com.shabinder.database.Database -import kotlinx.coroutines.flow.Flow interface SpotiFlyerMain { diff --git a/common/main/src/commonMain/kotlin/com/shabinder/common/main/integration/SpotiFlyerMainImpl.kt b/common/main/src/commonMain/kotlin/com/shabinder/common/main/integration/SpotiFlyerMainImpl.kt index 4ba6d31a..861aaf75 100644 --- a/common/main/src/commonMain/kotlin/com/shabinder/common/main/integration/SpotiFlyerMainImpl.kt +++ b/common/main/src/commonMain/kotlin/com/shabinder/common/main/integration/SpotiFlyerMainImpl.kt @@ -18,7 +18,6 @@ package com.shabinder.common.main.integration import co.touchlab.stately.ensureNeverFrozen import com.arkivanov.decompose.ComponentContext -import com.arkivanov.decompose.lifecycle.doOnDestroy import com.arkivanov.decompose.value.Value import com.shabinder.common.di.Picture import com.shabinder.common.di.utils.asValue