diff --git a/README.md b/README.md index b0b8f806..0aaeb02a 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,10 @@
-| Build | Latest Release | Downloads | Contribute | -|-------|----------|------------|------------| -| ![CI](https://github.com/deniscerri/ytdlnis/actions/workflows/android.yml/badge.svg?branch=main&event=pull) | [![stable release](https://img.shields.io/github/release/deniscerri/ytdlnis.svg?maxAge=3600&label=download)](https://github.com/deniscerri/ytdlnis/releases/latest) | [![downloads](https://img.shields.io/github/downloads/deniscerri/ytdlnis/total?style=flat-square)](https://github.com/deniscerri/ytdlnis/releases) | [![Translation status](https://hosted.weblate.org/widgets/ytdlnis/-/svg-badge.svg)](https://hosted.weblate.org/engage/ytdlnis/?utm_source=widget) | +![CI](https://github.com/deniscerri/ytdlnis/actions/workflows/android.yml/badge.svg?branch=main&event=pull) [![stable release](https://img.shields.io/github/release/deniscerri/ytdlnis.svg?maxAge=3600&label=download)](https://github.com/deniscerri/ytdlnis/releases/latest) [![downloads](https://img.shields.io/github/downloads/deniscerri/ytdlnis/total?style=flat-square)](https://github.com/deniscerri/ytdlnis/releases) [![Translation status](https://hosted.weblate.org/widgets/ytdlnis/-/svg-badge.svg)](https://hosted.weblate.org/engage/ytdlnis/?utm_source=widget) [![community](https://img.shields.io/badge/Discord-YTDLnis-blueviolet?style=flat-square&logo=discord)](https://discord.gg/WW3KYWxAPm) + + +
diff --git a/app/build.gradle b/app/build.gradle index 00d48d76..e61f6399 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -89,9 +89,6 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) -// implementation project(":library") -// implementation project(":ffmpeg") - implementation "com.github.junkfood02.youtubedl-android:library:$youtubedlAndroidVer" implementation "com.github.junkfood02.youtubedl-android:ffmpeg:$youtubedlAndroidVer" implementation "com.github.junkfood02.youtubedl-android:aria2c:$youtubedlAndroidVer" @@ -122,10 +119,10 @@ dependencies { implementation "androidx.work:work-runtime-ktx:$workVer" - implementation "androidx.room:room-runtime:2.4.3" - implementation "androidx.room:room-ktx:2.4.3" - kapt "androidx.room:room-compiler:2.4.3" - androidTestImplementation "androidx.room:room-testing:2.4.3" + implementation "androidx.room:room-runtime:2.5.0" + implementation "androidx.room:room-ktx:2.5.0" + kapt "androidx.room:room-compiler:2.5.0" + androidTestImplementation "androidx.room:room-testing:2.5.0" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1' implementation "androidx.compose.runtime:runtime:$composeVer" androidTestImplementation "com.google.truth:truth:1.1.3" @@ -137,4 +134,6 @@ dependencies { implementation "com.squareup.retrofit2:converter-gson:$retrofitVer" implementation "com.squareup.okhttp3:okhttp:5.0.0-alpha.10" implementation "com.squareup.okhttp3:logging-interceptor:4.7.2" + + implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1" } \ No newline at end of file diff --git a/app/schemas/com.deniscerri.ytdlnis.database.DBManager/1.json b/app/schemas/com.deniscerri.ytdlnis.database.DBManager/1.json index 1283c17f..5fe6d706 100644 --- a/app/schemas/com.deniscerri.ytdlnis.database.DBManager/1.json +++ b/app/schemas/com.deniscerri.ytdlnis.database.DBManager/1.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 1, - "identityHash": "1391e7ec82bbacc310cf6f23e3d1f85a", + "identityHash": "6c48acc2155f9a895971bd84c84b7d4e", "entities": [ { "tableName": "results", @@ -58,10 +58,66 @@ } ], "primaryKey": { + "autoGenerate": true, "columnNames": [ "id" - ], - "autoGenerate": true + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "formats", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` INTEGER NOT NULL, `format` TEXT NOT NULL, `format_id` TEXT NOT NULL, `ext` TEXT NOT NULL, `filesize` INTEGER NOT NULL, `format_note` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "format", + "columnName": "format", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "format_id", + "columnName": "format_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "ext", + "columnName": "ext", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filesize", + "columnName": "filesize", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "format_note", + "columnName": "format_note", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] }, "indices": [], "foreignKeys": [] @@ -132,17 +188,17 @@ } ], "primaryKey": { + "autoGenerate": true, "columnNames": [ "id" - ], - "autoGenerate": true + ] }, "indices": [], "foreignKeys": [] }, { "tableName": "downloads", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `thumb` TEXT NOT NULL, `duration` TEXT NOT NULL, `type` TEXT NOT NULL, `audioQualityId` TEXT NOT NULL, `videoQualityId` TEXT NOT NULL, `customTemplateId` INTEGER NOT NULL, `audioFormat` TEXT NOT NULL, `videoFormat` TEXT NOT NULL, `formatDesc` TEXT NOT NULL, `removeAudio` INTEGER NOT NULL DEFAULT 0, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL, `downloadSize` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL, `status` TEXT NOT NULL DEFAULT 'Queued', `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `thumb` TEXT NOT NULL, `duration` TEXT NOT NULL, `type` TEXT NOT NULL, `audioQualityId` TEXT NOT NULL, `videoQualityId` TEXT NOT NULL, `customTemplateId` INTEGER NOT NULL, `audioFormat` TEXT NOT NULL, `videoFormat` TEXT NOT NULL, `formatDesc` TEXT NOT NULL, `removeAudio` INTEGER NOT NULL DEFAULT 0, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL, `downloadSize` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL, `status` TEXT NOT NULL DEFAULT 'Queued', `workID` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", "fields": [ { "fieldPath": "url", @@ -254,6 +310,12 @@ "notNull": true, "defaultValue": "'Queued'" }, + { + "fieldPath": "workID", + "columnName": "workID", + "affinity": "INTEGER", + "notNull": true + }, { "fieldPath": "id", "columnName": "id", @@ -262,10 +324,10 @@ } ], "primaryKey": { + "autoGenerate": true, "columnNames": [ "id" - ], - "autoGenerate": true + ] }, "indices": [], "foreignKeys": [] @@ -294,10 +356,10 @@ } ], "primaryKey": { + "autoGenerate": true, "columnNames": [ "id" - ], - "autoGenerate": true + ] }, "indices": [], "foreignKeys": [] @@ -306,7 +368,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1391e7ec82bbacc310cf6f23e3d1f85a')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6c48acc2155f9a895971bd84c84b7d4e')" ] } } \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdlnis/DownloaderService.kt b/app/src/main/java/com/deniscerri/ytdlnis/DownloaderService.kt index b2966c92..e031014e 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/DownloaderService.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/DownloaderService.kt @@ -1,7 +1,6 @@ package com.deniscerri.ytdlnis import android.app.Activity -import android.app.PendingIntent import android.app.Service import android.content.Context import android.content.Intent @@ -10,7 +9,6 @@ import android.os.Build import android.os.IBinder import android.os.SystemClock import android.util.Log -import android.widget.Toast import androidx.work.ExistingWorkPolicy import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkManager @@ -19,9 +17,7 @@ import com.deniscerri.ytdlnis.database.models.ResultItem import com.deniscerri.ytdlnis.service.DownloadInfo import com.deniscerri.ytdlnis.service.IDownloaderListener import com.deniscerri.ytdlnis.service.IDownloaderService -import com.deniscerri.ytdlnis.ui.CustomCommandActivity import com.deniscerri.ytdlnis.util.FileUtil -import com.deniscerri.ytdlnis.util.NotificationUtil import com.deniscerri.ytdlnis.work.FileTransferWorker import com.deniscerri.ytdlnis.work.FileTransferWorker.Companion.downLocation import com.deniscerri.ytdlnis.work.FileTransferWorker.Companion.originDir @@ -34,7 +30,6 @@ import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import io.reactivex.schedulers.Schedulers import java.io.File -import java.io.FileOutputStream import java.util.* import java.util.concurrent.ConcurrentHashMap import java.util.regex.Pattern diff --git a/app/src/main/java/com/deniscerri/ytdlnis/MainActivity.kt b/app/src/main/java/com/deniscerri/ytdlnis/MainActivity.kt index b8d8930c..25b13c0f 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/MainActivity.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/MainActivity.kt @@ -18,7 +18,6 @@ import androidx.core.view.WindowInsetsCompat import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import com.deniscerri.ytdlnis.DownloaderService.LocalBinder -import com.deniscerri.ytdlnis.database.DatabaseManager import com.deniscerri.ytdlnis.database.models.ResultItem import com.deniscerri.ytdlnis.databinding.ActivityMainBinding import com.deniscerri.ytdlnis.ui.DownloadsFragment @@ -331,7 +330,6 @@ class MainActivity : AppCompatActivity() { grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) - Log.e(TAG, grantResults[0].toString()) for (i in permissions.indices) { if (grantResults[i] == PackageManager.PERMISSION_DENIED) { createPermissionRequestDialog() diff --git a/app/src/main/java/com/deniscerri/ytdlnis/adapter/HomeAdapter.java b/app/src/main/java/com/deniscerri/ytdlnis/adapter/HomeAdapter.java index a80280e8..7b4bdf4d 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/adapter/HomeAdapter.java +++ b/app/src/main/java/com/deniscerri/ytdlnis/adapter/HomeAdapter.java @@ -4,7 +4,6 @@ import android.app.Activity; import android.graphics.Color; import android.os.Handler; import android.os.Looper; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -36,19 +35,16 @@ public class HomeAdapter extends ListAdapter this.checkedVideos = new ArrayList<>(); this.onItemClickListener = onItemClickListener; this.activity = activity; - Log.e("TAG", "adapter"); } private static final DiffUtil.ItemCallback DIFF_CALLBACK = new DiffUtil.ItemCallback() { @Override public boolean areItemsTheSame(@NonNull ResultItem oldItem, @NonNull ResultItem newItem) { - Log.e("TAG", "adapter2"); return oldItem.getId() == newItem.getId(); } @Override public boolean areContentsTheSame(@NonNull ResultItem oldItem, @NonNull ResultItem newItem) { - Log.e("TAG", "adapter3"); return oldItem.getUrl().equals(newItem.getUrl()); } }; @@ -79,7 +75,6 @@ public class HomeAdapter extends ListAdapter @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { ResultItem video = getItem(position); - Log.e("TAG", "adapter4"); MaterialCardView card = holder.cardView; // THUMBNAIL ---------------------------------- ImageView thumbnail = card.findViewById(R.id.result_image_view); diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/DBManager.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/DBManager.kt index 3e0c57fa..f0ab6c3c 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/DBManager.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/DBManager.kt @@ -4,23 +4,18 @@ import android.content.Context import androidx.room.Database import androidx.room.Room import androidx.room.RoomDatabase -import com.deniscerri.ytdlnis.database.dao.CommandTemplateDao -import com.deniscerri.ytdlnis.database.dao.DownloadDao -import com.deniscerri.ytdlnis.database.dao.HistoryDao -import com.deniscerri.ytdlnis.database.dao.ResultDao -import com.deniscerri.ytdlnis.database.models.CommandTemplate -import com.deniscerri.ytdlnis.database.models.DownloadItem -import com.deniscerri.ytdlnis.database.models.HistoryItem -import com.deniscerri.ytdlnis.database.models.ResultItem +import com.deniscerri.ytdlnis.database.dao.* +import com.deniscerri.ytdlnis.database.models.* @Database( - entities = [ResultItem::class, HistoryItem::class, DownloadItem::class, CommandTemplate::class], + entities = [ResultItem::class, Format::class, HistoryItem::class, DownloadItem::class, CommandTemplate::class], version = 1, autoMigrations = [] ) abstract class DBManager : RoomDatabase(){ abstract val resultDao : ResultDao abstract val historyDao : HistoryDao + abstract val formatDao : FormatDao abstract val downloadDao : DownloadDao abstract val commandTemplateDao : CommandTemplateDao diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/DatabaseManager.java b/app/src/main/java/com/deniscerri/ytdlnis/database/DatabaseManager.java index 4c43be17..f5e3c6e2 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/DatabaseManager.java +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/DatabaseManager.java @@ -6,9 +6,7 @@ import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; -import android.widget.Toast; -import com.deniscerri.ytdlnis.R; import com.deniscerri.ytdlnis.database.models.HistoryItem; import com.deniscerri.ytdlnis.database.models.ResultItem; diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/DownloadDao.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/DownloadDao.kt index 6346d48d..ea049c8b 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/DownloadDao.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/DownloadDao.kt @@ -12,8 +12,11 @@ interface DownloadDao { @Query("SELECT * FROM downloads") fun getAllDownloads() : LiveData> - @Query("SELECT * FROM downloads WHERE status='Queued'") - fun getQueuedDownloads() : List + @Query("SELECT * FROM downloads WHERE status='Active'") + fun getActiveDownloads() : LiveData> + + @Query("SELECT * FROM downloads WHERE workID=:workID") + fun getDownloadsByWorkId(workID: Int) : List @Query("SELECT * FROM downloads WHERE id=:id LIMIT 1") fun getDownloadById(id: Int) : DownloadItem diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/FormatDao.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/FormatDao.kt new file mode 100644 index 00000000..e349b147 --- /dev/null +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/FormatDao.kt @@ -0,0 +1,25 @@ +package com.deniscerri.ytdlnis.database.dao + +import androidx.lifecycle.LiveData +import androidx.room.* +import com.deniscerri.ytdlnis.database.models.Format +import com.deniscerri.ytdlnis.database.models.ResultItem + +@Dao +interface FormatDao { + @Query("SELECT * FROM formats") + fun getFormatsByItemId() : LiveData> + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insert(item: Format) + + @Update(onConflict = OnConflictStrategy.REPLACE) + suspend fun update(item: Format) + + @Query("DELETE FROM formats") + suspend fun deleteAll() + + @Query("DELETE FROM formats WHERE itemId=:itemId") + suspend fun deleteFromatsByItemId(itemId: Int) + +} \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/ResultDao.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/ResultDao.kt index 1aa7f5b4..e6cc5482 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/ResultDao.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/ResultDao.kt @@ -3,19 +3,25 @@ package com.deniscerri.ytdlnis.database.dao import androidx.lifecycle.LiveData import androidx.room.* import com.deniscerri.ytdlnis.database.models.ResultItem -import com.deniscerri.ytdlnis.util.FileUtil @Dao interface ResultDao { @Query("SELECT * FROM results") fun getResults() : LiveData> + @Query("SELECT * FROM results LIMIT 1") + fun getFirstResult() : ResultItem + + @Query("SELECT * FROM results ORDER BY id DESC LIMIT 1") + fun getLastResult(): ResultItem + @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insert(item: ResultItem) + suspend fun insert(item: ResultItem) : Long @Update(onConflict = OnConflictStrategy.REPLACE) suspend fun update(item: ResultItem) @Query("DELETE FROM results") suspend fun deleteAll() + } \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/models/DownloadItem.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/models/DownloadItem.kt index 4b71a806..4764ecdb 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/models/DownloadItem.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/models/DownloadItem.kt @@ -25,7 +25,8 @@ data class DownloadItem( val downloadSize: String, val playlistTitle: String, @ColumnInfo(defaultValue = "Queued") - var status: String + val status: String, + val workID: Int ){ @PrimaryKey(autoGenerate = true) var id: Int? = null diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/models/Format.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/models/Format.kt new file mode 100644 index 00000000..2fecfc5c --- /dev/null +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/models/Format.kt @@ -0,0 +1,17 @@ +package com.deniscerri.ytdlnis.database.models + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "formats") +data class Format( + var itemId: Int, + val format: String, + val format_id: String, + val ext: String, + val filesize: Long, + val format_note: String +){ + @PrimaryKey(autoGenerate = true) + var id: Int? = null +} \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/models/ResultItem.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/models/ResultItem.kt index 27a89170..285dc47b 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/models/ResultItem.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/models/ResultItem.kt @@ -2,9 +2,6 @@ package com.deniscerri.ytdlnis.database.models import androidx.room.Entity import androidx.room.PrimaryKey -import com.google.gson.annotations.SerializedName -import com.yausername.youtubedl_android.mapper.VideoFormat -import com.yausername.youtubedl_android.mapper.VideoInfo @Entity(tableName = "results") data class ResultItem( diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/models/ResultItemWithFormats.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/models/ResultItemWithFormats.kt new file mode 100644 index 00000000..bd34497d --- /dev/null +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/models/ResultItemWithFormats.kt @@ -0,0 +1,6 @@ +package com.deniscerri.ytdlnis.database.models + +data class ResultItemWithFormats( + val resultItem: ResultItem, + var formats: ArrayList +) \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/DownloadRepository.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/DownloadRepository.kt index 8bcd6a15..0005803d 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/DownloadRepository.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/DownloadRepository.kt @@ -6,6 +6,7 @@ import com.deniscerri.ytdlnis.database.models.DownloadItem class DownloadRepository(private val downloadDao: DownloadDao) { val allDownloads : LiveData> = downloadDao.getAllDownloads() + val activeDownloads : LiveData> = downloadDao.getActiveDownloads() suspend fun insert(item: DownloadItem){ downloadDao.insert(item) @@ -19,13 +20,8 @@ class DownloadRepository(private val downloadDao: DownloadDao) { downloadDao.update(item) } - suspend fun getQueuedDownloads() : List{ - val items = downloadDao.getQueuedDownloads() - for (i in items){ - i.status = "Downloading" - downloadDao.update(i) - } - return items + fun getDownloadsByWorkID(workID: Int) : List{ + return downloadDao.getDownloadsByWorkId(workID) } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/ResultRepository.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/ResultRepository.kt index 17f43de0..8ae0a35e 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/ResultRepository.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/ResultRepository.kt @@ -1,31 +1,118 @@ package com.deniscerri.ytdlnis.database.repository import android.content.Context +import android.util.Log import androidx.lifecycle.LiveData -import com.deniscerri.ytdlnis.database.dao.DownloadDao +import com.deniscerri.ytdlnis.database.dao.FormatDao import com.deniscerri.ytdlnis.database.dao.ResultDao -import com.deniscerri.ytdlnis.database.models.DownloadItem +import com.deniscerri.ytdlnis.database.models.Format import com.deniscerri.ytdlnis.database.models.ResultItem import com.deniscerri.ytdlnis.util.InfoUtil -class ResultRepository(private val resultDao: ResultDao) { +class ResultRepository(private val resultDao: ResultDao, private val formatDao: FormatDao, private val context: Context) { + private val tag: String = "ResultRepository" val allResults : LiveData> = resultDao.getResults() suspend fun insert(it: ResultItem){ resultDao.insert(it) } - suspend fun updateTrending(context: Context){ - resultDao.deleteAll(); + fun getFirstResult() : ResultItem{ + return resultDao.getFirstResult() + } + + suspend fun updateTrending(){ + deleteAll() val infoUtil = InfoUtil(context) val items = infoUtil.getTrending(context) for (i in items){ - resultDao.insert(i!!); + val id = resultDao.insert(i!!.resultItem) + addFormats(i.formats,id) + } + } + + suspend fun search(inputQuery: String, resetResults: Boolean){ + val infoUtil = InfoUtil(context) + try{ + if (resetResults) deleteAll() + val res = infoUtil.search(inputQuery) + res.forEach { + val id = resultDao.insert(it!!.resultItem) + addFormats(it.formats,id) + } + }catch (ignored: Exception){} + } + + suspend fun getOne(inputQuery: String, resetResults: Boolean){ + var el: Array = + inputQuery.split("/".toRegex()).dropLastWhile { it.isEmpty() } + .toTypedArray() + var query = el[el.size - 1] + if (query!!.contains("watch?v=")) { + query = query.substring(8) + } + el = query.split("&".toRegex()).dropLastWhile { it.isEmpty() } + .toTypedArray() + query = el[0] + el = query!!.split("\\?".toRegex()).dropLastWhile { it.isEmpty() } + .toTypedArray() + query = el[0] + val infoUtil = InfoUtil(context) + try { + val v = infoUtil.getVideo(query!!) + if (resetResults) deleteAll() + val id = resultDao.insert(v!!.resultItem) + addFormats(v.formats,id) + } catch (e: Exception) { + Log.e(tag, e.toString()) + } + } + + suspend fun getPlaylist(inputQuery: String, resetResults: Boolean){ + val query = inputQuery.split("list=".toRegex()).dropLastWhile { it.isEmpty() } + .toTypedArray()[1] + var nextPageToken = "" + if (resetResults) deleteAll() + val infoUtil = InfoUtil(context) + do { + val tmp = infoUtil.getPlaylist(query, nextPageToken) + val tmpVids = tmp.videos + val tmpToken = tmp.nextPageToken + tmpVids.forEach { + val id = resultDao.insert(it!!.resultItem); + addFormats(it.formats,id) + } + if (tmpToken.isEmpty()) break + if (tmpToken == nextPageToken) break + nextPageToken = tmpToken + } while (true) + } + + suspend fun getDefault(inputQuery: String, resetResults: Boolean){ + val infoUtil = InfoUtil(context) + try { + if (resetResults) deleteAll() + val items = infoUtil.getFromYTDL(inputQuery) + items.forEach { + val id = resultDao.insert(it!!.resultItem); + addFormats(it.formats,id) + } + } catch (e: Exception) { + Log.e(tag, e.toString()) + } + } + + private suspend fun addFormats(formats : ArrayList, id: Long){ + formats.forEach { f -> + if (f.filesize == 0.toLong()) return@forEach + f.itemId = id.toInt() + formatDao.insert(f) } } suspend fun deleteAll(){ resultDao.deleteAll() + formatDao.deleteAll() } suspend fun update(item: ResultItem){ diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt index 420e5dad..5c3c0154 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt @@ -20,6 +20,13 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application allDownloads = repository.allDownloads } + fun startWork(items: List){ + items.forEach { + insertDownload(it) + } + + } + fun insertDownload(item: DownloadItem) = viewModelScope.launch(Dispatchers.IO){ repository.insert(item) } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ResultViewModel.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ResultViewModel.kt index 94b223d3..d19fc9bb 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ResultViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ResultViewModel.kt @@ -1,33 +1,76 @@ package com.deniscerri.ytdlnis.database.viewmodel import android.app.Application -import androidx.lifecycle.AndroidViewModel -import androidx.lifecycle.LiveData -import androidx.lifecycle.MediatorLiveData -import androidx.lifecycle.Observer -import androidx.lifecycle.viewModelScope +import android.util.Log +import androidx.lifecycle.* import com.deniscerri.ytdlnis.database.DBManager -import com.deniscerri.ytdlnis.database.models.HistoryItem import com.deniscerri.ytdlnis.database.models.ResultItem import com.deniscerri.ytdlnis.database.repository.ResultRepository import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import java.util.regex.Pattern class ResultViewModel(application: Application) : AndroidViewModel(application) { + private val tag: String = "ResultViewModel" private val repository : ResultRepository val items : LiveData> + val loadingItems = MutableLiveData() init { val dao = DBManager.getInstance(application).resultDao - repository = ResultRepository(dao) + val formatDao = DBManager.getInstance(application).formatDao + repository = ResultRepository(dao, formatDao, getApplication().applicationContext) items = repository.allResults + loadingItems.postValue(false) } - fun checkTrending() : Boolean{ - return items.value?.get(0)?.playlistTitle.equals("ytdlnis-TRENDING"); + fun checkTrending() = viewModelScope.launch(Dispatchers.IO){ + try { + val item = repository.getFirstResult() + if (item.playlistTitle == "ytdlnis-TRENDING") getTrending() + }catch (e : Exception){ + getTrending() + } } fun getTrending() = viewModelScope.launch(Dispatchers.IO){ - repository.updateTrending(getApplication().applicationContext); + loadingItems.postValue(true) + repository.updateTrending(); + loadingItems.postValue(false) + } + + fun parseQuery(inputQuery: String, resetResults: Boolean) = viewModelScope.launch(Dispatchers.IO){ + if (resetResults) loadingItems.postValue(true) + var type = "Search" + val p = Pattern.compile("^(https?)://(www.)?youtu(.be)?") + val m = p.matcher(inputQuery!!) + if (m.find()) { + type = "Video" + if (inputQuery!!.contains("playlist?list=")) { + type = "Playlist" + } + } else if (inputQuery!!.contains("http")) { + type = "Default" + } + Log.e(tag, "$inputQuery $type") + try { + when (type) { + "Search" -> { + repository.search(inputQuery, resetResults); + } + "Video" -> { + repository.getOne(inputQuery, resetResults); + } + "Playlist" -> { + repository.getPlaylist(inputQuery, resetResults); + } + "Default" -> { + repository.getDefault(inputQuery, resetResults); + } + } + } catch (e: Exception) { + Log.e(tag, e.toString()) + } + loadingItems.postValue(false) } fun insert(items: ArrayList) = viewModelScope.launch(Dispatchers.IO){ diff --git a/app/src/main/java/com/deniscerri/ytdlnis/service/InvidiousService.kt b/app/src/main/java/com/deniscerri/ytdlnis/service/InvidiousService.kt index 1ef4aded..e8e82f32 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/service/InvidiousService.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/service/InvidiousService.kt @@ -1,7 +1,5 @@ package com.deniscerri.ytdlnis.service -import androidx.room.Room -import com.deniscerri.ytdlnis.database.DBManager import com.deniscerri.ytdlnis.database.models.ResultItem import com.google.gson.JsonArray import com.google.gson.JsonObject diff --git a/app/src/main/java/com/deniscerri/ytdlnis/service/YoutubeAPIService.kt b/app/src/main/java/com/deniscerri/ytdlnis/service/YoutubeAPIService.kt index d542cbe0..1e05e0a4 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/service/YoutubeAPIService.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/service/YoutubeAPIService.kt @@ -1,10 +1,5 @@ package com.deniscerri.ytdlnis.service -import androidx.room.Query -import androidx.room.Room -import com.deniscerri.ytdlnis.database.DBManager -import com.deniscerri.ytdlnis.database.models.ResultItem -import com.deniscerri.ytdlnis.util.InfoUtil import com.google.gson.JsonArray import com.google.gson.JsonObject import retrofit2.Response diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt index 24ec3429..1bffb7d4 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt @@ -1,23 +1,19 @@ package com.deniscerri.ytdlnis.ui -import android.Manifest import android.annotation.SuppressLint import android.app.Activity import android.content.Context import android.content.Intent -import android.content.pm.PackageManager import android.os.Bundle import android.os.Handler import android.os.Looper import android.text.InputType -import android.util.Log import android.view.* import android.view.View.GONE import android.view.View.VISIBLE import android.widget.* import androidx.appcompat.widget.SearchView import androidx.coordinatorlayout.widget.CoordinatorLayout -import androidx.core.app.ActivityCompat import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager @@ -27,7 +23,6 @@ import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.adapter.HomeAdapter import com.deniscerri.ytdlnis.database.DatabaseManager import com.deniscerri.ytdlnis.database.models.ResultItem -import com.deniscerri.ytdlnis.database.viewmodel.HistoryViewModel import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel import com.deniscerri.ytdlnis.databinding.FragmentDownloadsBinding import com.deniscerri.ytdlnis.service.DownloadInfo @@ -44,8 +39,6 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.progressindicator.LinearProgressIndicator import com.google.android.material.textfield.TextInputLayout import java.util.* -import java.util.regex.Pattern -import kotlin.collections.ArrayList class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickListener { private var progressBar: LinearProgressIndicator? = null @@ -75,7 +68,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi private var topAppBar: MaterialToolbar? = null private var recyclerView: RecyclerView? = null private var bottomSheet: BottomSheetDialog? = null - private var sortSheet: BottomSheetDialog? = null private var uiHandler: Handler? = null private var noResults: RelativeLayout? = null private var selectionChips: LinearLayout? = null @@ -116,7 +108,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi selectedObjects = ArrayList() downloading = mainActivity!!.isDownloadServiceRunning() - downloadQueue = ArrayList() resultsList = mutableListOf() selectedObjects = ArrayList() @@ -140,8 +131,23 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi resultViewModel.items.observe(viewLifecycleOwner) { homeAdapter!!.submitList(it) resultsList = it - shimmerCards!!.stopShimmer() - shimmerCards!!.visibility = GONE + if(it.size > 1){ + if (it[0].playlistTitle.isNotEmpty() || it[0].playlistTitle != "ytdlnis-TRENDING"){ + downloadAllFabCoordinator!!.visibility = VISIBLE + } + } + } + + resultViewModel.loadingItems.observe(viewLifecycleOwner){ + if (it){ + recyclerView?.setPadding(0,0,0,0) + shimmerCards!!.startShimmer() + shimmerCards!!.visibility = VISIBLE + }else{ + recyclerView?.setPadding(0,0,0,100) + shimmerCards!!.stopShimmer() + shimmerCards!!.visibility = GONE + } } initMenu() @@ -163,12 +169,11 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi if (inputQueries != null) { resultViewModel.deleteAll() inputQueriesLength = inputQueries!!.size - shimmerCards!!.startShimmer() - shimmerCards!!.visibility = VISIBLE + Handler(Looper.getMainLooper()).post { scrollToTop() } val thread = Thread { while (!inputQueries!!.isEmpty()) { inputQuery = inputQueries!!.pop() - parseQuery(false) + resultViewModel.parseQuery(inputQuery!!, false) } try { Handler(Looper.getMainLooper()).post { @@ -187,11 +192,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi } thread.start() } else { - if (resultViewModel.checkTrending()){ - shimmerCards!!.startShimmer() - shimmerCards!!.visibility = VISIBLE - resultViewModel.getTrending() - } + resultViewModel.checkTrending() } } @@ -261,16 +262,16 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi val onActionExpandListener: MenuItem.OnActionExpandListener = object : MenuItem.OnActionExpandListener { override fun onMenuItemActionExpand(menuItem: MenuItem): Boolean { - homeFabs!!.visibility = View.GONE - recyclerView!!.visibility = View.GONE - searchSuggestions!!.visibility = View.VISIBLE + homeFabs!!.visibility = GONE + recyclerView!!.visibility = GONE + searchSuggestions!!.visibility = VISIBLE return true } override fun onMenuItemActionCollapse(menuItem: MenuItem): Boolean { - homeFabs!!.visibility = View.VISIBLE - recyclerView!!.visibility = View.VISIBLE - searchSuggestions!!.visibility = View.GONE + homeFabs!!.visibility = VISIBLE + recyclerView!!.visibility = VISIBLE + searchSuggestions!!.visibility = GONE return true } } @@ -287,20 +288,12 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi override fun onQueryTextSubmit(query: String): Boolean { topAppBar!!.menu.findItem(R.id.search).collapseActionView() downloadAllFabCoordinator!!.visibility = GONE - downloadFabs!!.visibility = View.GONE + downloadFabs!!.visibility = GONE selectedObjects = ArrayList() inputQuery = query.trim { it <= ' ' } - shimmerCards!!.startShimmer() - shimmerCards!!.visibility = View.VISIBLE resultViewModel.deleteAll() val thread = Thread { - parseQuery(true) - // DOWNLOAD ALL BUTTON - if (resultsList!!.size > 1 && resultsList!![1]!!.playlistTitle.isNotEmpty()) { - Handler(Looper.getMainLooper()).post { - downloadAllFabCoordinator!!.visibility = View.VISIBLE - } - } + resultViewModel.parseQuery(inputQuery!!, true) } thread.start() return true @@ -335,14 +328,14 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi return false } }) - topAppBar!!.setOnClickListener { view: View? -> scrollToTop() } + topAppBar!!.setOnClickListener { scrollToTop() } topAppBar!!.setOnMenuItemClickListener { m: MenuItem -> val itemId = m.itemId if (itemId == R.id.delete_results) { resultViewModel.getTrending() selectedObjects = ArrayList() - downloadAllFabCoordinator!!.visibility = View.GONE - downloadFabs!!.visibility = View.GONE + downloadAllFabCoordinator!!.visibility = GONE + downloadFabs!!.visibility = GONE } // } else if (itemId == R.id.cancel_download) { // try { @@ -376,114 +369,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi (topAppBar!!.parent as AppBarLayout).setExpanded(true, true) } - private fun parseQuery(resetResults: Boolean) { - databaseManager = DatabaseManager(context) - infoUtil = InfoUtil(requireContext()) - Handler(Looper.getMainLooper()).post { scrollToTop() } - var type = "Search" - val p = Pattern.compile("^(https?)://(www.)?youtu(.be)?") - val m = p.matcher(inputQuery!!) - if (m.find()) { - type = "Video" - if (inputQuery!!.contains("playlist?list=")) { - type = "Playlist" - } - } else if (inputQuery!!.contains("http")) { - type = "Default" - } - Log.e(TAG, "$inputQuery $type") - try { - when (type) { - "Search" -> { - try { - val res = infoUtil!!.search( - inputQuery!! - ) - Handler(Looper.getMainLooper()).post { - if (resetResults) resultViewModel.deleteAll() - resultViewModel.insert(res) - } - } catch (e: Exception) { - Log.e(TAG, e.toString()) - Handler(Looper.getMainLooper()).post { - shimmerCards!!.stopShimmer() - shimmerCards!!.visibility = View.GONE - } - } - } - "Video" -> { - var el: Array = - inputQuery!!.split("/".toRegex()).dropLastWhile { it.isEmpty() } - .toTypedArray() - inputQuery = el[el.size - 1] - if (inputQuery!!.contains("watch?v=")) { - inputQuery = inputQuery!!.substring(8) - } - el = inputQuery!!.split("&".toRegex()).dropLastWhile { it.isEmpty() } - .toTypedArray() - inputQuery = el[0] - el = inputQuery!!.split("\\?".toRegex()).dropLastWhile { it.isEmpty() } - .toTypedArray() - inputQuery = el[0] - try { - val v = infoUtil!!.getVideo(inputQuery!!) - val res = ArrayList() - res.add(v) - Handler(Looper.getMainLooper()).post { - if (resetResults) resultViewModel.deleteAll() - resultViewModel.insert(res) - } - } catch (e: Exception) { - Log.e(TAG, e.toString()) - Handler(Looper.getMainLooper()).post { - shimmerCards!!.stopShimmer() - shimmerCards!!.visibility = View.GONE - } - } - } - "Playlist" -> { - inputQuery = - inputQuery!!.split("list=".toRegex()).dropLastWhile { it.isEmpty() } - .toTypedArray()[1] - var nextPageToken = "" - if (resetResults) resultViewModel.deleteAll() - do { - val tmp = infoUtil!!.getPlaylist(inputQuery!!, nextPageToken) - val tmp_vids = tmp.videos - val tmp_token = tmp.nextPageToken - Handler(Looper.getMainLooper()).post { - resultViewModel.insert(tmp_vids) - } - if (tmp_token.isEmpty()) break - if (tmp_token == nextPageToken) break - nextPageToken = tmp_token - } while (true) - } - "Default" -> { - try { - val video = infoUtil!!.getFromYTDL( - inputQuery!! - ) - Handler(Looper.getMainLooper()).post { - if (resetResults) resultViewModel.deleteAll() - resultViewModel.insert(video) - } - Handler(Looper.getMainLooper()).post { - shimmerCards!!.stopShimmer() - shimmerCards!!.visibility = View.GONE - } - } catch (e: Exception) { - Log.e(TAG, e.toString()) - } - } - } - } catch (e: Exception) { - Log.e(TAG, e.toString()) - } - databaseManager!!.close() - } - - fun findVideo(url: String): ResultItem? { + private fun findVideo(url: String): ResultItem? { for (i in resultsList!!.indices) { val v = resultsList!![i] if (v!!.url == url) { @@ -524,23 +410,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi // } } - private val isStoragePermissionGranted: Boolean - get() = if (ActivityCompat.checkSelfPermission( - requireContext(), - Manifest.permission.WRITE_EXTERNAL_STORAGE - ) - == PackageManager.PERMISSION_GRANTED - ) { - true - } else { - downloadQueue = ArrayList() - ActivityCompat.requestPermissions( - requireActivity(), - arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), - 1 - ) - false - } fun updateDownloadStatusOnResult(v: ResultItem?, type: String?, downloaded: Boolean) { // if (v != null) { @@ -571,19 +440,18 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi val item = resultsList!![position] if (add) selectedObjects!!.add(item!!) else selectedObjects!!.remove(item) if (selectedObjects!!.size > 1) { - downloadAllFabCoordinator!!.visibility = View.GONE - downloadFabs!!.visibility = View.VISIBLE + downloadAllFabCoordinator!!.visibility = GONE + downloadFabs!!.visibility = VISIBLE } else { - downloadFabs!!.visibility = View.GONE + downloadFabs!!.visibility = GONE if (resultsList!!.size > 1 && resultsList!![1]!!.playlistTitle.isNotEmpty()) { - downloadAllFabCoordinator!!.visibility = View.VISIBLE + downloadAllFabCoordinator!!.visibility = VISIBLE } } } override fun onClick(v: View) { - val viewIdName: String - viewIdName = try { + val viewIdName: String = try { v.tag.toString() } catch (e: Exception) { "" @@ -600,12 +468,12 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi //remove previously selected for (i in selectedObjects!!.indices) { val vid = findVideo( - selectedObjects!![i]!!.url + selectedObjects!![i].url ) homeAdapter!!.notifyItemChanged(resultsList!!.indexOf(vid)) } selectedObjects = ArrayList() - downloadFabs!!.visibility = View.GONE + downloadFabs!!.visibility = GONE bottomSheet = BottomSheetDialog(fragmentContext!!) bottomSheet!!.requestWindowFeature(Window.FEATURE_NO_TITLE) bottomSheet!!.setContentView(R.layout.home_download_all_bottom_sheet) @@ -614,13 +482,13 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi val last = bottomSheet!!.findViewById(R.id.last_textinput) last!!.editText!!.setText(resultsList!!.size.toString()) val audio = bottomSheet!!.findViewById