added formats
This commit is contained in:
parent
09bddccb61
commit
c4c4e7eab1
31 changed files with 602 additions and 365 deletions
|
|
@ -9,9 +9,10 @@
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
| Build | Latest Release | Downloads | Contribute |
|
 [](https://github.com/deniscerri/ytdlnis/releases/latest) [](https://github.com/deniscerri/ytdlnis/releases) [](https://hosted.weblate.org/engage/ytdlnis/?utm_source=widget) [](https://discord.gg/WW3KYWxAPm)
|
||||||
|-------|----------|------------|------------|
|
|
||||||
|  | [](https://github.com/deniscerri/ytdlnis/releases/latest) | [](https://github.com/deniscerri/ytdlnis/releases) | [](https://hosted.weblate.org/engage/ytdlnis/?utm_source=widget) |
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,9 +89,6 @@ android {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
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:library:$youtubedlAndroidVer"
|
||||||
implementation "com.github.junkfood02.youtubedl-android:ffmpeg:$youtubedlAndroidVer"
|
implementation "com.github.junkfood02.youtubedl-android:ffmpeg:$youtubedlAndroidVer"
|
||||||
implementation "com.github.junkfood02.youtubedl-android:aria2c:$youtubedlAndroidVer"
|
implementation "com.github.junkfood02.youtubedl-android:aria2c:$youtubedlAndroidVer"
|
||||||
|
|
@ -122,10 +119,10 @@ dependencies {
|
||||||
|
|
||||||
implementation "androidx.work:work-runtime-ktx:$workVer"
|
implementation "androidx.work:work-runtime-ktx:$workVer"
|
||||||
|
|
||||||
implementation "androidx.room:room-runtime:2.4.3"
|
implementation "androidx.room:room-runtime:2.5.0"
|
||||||
implementation "androidx.room:room-ktx:2.4.3"
|
implementation "androidx.room:room-ktx:2.5.0"
|
||||||
kapt "androidx.room:room-compiler:2.4.3"
|
kapt "androidx.room:room-compiler:2.5.0"
|
||||||
androidTestImplementation "androidx.room:room-testing:2.4.3"
|
androidTestImplementation "androidx.room:room-testing:2.5.0"
|
||||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
|
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
|
||||||
implementation "androidx.compose.runtime:runtime:$composeVer"
|
implementation "androidx.compose.runtime:runtime:$composeVer"
|
||||||
androidTestImplementation "com.google.truth:truth:1.1.3"
|
androidTestImplementation "com.google.truth:truth:1.1.3"
|
||||||
|
|
@ -137,4 +134,6 @@ dependencies {
|
||||||
implementation "com.squareup.retrofit2:converter-gson:$retrofitVer"
|
implementation "com.squareup.retrofit2:converter-gson:$retrofitVer"
|
||||||
implementation "com.squareup.okhttp3:okhttp:5.0.0-alpha.10"
|
implementation "com.squareup.okhttp3:okhttp:5.0.0-alpha.10"
|
||||||
implementation "com.squareup.okhttp3:logging-interceptor:4.7.2"
|
implementation "com.squareup.okhttp3:logging-interceptor:4.7.2"
|
||||||
|
|
||||||
|
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1"
|
||||||
}
|
}
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"formatVersion": 1,
|
"formatVersion": 1,
|
||||||
"database": {
|
"database": {
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"identityHash": "1391e7ec82bbacc310cf6f23e3d1f85a",
|
"identityHash": "6c48acc2155f9a895971bd84c84b7d4e",
|
||||||
"entities": [
|
"entities": [
|
||||||
{
|
{
|
||||||
"tableName": "results",
|
"tableName": "results",
|
||||||
|
|
@ -58,10 +58,66 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"primaryKey": {
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
"columnNames": [
|
"columnNames": [
|
||||||
"id"
|
"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": [],
|
"indices": [],
|
||||||
"foreignKeys": []
|
"foreignKeys": []
|
||||||
|
|
@ -132,17 +188,17 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"primaryKey": {
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
"columnNames": [
|
"columnNames": [
|
||||||
"id"
|
"id"
|
||||||
],
|
]
|
||||||
"autoGenerate": true
|
|
||||||
},
|
},
|
||||||
"indices": [],
|
"indices": [],
|
||||||
"foreignKeys": []
|
"foreignKeys": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tableName": "downloads",
|
"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": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldPath": "url",
|
"fieldPath": "url",
|
||||||
|
|
@ -254,6 +310,12 @@
|
||||||
"notNull": true,
|
"notNull": true,
|
||||||
"defaultValue": "'Queued'"
|
"defaultValue": "'Queued'"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "workID",
|
||||||
|
"columnName": "workID",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldPath": "id",
|
"fieldPath": "id",
|
||||||
"columnName": "id",
|
"columnName": "id",
|
||||||
|
|
@ -262,10 +324,10 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"primaryKey": {
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
"columnNames": [
|
"columnNames": [
|
||||||
"id"
|
"id"
|
||||||
],
|
]
|
||||||
"autoGenerate": true
|
|
||||||
},
|
},
|
||||||
"indices": [],
|
"indices": [],
|
||||||
"foreignKeys": []
|
"foreignKeys": []
|
||||||
|
|
@ -294,10 +356,10 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"primaryKey": {
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
"columnNames": [
|
"columnNames": [
|
||||||
"id"
|
"id"
|
||||||
],
|
]
|
||||||
"autoGenerate": true
|
|
||||||
},
|
},
|
||||||
"indices": [],
|
"indices": [],
|
||||||
"foreignKeys": []
|
"foreignKeys": []
|
||||||
|
|
@ -306,7 +368,7 @@
|
||||||
"views": [],
|
"views": [],
|
||||||
"setupQueries": [
|
"setupQueries": [
|
||||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
"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')"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package com.deniscerri.ytdlnis
|
package com.deniscerri.ytdlnis
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.app.Service
|
import android.app.Service
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
|
@ -10,7 +9,6 @@ import android.os.Build
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.work.ExistingWorkPolicy
|
import androidx.work.ExistingWorkPolicy
|
||||||
import androidx.work.OneTimeWorkRequestBuilder
|
import androidx.work.OneTimeWorkRequestBuilder
|
||||||
import androidx.work.WorkManager
|
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.DownloadInfo
|
||||||
import com.deniscerri.ytdlnis.service.IDownloaderListener
|
import com.deniscerri.ytdlnis.service.IDownloaderListener
|
||||||
import com.deniscerri.ytdlnis.service.IDownloaderService
|
import com.deniscerri.ytdlnis.service.IDownloaderService
|
||||||
import com.deniscerri.ytdlnis.ui.CustomCommandActivity
|
|
||||||
import com.deniscerri.ytdlnis.util.FileUtil
|
import com.deniscerri.ytdlnis.util.FileUtil
|
||||||
import com.deniscerri.ytdlnis.util.NotificationUtil
|
|
||||||
import com.deniscerri.ytdlnis.work.FileTransferWorker
|
import com.deniscerri.ytdlnis.work.FileTransferWorker
|
||||||
import com.deniscerri.ytdlnis.work.FileTransferWorker.Companion.downLocation
|
import com.deniscerri.ytdlnis.work.FileTransferWorker.Companion.downLocation
|
||||||
import com.deniscerri.ytdlnis.work.FileTransferWorker.Companion.originDir
|
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.disposables.CompositeDisposable
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import com.deniscerri.ytdlnis.DownloaderService.LocalBinder
|
import com.deniscerri.ytdlnis.DownloaderService.LocalBinder
|
||||||
import com.deniscerri.ytdlnis.database.DatabaseManager
|
|
||||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||||
import com.deniscerri.ytdlnis.databinding.ActivityMainBinding
|
import com.deniscerri.ytdlnis.databinding.ActivityMainBinding
|
||||||
import com.deniscerri.ytdlnis.ui.DownloadsFragment
|
import com.deniscerri.ytdlnis.ui.DownloadsFragment
|
||||||
|
|
@ -331,7 +330,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
grantResults: IntArray
|
grantResults: IntArray
|
||||||
) {
|
) {
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
Log.e(TAG, grantResults[0].toString())
|
|
||||||
for (i in permissions.indices) {
|
for (i in permissions.indices) {
|
||||||
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
|
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
|
||||||
createPermissionRequestDialog()
|
createPermissionRequestDialog()
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import android.app.Activity;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
@ -36,19 +35,16 @@ public class HomeAdapter extends ListAdapter<ResultItem, HomeAdapter.ViewHolder>
|
||||||
this.checkedVideos = new ArrayList<>();
|
this.checkedVideos = new ArrayList<>();
|
||||||
this.onItemClickListener = onItemClickListener;
|
this.onItemClickListener = onItemClickListener;
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
Log.e("TAG", "adapter");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final DiffUtil.ItemCallback<ResultItem> DIFF_CALLBACK = new DiffUtil.ItemCallback<ResultItem>() {
|
private static final DiffUtil.ItemCallback<ResultItem> DIFF_CALLBACK = new DiffUtil.ItemCallback<ResultItem>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean areItemsTheSame(@NonNull ResultItem oldItem, @NonNull ResultItem newItem) {
|
public boolean areItemsTheSame(@NonNull ResultItem oldItem, @NonNull ResultItem newItem) {
|
||||||
Log.e("TAG", "adapter2");
|
|
||||||
return oldItem.getId() == newItem.getId();
|
return oldItem.getId() == newItem.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean areContentsTheSame(@NonNull ResultItem oldItem, @NonNull ResultItem newItem) {
|
public boolean areContentsTheSame(@NonNull ResultItem oldItem, @NonNull ResultItem newItem) {
|
||||||
Log.e("TAG", "adapter3");
|
|
||||||
return oldItem.getUrl().equals(newItem.getUrl());
|
return oldItem.getUrl().equals(newItem.getUrl());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -79,7 +75,6 @@ public class HomeAdapter extends ListAdapter<ResultItem, HomeAdapter.ViewHolder>
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||||
ResultItem video = getItem(position);
|
ResultItem video = getItem(position);
|
||||||
Log.e("TAG", "adapter4");
|
|
||||||
MaterialCardView card = holder.cardView;
|
MaterialCardView card = holder.cardView;
|
||||||
// THUMBNAIL ----------------------------------
|
// THUMBNAIL ----------------------------------
|
||||||
ImageView thumbnail = card.findViewById(R.id.result_image_view);
|
ImageView thumbnail = card.findViewById(R.id.result_image_view);
|
||||||
|
|
|
||||||
|
|
@ -4,23 +4,18 @@ import android.content.Context
|
||||||
import androidx.room.Database
|
import androidx.room.Database
|
||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
import androidx.room.RoomDatabase
|
import androidx.room.RoomDatabase
|
||||||
import com.deniscerri.ytdlnis.database.dao.CommandTemplateDao
|
import com.deniscerri.ytdlnis.database.dao.*
|
||||||
import com.deniscerri.ytdlnis.database.dao.DownloadDao
|
import com.deniscerri.ytdlnis.database.models.*
|
||||||
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
|
|
||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
entities = [ResultItem::class, HistoryItem::class, DownloadItem::class, CommandTemplate::class],
|
entities = [ResultItem::class, Format::class, HistoryItem::class, DownloadItem::class, CommandTemplate::class],
|
||||||
version = 1,
|
version = 1,
|
||||||
autoMigrations = []
|
autoMigrations = []
|
||||||
)
|
)
|
||||||
abstract class DBManager : RoomDatabase(){
|
abstract class DBManager : RoomDatabase(){
|
||||||
abstract val resultDao : ResultDao
|
abstract val resultDao : ResultDao
|
||||||
abstract val historyDao : HistoryDao
|
abstract val historyDao : HistoryDao
|
||||||
|
abstract val formatDao : FormatDao
|
||||||
abstract val downloadDao : DownloadDao
|
abstract val downloadDao : DownloadDao
|
||||||
abstract val commandTemplateDao : CommandTemplateDao
|
abstract val commandTemplateDao : CommandTemplateDao
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,7 @@ import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
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.HistoryItem;
|
||||||
import com.deniscerri.ytdlnis.database.models.ResultItem;
|
import com.deniscerri.ytdlnis.database.models.ResultItem;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,11 @@ interface DownloadDao {
|
||||||
@Query("SELECT * FROM downloads")
|
@Query("SELECT * FROM downloads")
|
||||||
fun getAllDownloads() : LiveData<List<DownloadItem>>
|
fun getAllDownloads() : LiveData<List<DownloadItem>>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status='Queued'")
|
@Query("SELECT * FROM downloads WHERE status='Active'")
|
||||||
fun getQueuedDownloads() : List<DownloadItem>
|
fun getActiveDownloads() : LiveData<List<DownloadItem>>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM downloads WHERE workID=:workID")
|
||||||
|
fun getDownloadsByWorkId(workID: Int) : List<DownloadItem>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE id=:id LIMIT 1")
|
@Query("SELECT * FROM downloads WHERE id=:id LIMIT 1")
|
||||||
fun getDownloadById(id: Int) : DownloadItem
|
fun getDownloadById(id: Int) : DownloadItem
|
||||||
|
|
|
||||||
|
|
@ -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<List<Format>>
|
||||||
|
|
||||||
|
@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)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -3,19 +3,25 @@ package com.deniscerri.ytdlnis.database.dao
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||||
import com.deniscerri.ytdlnis.util.FileUtil
|
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface ResultDao {
|
interface ResultDao {
|
||||||
@Query("SELECT * FROM results")
|
@Query("SELECT * FROM results")
|
||||||
fun getResults() : LiveData<List<ResultItem>>
|
fun getResults() : LiveData<List<ResultItem>>
|
||||||
|
|
||||||
|
@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)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
suspend fun insert(item: ResultItem)
|
suspend fun insert(item: ResultItem) : Long
|
||||||
|
|
||||||
@Update(onConflict = OnConflictStrategy.REPLACE)
|
@Update(onConflict = OnConflictStrategy.REPLACE)
|
||||||
suspend fun update(item: ResultItem)
|
suspend fun update(item: ResultItem)
|
||||||
|
|
||||||
@Query("DELETE FROM results")
|
@Query("DELETE FROM results")
|
||||||
suspend fun deleteAll()
|
suspend fun deleteAll()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -25,7 +25,8 @@ data class DownloadItem(
|
||||||
val downloadSize: String,
|
val downloadSize: String,
|
||||||
val playlistTitle: String,
|
val playlistTitle: String,
|
||||||
@ColumnInfo(defaultValue = "Queued")
|
@ColumnInfo(defaultValue = "Queued")
|
||||||
var status: String
|
val status: String,
|
||||||
|
val workID: Int
|
||||||
){
|
){
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
var id: Int? = null
|
var id: Int? = null
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -2,9 +2,6 @@ package com.deniscerri.ytdlnis.database.models
|
||||||
|
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
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")
|
@Entity(tableName = "results")
|
||||||
data class ResultItem(
|
data class ResultItem(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
package com.deniscerri.ytdlnis.database.models
|
||||||
|
|
||||||
|
data class ResultItemWithFormats(
|
||||||
|
val resultItem: ResultItem,
|
||||||
|
var formats: ArrayList<Format>
|
||||||
|
)
|
||||||
|
|
@ -6,6 +6,7 @@ import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||||
|
|
||||||
class DownloadRepository(private val downloadDao: DownloadDao) {
|
class DownloadRepository(private val downloadDao: DownloadDao) {
|
||||||
val allDownloads : LiveData<List<DownloadItem>> = downloadDao.getAllDownloads()
|
val allDownloads : LiveData<List<DownloadItem>> = downloadDao.getAllDownloads()
|
||||||
|
val activeDownloads : LiveData<List<DownloadItem>> = downloadDao.getActiveDownloads()
|
||||||
|
|
||||||
suspend fun insert(item: DownloadItem){
|
suspend fun insert(item: DownloadItem){
|
||||||
downloadDao.insert(item)
|
downloadDao.insert(item)
|
||||||
|
|
@ -19,13 +20,8 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
|
||||||
downloadDao.update(item)
|
downloadDao.update(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getQueuedDownloads() : List<DownloadItem>{
|
fun getDownloadsByWorkID(workID: Int) : List<DownloadItem>{
|
||||||
val items = downloadDao.getQueuedDownloads()
|
return downloadDao.getDownloadsByWorkId(workID)
|
||||||
for (i in items){
|
|
||||||
i.status = "Downloading"
|
|
||||||
downloadDao.update(i)
|
|
||||||
}
|
|
||||||
return items
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,118 @@
|
||||||
package com.deniscerri.ytdlnis.database.repository
|
package com.deniscerri.ytdlnis.database.repository
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
import androidx.lifecycle.LiveData
|
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.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.database.models.ResultItem
|
||||||
import com.deniscerri.ytdlnis.util.InfoUtil
|
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<List<ResultItem>> = resultDao.getResults()
|
val allResults : LiveData<List<ResultItem>> = resultDao.getResults()
|
||||||
|
|
||||||
suspend fun insert(it: ResultItem){
|
suspend fun insert(it: ResultItem){
|
||||||
resultDao.insert(it)
|
resultDao.insert(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateTrending(context: Context){
|
fun getFirstResult() : ResultItem{
|
||||||
resultDao.deleteAll();
|
return resultDao.getFirstResult()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun updateTrending(){
|
||||||
|
deleteAll()
|
||||||
val infoUtil = InfoUtil(context)
|
val infoUtil = InfoUtil(context)
|
||||||
val items = infoUtil.getTrending(context)
|
val items = infoUtil.getTrending(context)
|
||||||
for (i in items){
|
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<String?> =
|
||||||
|
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<Format>, id: Long){
|
||||||
|
formats.forEach { f ->
|
||||||
|
if (f.filesize == 0.toLong()) return@forEach
|
||||||
|
f.itemId = id.toInt()
|
||||||
|
formatDao.insert(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteAll(){
|
suspend fun deleteAll(){
|
||||||
resultDao.deleteAll()
|
resultDao.deleteAll()
|
||||||
|
formatDao.deleteAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun update(item: ResultItem){
|
suspend fun update(item: ResultItem){
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,13 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
allDownloads = repository.allDownloads
|
allDownloads = repository.allDownloads
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun startWork(items: List<DownloadItem>){
|
||||||
|
items.forEach {
|
||||||
|
insertDownload(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
fun insertDownload(item: DownloadItem) = viewModelScope.launch(Dispatchers.IO){
|
fun insertDownload(item: DownloadItem) = viewModelScope.launch(Dispatchers.IO){
|
||||||
repository.insert(item)
|
repository.insert(item)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,76 @@
|
||||||
package com.deniscerri.ytdlnis.database.viewmodel
|
package com.deniscerri.ytdlnis.database.viewmodel
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import android.util.Log
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.*
|
||||||
import androidx.lifecycle.MediatorLiveData
|
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import com.deniscerri.ytdlnis.database.DBManager
|
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.models.ResultItem
|
||||||
import com.deniscerri.ytdlnis.database.repository.ResultRepository
|
import com.deniscerri.ytdlnis.database.repository.ResultRepository
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
class ResultViewModel(application: Application) : AndroidViewModel(application) {
|
class ResultViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
|
private val tag: String = "ResultViewModel"
|
||||||
private val repository : ResultRepository
|
private val repository : ResultRepository
|
||||||
val items : LiveData<List<ResultItem>>
|
val items : LiveData<List<ResultItem>>
|
||||||
|
val loadingItems = MutableLiveData<Boolean>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val dao = DBManager.getInstance(application).resultDao
|
val dao = DBManager.getInstance(application).resultDao
|
||||||
repository = ResultRepository(dao)
|
val formatDao = DBManager.getInstance(application).formatDao
|
||||||
|
repository = ResultRepository(dao, formatDao, getApplication<Application>().applicationContext)
|
||||||
items = repository.allResults
|
items = repository.allResults
|
||||||
|
loadingItems.postValue(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkTrending() : Boolean{
|
fun checkTrending() = viewModelScope.launch(Dispatchers.IO){
|
||||||
return items.value?.get(0)?.playlistTitle.equals("ytdlnis-TRENDING");
|
try {
|
||||||
|
val item = repository.getFirstResult()
|
||||||
|
if (item.playlistTitle == "ytdlnis-TRENDING") getTrending()
|
||||||
|
}catch (e : Exception){
|
||||||
|
getTrending()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fun getTrending() = viewModelScope.launch(Dispatchers.IO){
|
fun getTrending() = viewModelScope.launch(Dispatchers.IO){
|
||||||
repository.updateTrending(getApplication<Application>().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<ResultItem?>) = viewModelScope.launch(Dispatchers.IO){
|
fun insert(items: ArrayList<ResultItem?>) = viewModelScope.launch(Dispatchers.IO){
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
package com.deniscerri.ytdlnis.service
|
package com.deniscerri.ytdlnis.service
|
||||||
|
|
||||||
import androidx.room.Room
|
|
||||||
import com.deniscerri.ytdlnis.database.DBManager
|
|
||||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||||
import com.google.gson.JsonArray
|
import com.google.gson.JsonArray
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,5 @@
|
||||||
package com.deniscerri.ytdlnis.service
|
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.JsonArray
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,19 @@
|
||||||
package com.deniscerri.ytdlnis.ui
|
package com.deniscerri.ytdlnis.ui
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.text.InputType
|
import android.text.InputType
|
||||||
import android.util.Log
|
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import android.view.View.GONE
|
import android.view.View.GONE
|
||||||
import android.view.View.VISIBLE
|
import android.view.View.VISIBLE
|
||||||
import android.widget.*
|
import android.widget.*
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
import androidx.core.app.ActivityCompat
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
|
@ -27,7 +23,6 @@ import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.adapter.HomeAdapter
|
import com.deniscerri.ytdlnis.adapter.HomeAdapter
|
||||||
import com.deniscerri.ytdlnis.database.DatabaseManager
|
import com.deniscerri.ytdlnis.database.DatabaseManager
|
||||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
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.database.viewmodel.ResultViewModel
|
||||||
import com.deniscerri.ytdlnis.databinding.FragmentDownloadsBinding
|
import com.deniscerri.ytdlnis.databinding.FragmentDownloadsBinding
|
||||||
import com.deniscerri.ytdlnis.service.DownloadInfo
|
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.progressindicator.LinearProgressIndicator
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.regex.Pattern
|
|
||||||
import kotlin.collections.ArrayList
|
|
||||||
|
|
||||||
class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickListener {
|
class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickListener {
|
||||||
private var progressBar: LinearProgressIndicator? = null
|
private var progressBar: LinearProgressIndicator? = null
|
||||||
|
|
@ -75,7 +68,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
|
||||||
private var topAppBar: MaterialToolbar? = null
|
private var topAppBar: MaterialToolbar? = null
|
||||||
private var recyclerView: RecyclerView? = null
|
private var recyclerView: RecyclerView? = null
|
||||||
private var bottomSheet: BottomSheetDialog? = null
|
private var bottomSheet: BottomSheetDialog? = null
|
||||||
private var sortSheet: BottomSheetDialog? = null
|
|
||||||
private var uiHandler: Handler? = null
|
private var uiHandler: Handler? = null
|
||||||
private var noResults: RelativeLayout? = null
|
private var noResults: RelativeLayout? = null
|
||||||
private var selectionChips: LinearLayout? = null
|
private var selectionChips: LinearLayout? = null
|
||||||
|
|
@ -116,7 +108,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
|
||||||
selectedObjects = ArrayList()
|
selectedObjects = ArrayList()
|
||||||
downloading = mainActivity!!.isDownloadServiceRunning()
|
downloading = mainActivity!!.isDownloadServiceRunning()
|
||||||
|
|
||||||
|
|
||||||
downloadQueue = ArrayList()
|
downloadQueue = ArrayList()
|
||||||
resultsList = mutableListOf()
|
resultsList = mutableListOf()
|
||||||
selectedObjects = ArrayList()
|
selectedObjects = ArrayList()
|
||||||
|
|
@ -140,8 +131,23 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
|
||||||
resultViewModel.items.observe(viewLifecycleOwner) {
|
resultViewModel.items.observe(viewLifecycleOwner) {
|
||||||
homeAdapter!!.submitList(it)
|
homeAdapter!!.submitList(it)
|
||||||
resultsList = it
|
resultsList = it
|
||||||
shimmerCards!!.stopShimmer()
|
if(it.size > 1){
|
||||||
shimmerCards!!.visibility = GONE
|
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()
|
initMenu()
|
||||||
|
|
@ -163,12 +169,11 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
|
||||||
if (inputQueries != null) {
|
if (inputQueries != null) {
|
||||||
resultViewModel.deleteAll()
|
resultViewModel.deleteAll()
|
||||||
inputQueriesLength = inputQueries!!.size
|
inputQueriesLength = inputQueries!!.size
|
||||||
shimmerCards!!.startShimmer()
|
Handler(Looper.getMainLooper()).post { scrollToTop() }
|
||||||
shimmerCards!!.visibility = VISIBLE
|
|
||||||
val thread = Thread {
|
val thread = Thread {
|
||||||
while (!inputQueries!!.isEmpty()) {
|
while (!inputQueries!!.isEmpty()) {
|
||||||
inputQuery = inputQueries!!.pop()
|
inputQuery = inputQueries!!.pop()
|
||||||
parseQuery(false)
|
resultViewModel.parseQuery(inputQuery!!, false)
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Handler(Looper.getMainLooper()).post {
|
Handler(Looper.getMainLooper()).post {
|
||||||
|
|
@ -187,11 +192,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
|
||||||
}
|
}
|
||||||
thread.start()
|
thread.start()
|
||||||
} else {
|
} else {
|
||||||
if (resultViewModel.checkTrending()){
|
resultViewModel.checkTrending()
|
||||||
shimmerCards!!.startShimmer()
|
|
||||||
shimmerCards!!.visibility = VISIBLE
|
|
||||||
resultViewModel.getTrending()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -261,16 +262,16 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
|
||||||
val onActionExpandListener: MenuItem.OnActionExpandListener =
|
val onActionExpandListener: MenuItem.OnActionExpandListener =
|
||||||
object : MenuItem.OnActionExpandListener {
|
object : MenuItem.OnActionExpandListener {
|
||||||
override fun onMenuItemActionExpand(menuItem: MenuItem): Boolean {
|
override fun onMenuItemActionExpand(menuItem: MenuItem): Boolean {
|
||||||
homeFabs!!.visibility = View.GONE
|
homeFabs!!.visibility = GONE
|
||||||
recyclerView!!.visibility = View.GONE
|
recyclerView!!.visibility = GONE
|
||||||
searchSuggestions!!.visibility = View.VISIBLE
|
searchSuggestions!!.visibility = VISIBLE
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMenuItemActionCollapse(menuItem: MenuItem): Boolean {
|
override fun onMenuItemActionCollapse(menuItem: MenuItem): Boolean {
|
||||||
homeFabs!!.visibility = View.VISIBLE
|
homeFabs!!.visibility = VISIBLE
|
||||||
recyclerView!!.visibility = View.VISIBLE
|
recyclerView!!.visibility = VISIBLE
|
||||||
searchSuggestions!!.visibility = View.GONE
|
searchSuggestions!!.visibility = GONE
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -287,20 +288,12 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
|
||||||
override fun onQueryTextSubmit(query: String): Boolean {
|
override fun onQueryTextSubmit(query: String): Boolean {
|
||||||
topAppBar!!.menu.findItem(R.id.search).collapseActionView()
|
topAppBar!!.menu.findItem(R.id.search).collapseActionView()
|
||||||
downloadAllFabCoordinator!!.visibility = GONE
|
downloadAllFabCoordinator!!.visibility = GONE
|
||||||
downloadFabs!!.visibility = View.GONE
|
downloadFabs!!.visibility = GONE
|
||||||
selectedObjects = ArrayList()
|
selectedObjects = ArrayList()
|
||||||
inputQuery = query.trim { it <= ' ' }
|
inputQuery = query.trim { it <= ' ' }
|
||||||
shimmerCards!!.startShimmer()
|
|
||||||
shimmerCards!!.visibility = View.VISIBLE
|
|
||||||
resultViewModel.deleteAll()
|
resultViewModel.deleteAll()
|
||||||
val thread = Thread {
|
val thread = Thread {
|
||||||
parseQuery(true)
|
resultViewModel.parseQuery(inputQuery!!, true)
|
||||||
// DOWNLOAD ALL BUTTON
|
|
||||||
if (resultsList!!.size > 1 && resultsList!![1]!!.playlistTitle.isNotEmpty()) {
|
|
||||||
Handler(Looper.getMainLooper()).post {
|
|
||||||
downloadAllFabCoordinator!!.visibility = View.VISIBLE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
thread.start()
|
thread.start()
|
||||||
return true
|
return true
|
||||||
|
|
@ -335,14 +328,14 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
topAppBar!!.setOnClickListener { view: View? -> scrollToTop() }
|
topAppBar!!.setOnClickListener { scrollToTop() }
|
||||||
topAppBar!!.setOnMenuItemClickListener { m: MenuItem ->
|
topAppBar!!.setOnMenuItemClickListener { m: MenuItem ->
|
||||||
val itemId = m.itemId
|
val itemId = m.itemId
|
||||||
if (itemId == R.id.delete_results) {
|
if (itemId == R.id.delete_results) {
|
||||||
resultViewModel.getTrending()
|
resultViewModel.getTrending()
|
||||||
selectedObjects = ArrayList()
|
selectedObjects = ArrayList()
|
||||||
downloadAllFabCoordinator!!.visibility = View.GONE
|
downloadAllFabCoordinator!!.visibility = GONE
|
||||||
downloadFabs!!.visibility = View.GONE
|
downloadFabs!!.visibility = GONE
|
||||||
}
|
}
|
||||||
// } else if (itemId == R.id.cancel_download) {
|
// } else if (itemId == R.id.cancel_download) {
|
||||||
// try {
|
// try {
|
||||||
|
|
@ -376,114 +369,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
|
||||||
(topAppBar!!.parent as AppBarLayout).setExpanded(true, true)
|
(topAppBar!!.parent as AppBarLayout).setExpanded(true, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseQuery(resetResults: Boolean) {
|
private fun findVideo(url: String): ResultItem? {
|
||||||
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<String?> =
|
|
||||||
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<ResultItem?>()
|
|
||||||
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? {
|
|
||||||
for (i in resultsList!!.indices) {
|
for (i in resultsList!!.indices) {
|
||||||
val v = resultsList!![i]
|
val v = resultsList!![i]
|
||||||
if (v!!.url == url) {
|
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) {
|
fun updateDownloadStatusOnResult(v: ResultItem?, type: String?, downloaded: Boolean) {
|
||||||
// if (v != null) {
|
// if (v != null) {
|
||||||
|
|
@ -571,19 +440,18 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
|
||||||
val item = resultsList!![position]
|
val item = resultsList!![position]
|
||||||
if (add) selectedObjects!!.add(item!!) else selectedObjects!!.remove(item)
|
if (add) selectedObjects!!.add(item!!) else selectedObjects!!.remove(item)
|
||||||
if (selectedObjects!!.size > 1) {
|
if (selectedObjects!!.size > 1) {
|
||||||
downloadAllFabCoordinator!!.visibility = View.GONE
|
downloadAllFabCoordinator!!.visibility = GONE
|
||||||
downloadFabs!!.visibility = View.VISIBLE
|
downloadFabs!!.visibility = VISIBLE
|
||||||
} else {
|
} else {
|
||||||
downloadFabs!!.visibility = View.GONE
|
downloadFabs!!.visibility = GONE
|
||||||
if (resultsList!!.size > 1 && resultsList!![1]!!.playlistTitle.isNotEmpty()) {
|
if (resultsList!!.size > 1 && resultsList!![1]!!.playlistTitle.isNotEmpty()) {
|
||||||
downloadAllFabCoordinator!!.visibility = View.VISIBLE
|
downloadAllFabCoordinator!!.visibility = VISIBLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(v: View) {
|
override fun onClick(v: View) {
|
||||||
val viewIdName: String
|
val viewIdName: String = try {
|
||||||
viewIdName = try {
|
|
||||||
v.tag.toString()
|
v.tag.toString()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
""
|
""
|
||||||
|
|
@ -600,12 +468,12 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
|
||||||
//remove previously selected
|
//remove previously selected
|
||||||
for (i in selectedObjects!!.indices) {
|
for (i in selectedObjects!!.indices) {
|
||||||
val vid = findVideo(
|
val vid = findVideo(
|
||||||
selectedObjects!![i]!!.url
|
selectedObjects!![i].url
|
||||||
)
|
)
|
||||||
homeAdapter!!.notifyItemChanged(resultsList!!.indexOf(vid))
|
homeAdapter!!.notifyItemChanged(resultsList!!.indexOf(vid))
|
||||||
}
|
}
|
||||||
selectedObjects = ArrayList()
|
selectedObjects = ArrayList()
|
||||||
downloadFabs!!.visibility = View.GONE
|
downloadFabs!!.visibility = GONE
|
||||||
bottomSheet = BottomSheetDialog(fragmentContext!!)
|
bottomSheet = BottomSheetDialog(fragmentContext!!)
|
||||||
bottomSheet!!.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
bottomSheet!!.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||||
bottomSheet!!.setContentView(R.layout.home_download_all_bottom_sheet)
|
bottomSheet!!.setContentView(R.layout.home_download_all_bottom_sheet)
|
||||||
|
|
@ -614,13 +482,13 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
|
||||||
val last = bottomSheet!!.findViewById<TextInputLayout>(R.id.last_textinput)
|
val last = bottomSheet!!.findViewById<TextInputLayout>(R.id.last_textinput)
|
||||||
last!!.editText!!.setText(resultsList!!.size.toString())
|
last!!.editText!!.setText(resultsList!!.size.toString())
|
||||||
val audio = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_audio_button)
|
val audio = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_audio_button)
|
||||||
audio!!.setOnClickListener { view: View? ->
|
audio!!.setOnClickListener {
|
||||||
val start = first.editText!!.text.toString().toInt()
|
val start = first.editText!!.text.toString().toInt()
|
||||||
val end = last.editText!!.text.toString().toInt()
|
val end = last.editText!!.text.toString().toInt()
|
||||||
initDownloadAll(bottomSheet!!, start, end, "audio")
|
initDownloadAll(bottomSheet!!, start, end, "audio")
|
||||||
}
|
}
|
||||||
val video = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_video_button)
|
val video = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_video_button)
|
||||||
video!!.setOnClickListener { view: View? ->
|
video!!.setOnClickListener {
|
||||||
val start = first.editText!!.text.toString().toInt()
|
val start = first.editText!!.text.toString().toInt()
|
||||||
val end = last.editText!!.text.toString().toInt()
|
val end = last.editText!!.text.toString().toInt()
|
||||||
initDownloadAll(bottomSheet!!, start, end, "video")
|
initDownloadAll(bottomSheet!!, start, end, "video")
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import android.webkit.MimeTypeMap
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,13 @@ import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.DatabaseManager
|
import com.deniscerri.ytdlnis.database.DatabaseManager
|
||||||
|
import com.deniscerri.ytdlnis.database.models.Format
|
||||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||||
|
import com.deniscerri.ytdlnis.database.models.ResultItemWithFormats
|
||||||
|
import com.google.gson.Gson
|
||||||
import com.yausername.youtubedl_android.YoutubeDL
|
import com.yausername.youtubedl_android.YoutubeDL
|
||||||
import com.yausername.youtubedl_android.YoutubeDLRequest
|
import com.yausername.youtubedl_android.YoutubeDLRequest
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
@ -19,7 +23,7 @@ import java.util.*
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
class InfoUtil(context: Context) {
|
class InfoUtil(context: Context) {
|
||||||
private var items: ArrayList<ResultItem?>
|
private var items: ArrayList<ResultItemWithFormats?>
|
||||||
private var key: String? = null
|
private var key: String? = null
|
||||||
private var useInvidous = false
|
private var useInvidous = false
|
||||||
private var databaseManager: DatabaseManager? = null
|
private var databaseManager: DatabaseManager? = null
|
||||||
|
|
@ -52,7 +56,7 @@ class InfoUtil(context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(JSONException::class)
|
@Throws(JSONException::class)
|
||||||
fun search(query: String): ArrayList<ResultItem?> {
|
fun search(query: String): ArrayList<ResultItemWithFormats?> {
|
||||||
init()
|
init()
|
||||||
items = ArrayList()
|
items = ArrayList()
|
||||||
return if (key!!.isEmpty()) {
|
return if (key!!.isEmpty()) {
|
||||||
|
|
@ -61,7 +65,7 @@ class InfoUtil(context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(JSONException::class)
|
@Throws(JSONException::class)
|
||||||
fun searchFromKey(query: String): ArrayList<ResultItem?> {
|
fun searchFromKey(query: String): ArrayList<ResultItemWithFormats?> {
|
||||||
//short data
|
//short data
|
||||||
val res =
|
val res =
|
||||||
genericRequest("https://youtube.googleapis.com/youtube/v3/search?part=snippet&q=$query&maxResults=25®ionCode=$countryCODE&key=$key")
|
genericRequest("https://youtube.googleapis.com/youtube/v3/search?part=snippet&q=$query&maxResults=25®ionCode=$countryCODE&key=$key")
|
||||||
|
|
@ -100,7 +104,7 @@ class InfoUtil(context: Context) {
|
||||||
snippet.put("duration", duration)
|
snippet.put("duration", duration)
|
||||||
fixThumbnail(snippet)
|
fixThumbnail(snippet)
|
||||||
val v = createVideofromJSON(snippet)
|
val v = createVideofromJSON(snippet)
|
||||||
if (v == null || v.thumb.isEmpty()) {
|
if (v == null || v.resultItem.thumb.isEmpty()) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
items.add(createVideofromJSON(snippet))
|
items.add(createVideofromJSON(snippet))
|
||||||
|
|
@ -110,7 +114,7 @@ class InfoUtil(context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(JSONException::class)
|
@Throws(JSONException::class)
|
||||||
fun searchFromInvidous(query: String): ArrayList<ResultItem?> {
|
fun searchFromInvidous(query: String): ArrayList<ResultItemWithFormats?> {
|
||||||
val dataArray = genericArrayRequest(invidousURL + "search?q=" + query + "?type=video")
|
val dataArray = genericArrayRequest(invidousURL + "search?q=" + query + "?type=video")
|
||||||
if (dataArray.length() == 0) return getFromYTDL(query)
|
if (dataArray.length() == 0) return getFromYTDL(query)
|
||||||
for (i in 0 until dataArray.length()) {
|
for (i in 0 until dataArray.length()) {
|
||||||
|
|
@ -126,7 +130,7 @@ class InfoUtil(context: Context) {
|
||||||
element.getJSONArray("videoThumbnails").getJSONObject(0).getString("url")
|
element.getJSONArray("videoThumbnails").getJSONObject(0).getString("url")
|
||||||
)
|
)
|
||||||
val v = createVideofromInvidiousJSON(element)
|
val v = createVideofromInvidiousJSON(element)
|
||||||
if (v == null || v.thumb.isEmpty()) {
|
if (v == null || v.resultItem.thumb.isEmpty()) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
items.add(v)
|
items.add(v)
|
||||||
|
|
@ -181,13 +185,13 @@ class InfoUtil(context: Context) {
|
||||||
snippet.put("duration", duration)
|
snippet.put("duration", duration)
|
||||||
fixThumbnail(snippet)
|
fixThumbnail(snippet)
|
||||||
val v = createVideofromJSON(snippet)
|
val v = createVideofromJSON(snippet)
|
||||||
if (v == null || v.thumb.isEmpty()) {
|
if (v == null || v.resultItem.thumb.isEmpty()) {
|
||||||
i++
|
i++
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
j++
|
j++
|
||||||
}
|
}
|
||||||
v.playlistTitle = "YTDLnis"
|
v.resultItem.playlistTitle = "YTDLnis"
|
||||||
items.add(v)
|
items.add(v)
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
@ -207,8 +211,8 @@ class InfoUtil(context: Context) {
|
||||||
for (i in 0 until vids.length()) {
|
for (i in 0 until vids.length()) {
|
||||||
val element = vids.getJSONObject(i)
|
val element = vids.getJSONObject(i)
|
||||||
val v = createVideofromInvidiousJSON(element)
|
val v = createVideofromInvidiousJSON(element)
|
||||||
if (v == null || v.thumb.isEmpty()) continue
|
if (v == null || v.resultItem.thumb.isEmpty()) continue
|
||||||
v.playlistTitle = res.getString("title")
|
v.resultItem.playlistTitle = res.getString("title")
|
||||||
items.add(v)
|
items.add(v)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|
@ -218,7 +222,7 @@ class InfoUtil(context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(JSONException::class)
|
@Throws(JSONException::class)
|
||||||
fun getVideo(id: String): ResultItem? {
|
fun getVideo(id: String): ResultItemWithFormats? {
|
||||||
init()
|
init()
|
||||||
if (key!!.isEmpty()) {
|
if (key!!.isEmpty()) {
|
||||||
return if (useInvidous) {
|
return if (useInvidous) {
|
||||||
|
|
@ -245,8 +249,8 @@ class InfoUtil(context: Context) {
|
||||||
return createVideofromJSON(res)
|
return createVideofromJSON(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createVideofromJSON(obj: JSONObject): ResultItem? {
|
private fun createVideofromJSON(obj: JSONObject): ResultItemWithFormats? {
|
||||||
var video: ResultItem? = null
|
var video: ResultItemWithFormats? = null
|
||||||
try {
|
try {
|
||||||
val id = obj.getString("videoID")
|
val id = obj.getString("videoID")
|
||||||
val title = obj.getString("title").toString()
|
val title = obj.getString("title").toString()
|
||||||
|
|
@ -257,23 +261,26 @@ class InfoUtil(context: Context) {
|
||||||
val downloadedAudio = databaseManager!!.checkDownloaded(url, "audio")
|
val downloadedAudio = databaseManager!!.checkDownloaded(url, "audio")
|
||||||
val downloadedVideo = databaseManager!!.checkDownloaded(url, "video")
|
val downloadedVideo = databaseManager!!.checkDownloaded(url, "video")
|
||||||
val isPlaylist = 0
|
val isPlaylist = 0
|
||||||
video = ResultItem(
|
video = ResultItemWithFormats(
|
||||||
id,
|
ResultItem(
|
||||||
url,
|
url,
|
||||||
title,
|
title,
|
||||||
author,
|
author,
|
||||||
duration,
|
duration,
|
||||||
thumb,
|
thumb,
|
||||||
"youtube"
|
"youtube",
|
||||||
)
|
"",
|
||||||
|
),
|
||||||
|
ArrayList()
|
||||||
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, e.toString())
|
Log.e(TAG, e.toString())
|
||||||
}
|
}
|
||||||
return video
|
return video
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createVideofromInvidiousJSON(obj: JSONObject): ResultItem? {
|
private fun createVideofromInvidiousJSON(obj: JSONObject): ResultItemWithFormats? {
|
||||||
var video: ResultItem? = null
|
var video: ResultItemWithFormats? = null
|
||||||
try {
|
try {
|
||||||
val id = obj.getString("videoId")
|
val id = obj.getString("videoId")
|
||||||
val title = obj.getString("title").toString()
|
val title = obj.getString("title").toString()
|
||||||
|
|
@ -281,17 +288,17 @@ class InfoUtil(context: Context) {
|
||||||
val duration = formatIntegerDuration(obj.getInt("lengthSeconds"))
|
val duration = formatIntegerDuration(obj.getInt("lengthSeconds"))
|
||||||
val thumb = "https://i.ytimg.com/vi/$id/hqdefault.jpg"
|
val thumb = "https://i.ytimg.com/vi/$id/hqdefault.jpg"
|
||||||
val url = "https://www.youtube.com/watch?v=$id"
|
val url = "https://www.youtube.com/watch?v=$id"
|
||||||
val downloadedAudio = databaseManager!!.checkDownloaded(url, "audio")
|
video = ResultItemWithFormats(
|
||||||
val downloadedVideo = databaseManager!!.checkDownloaded(url, "video")
|
ResultItem(
|
||||||
val isPlaylist = 0
|
url,
|
||||||
video = ResultItem(
|
title,
|
||||||
id,
|
author,
|
||||||
url,
|
duration,
|
||||||
title,
|
thumb,
|
||||||
author,
|
"youtube",
|
||||||
duration,
|
"",
|
||||||
thumb,
|
),
|
||||||
"youtube"
|
ArrayList()
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, e.toString())
|
Log.e(TAG, e.toString())
|
||||||
|
|
@ -383,7 +390,7 @@ class InfoUtil(context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(JSONException::class)
|
@Throws(JSONException::class)
|
||||||
fun getTrending(context: Context): ArrayList<ResultItem?> {
|
fun getTrending(context: Context): ArrayList<ResultItemWithFormats?> {
|
||||||
init()
|
init()
|
||||||
items = ArrayList()
|
items = ArrayList()
|
||||||
return if (key!!.isEmpty()) {
|
return if (key!!.isEmpty()) {
|
||||||
|
|
@ -392,7 +399,7 @@ class InfoUtil(context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(JSONException::class)
|
@Throws(JSONException::class)
|
||||||
fun getTrendingFromKey(context: Context): ArrayList<ResultItem?> {
|
fun getTrendingFromKey(context: Context): ArrayList<ResultItemWithFormats?> {
|
||||||
val url = "https://www.googleapis.com/youtube/v3/videos?part=snippet&chart=mostPopular&videoCategoryId=10®ionCode=$countryCODE&maxResults=25&key=$key"
|
val url = "https://www.googleapis.com/youtube/v3/videos?part=snippet&chart=mostPopular&videoCategoryId=10®ionCode=$countryCODE&maxResults=25&key=$key"
|
||||||
//short data
|
//short data
|
||||||
val res = genericRequest(url)
|
val res = genericRequest(url)
|
||||||
|
|
@ -412,16 +419,16 @@ class InfoUtil(context: Context) {
|
||||||
snippet.put("duration", duration)
|
snippet.put("duration", duration)
|
||||||
fixThumbnail(snippet)
|
fixThumbnail(snippet)
|
||||||
val v = createVideofromJSON(snippet)
|
val v = createVideofromJSON(snippet)
|
||||||
if (v == null || v.thumb.isEmpty()) {
|
if (v == null || v.resultItem.thumb.isEmpty()) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
v.playlistTitle = context.getString(R.string.trendingPlaylist)
|
v.resultItem.playlistTitle = context.getString(R.string.trendingPlaylist)
|
||||||
items.add(v)
|
items.add(v)
|
||||||
}
|
}
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getTrendingFromInvidous(context: Context): ArrayList<ResultItem?> {
|
private fun getTrendingFromInvidous(context: Context): ArrayList<ResultItemWithFormats?> {
|
||||||
val url = invidousURL + "trending?type=music®ion=" + countryCODE
|
val url = invidousURL + "trending?type=music®ion=" + countryCODE
|
||||||
val res = genericArrayRequest(url)
|
val res = genericArrayRequest(url)
|
||||||
try {
|
try {
|
||||||
|
|
@ -429,8 +436,8 @@ class InfoUtil(context: Context) {
|
||||||
val element = res.getJSONObject(i)
|
val element = res.getJSONObject(i)
|
||||||
if (element.getString("type") != "video") continue
|
if (element.getString("type") != "video") continue
|
||||||
val v = createVideofromInvidiousJSON(element)
|
val v = createVideofromInvidiousJSON(element)
|
||||||
if (v == null || v.thumb.isEmpty()) continue
|
if (v == null || v.resultItem.thumb.isEmpty()) continue
|
||||||
v.playlistTitle = context.getString(R.string.trendingPlaylist)
|
v.resultItem.playlistTitle = context.getString(R.string.trendingPlaylist)
|
||||||
items.add(v)
|
items.add(v)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|
@ -439,7 +446,7 @@ class InfoUtil(context: Context) {
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFromYTDL(query: String): ArrayList<ResultItem?> {
|
fun getFromYTDL(query: String): ArrayList<ResultItemWithFormats?> {
|
||||||
init()
|
init()
|
||||||
items = ArrayList()
|
items = ArrayList()
|
||||||
try {
|
try {
|
||||||
|
|
@ -488,8 +495,16 @@ class InfoUtil(context: Context) {
|
||||||
}
|
}
|
||||||
val website = if (jsonObject.has("ie_key")) jsonObject.getString("ie_key") else jsonObject.getString("extractor")
|
val website = if (jsonObject.has("ie_key")) jsonObject.getString("ie_key") else jsonObject.getString("extractor")
|
||||||
var playlistTitle: String? = ""
|
var playlistTitle: String? = ""
|
||||||
if (jsonObject.has("playlist")) playlistTitle = jsonObject.getString("playlist")
|
if (jsonObject.has("playlist_title")) playlistTitle = jsonObject.getString("playlist_title")
|
||||||
items.add(
|
val formatsInJSON = if (jsonObject.has("formats")) jsonObject.getJSONArray("formats") else null
|
||||||
|
val formats : ArrayList<Format> = ArrayList()
|
||||||
|
if (formatsInJSON != null) {
|
||||||
|
for (f in 0 until formatsInJSON.length()){
|
||||||
|
val format = formatsInJSON.getJSONObject(f).toString()
|
||||||
|
formats.add(Gson().fromJson(format, Format::class.java))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
items.add(ResultItemWithFormats(
|
||||||
ResultItem(
|
ResultItem(
|
||||||
url,
|
url,
|
||||||
title,
|
title,
|
||||||
|
|
@ -498,6 +513,8 @@ class InfoUtil(context: Context) {
|
||||||
thumb!!,
|
thumb!!,
|
||||||
website,
|
website,
|
||||||
playlistTitle!!
|
playlistTitle!!
|
||||||
|
),
|
||||||
|
formats
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -507,6 +524,10 @@ class InfoUtil(context: Context) {
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val jsonFormat = Json {
|
||||||
|
ignoreUnknownKeys = true
|
||||||
|
}
|
||||||
|
|
||||||
fun getSearchSuggestions(query: String): ArrayList<String> {
|
fun getSearchSuggestions(query: String): ArrayList<String> {
|
||||||
if (!useInvidous) return ArrayList()
|
if (!useInvidous) return ArrayList()
|
||||||
val url = invidousURL + "search/suggestions?q=" + query
|
val url = invidousURL + "search/suggestions?q=" + query
|
||||||
|
|
@ -580,12 +601,12 @@ class InfoUtil(context: Context) {
|
||||||
|
|
||||||
class PlaylistTuple internal constructor(
|
class PlaylistTuple internal constructor(
|
||||||
var nextPageToken: String,
|
var nextPageToken: String,
|
||||||
var videos: ArrayList<ResultItem?>
|
var videos: ArrayList<ResultItemWithFormats?>
|
||||||
)
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "API MANAGER"
|
private const val TAG = "API MANAGER"
|
||||||
private const val invidousURL = "https://invidious.baczek.me/api/v1/"
|
private const val invidousURL = "https://invidious.baczek.me/api/vvvvvv/"
|
||||||
private var countryCODE: String? = null
|
private var countryCODE: String? = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -41,7 +41,7 @@ class DownloadWorker(
|
||||||
val commandTemplateDao = dbManager.commandTemplateDao
|
val commandTemplateDao = dbManager.commandTemplateDao
|
||||||
val historyDao = dbManager.historyDao
|
val historyDao = dbManager.historyDao
|
||||||
|
|
||||||
queue = repository.getQueuedDownloads() as MutableList<DownloadItem>
|
queue = repository.getDownloadsByWorkID(workID) as MutableList<DownloadItem>
|
||||||
|
|
||||||
val intent = Intent(context, MainActivity::class.java)
|
val intent = Intent(context, MainActivity::class.java)
|
||||||
val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
|
val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@
|
||||||
tools:ignore="MissingConstraints">
|
tools:ignore="MissingConstraints">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="200sp"
|
android:layout_width="150sp"
|
||||||
android:layout_height="150sp"
|
android:layout_height="100sp"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:scaleType="centerInside"
|
android:scaleType="centerInside"
|
||||||
android:background="@drawable/ic_no_results" />
|
android:background="@drawable/ic_no_results" />
|
||||||
|
|
|
||||||
|
|
@ -43,51 +43,56 @@
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:id="@+id/recyclerViewHome"
|
|
||||||
android:nestedScrollingEnabled="false"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:scrollbars="vertical"
|
|
||||||
android:layout_marginLeft="10dp"
|
|
||||||
android:layout_marginRight="10dp"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:paddingBottom="150dp"
|
|
||||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
/>
|
|
||||||
|
|
||||||
|
|
||||||
<com.facebook.shimmer.ShimmerFrameLayout
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:id="@+id/shimmer_results_framelayout"
|
|
||||||
android:layout_marginTop="?attr/actionBarSize"
|
android:layout_marginTop="?attr/actionBarSize"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/shimmer_linear_layout"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
android:id="@+id/recyclerViewHome"
|
||||||
|
android:nestedScrollingEnabled="false"
|
||||||
|
android:orientation="vertical"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="10dp"
|
android:scrollbars="vertical"
|
||||||
|
android:layout_marginLeft="10dp"
|
||||||
|
android:layout_marginRight="10dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingBottom="100dp"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
<com.facebook.shimmer.ShimmerFrameLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:id="@+id/shimmer_results_framelayout"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<include layout="@layout/result_card_shimmer" />
|
<LinearLayout
|
||||||
|
android:id="@+id/shimmer_linear_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
<include layout="@layout/result_card_shimmer" />
|
<include layout="@layout/result_card_shimmer" />
|
||||||
|
|
||||||
<include layout="@layout/result_card_shimmer" />
|
<include layout="@layout/result_card_shimmer" />
|
||||||
|
|
||||||
<include layout="@layout/result_card_shimmer" />
|
<include layout="@layout/result_card_shimmer" />
|
||||||
|
|
||||||
<include layout="@layout/result_card_shimmer" />
|
<include layout="@layout/result_card_shimmer" />
|
||||||
|
|
||||||
|
<include layout="@layout/result_card_shimmer" />
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</com.facebook.shimmer.ShimmerFrameLayout>
|
</com.facebook.shimmer.ShimmerFrameLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:id="@+id/home_fabs"
|
android:id="@+id/home_fabs"
|
||||||
|
|
|
||||||
|
|
@ -6,36 +6,9 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="?attr/actionBarSize"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:id="@+id/preference_coordinatorLayout"
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
|
||||||
android:id="@+id/home_toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="?attr/actionBarSize"
|
|
||||||
app:title="@string/more"
|
|
||||||
app:menu="@menu/empty_menu"
|
|
||||||
/>
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="200dp"
|
android:layout_height="200dp"
|
||||||
android:layout_marginTop="?attr/actionBarSize"
|
|
||||||
android:gravity="top"
|
android:gravity="top"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
tools:ignore="MissingConstraints">
|
tools:ignore="MissingConstraints">
|
||||||
|
|
|
||||||
146
app/src/main/res/layout/home_download_single_bottom_sheet.xml
Normal file
146
app/src/main/res/layout/home_download_single_bottom_sheet.xml
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout android:id="@+id/history_element_bottom_sheet"
|
||||||
|
style="@style/Widget.Material3.BottomSheet"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
>
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/result_card_constraintLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/result_relative_layout"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintDimensionRatio="H,16:9"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:paddingBottom="10dp">
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/result_card_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:cardCornerRadius="10dp"
|
||||||
|
app:cardElevation="10dp"
|
||||||
|
app:cardMaxElevation="12dp"
|
||||||
|
app:cardBackgroundColor="@color/black"
|
||||||
|
app:cardPreventCornerOverlap="true"
|
||||||
|
android:checkable="true"
|
||||||
|
app:strokeWidth="0dp">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/result_image_view"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="100dp"
|
||||||
|
android:dividerPadding="5dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/result_title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:paddingEnd="10dp"
|
||||||
|
android:paddingStart="10dp"
|
||||||
|
android:textSize="17sp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:shadowRadius="2"
|
||||||
|
android:shadowDx="4"
|
||||||
|
android:shadowDy="4"
|
||||||
|
android:shadowColor="@color/black" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/author"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="bottom"
|
||||||
|
android:paddingEnd="10dp"
|
||||||
|
android:paddingBottom="5dp"
|
||||||
|
android:paddingStart="10dp"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:shadowRadius="1.5"
|
||||||
|
android:shadowDx="4"
|
||||||
|
android:shadowDy="4"
|
||||||
|
android:shadowColor="@color/black" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/duration"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="bottom"
|
||||||
|
android:paddingEnd="10dp"
|
||||||
|
android:paddingBottom="20dp"
|
||||||
|
android:paddingStart="10dp"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:shadowRadius="1.5"
|
||||||
|
android:shadowDx="4"
|
||||||
|
android:shadowDy="4"
|
||||||
|
android:shadowColor="@color/black" />
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabLayout
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="match_parent">
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabItem
|
||||||
|
android:icon="@drawable/ic_music"
|
||||||
|
android:text="@string/audio"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="wrap_content"/>
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabItem
|
||||||
|
android:icon="@drawable/ic_video"
|
||||||
|
android:text="@string/video"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="wrap_content"/>
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabItem
|
||||||
|
android:icon="@drawable/ic_baseline_keyboard_arrow_right_24"
|
||||||
|
android:text="@string/run_command"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="wrap_content"/>
|
||||||
|
|
||||||
|
</com.google.android.material.tabs.TabLayout>
|
||||||
|
|
||||||
|
<androidx.viewpager.widget.ViewPager
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
</androidx.viewpager.widget.ViewPager>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
|
app:enabled="false"
|
||||||
android:layout="@layout/fragment_more"/>
|
android:layout="@layout/fragment_more"/>
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,10 @@ buildscript {
|
||||||
versionCode = versionMajor * 100000 + versionMinor * 1000 + versionPatch * 100 + versionBuild
|
versionCode = versionMajor * 100000 + versionMinor * 1000 + versionPatch * 100 + versionBuild
|
||||||
versionName = "${versionMajor}.${versionMinor}.${versionPatch}"
|
versionName = "${versionMajor}.${versionMinor}.${versionPatch}"
|
||||||
// dependency versions
|
// dependency versions
|
||||||
appCompatVer = '1.5.1'
|
appCompatVer = '1.6.0'
|
||||||
junitVer = '4.13.2'
|
junitVer = '4.13.2'
|
||||||
androidJunitVer = '1.1.1'
|
androidJunitVer = '1.1.5'
|
||||||
espressoVer = '3.2.0'
|
espressoVer = '3.5.1'
|
||||||
jacksonVer = '2.9.8'
|
jacksonVer = '2.9.8'
|
||||||
// supports java 1.6
|
// supports java 1.6
|
||||||
commonsIoVer = '2.5'
|
commonsIoVer = '2.5'
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue