implemented format inside objects instead of a separate table

This commit is contained in:
Denis Çerri 2023-01-27 21:27:41 +01:00
parent ff58bd2dbf
commit 9e5f0e5fab
No known key found for this signature in database
GPG key ID: 95C43D517D830350
25 changed files with 186 additions and 261 deletions

View file

@ -24,7 +24,7 @@ android {
defaultConfig { defaultConfig {
applicationId "com.deniscerri.ytdl" applicationId "com.deniscerri.ytdl"
minSdkVersion 23 minSdkVersion 23
targetSdkVersion 29 targetSdkVersion 33
versionCode project.versionCode versionCode project.versionCode
versionName project.versionName versionName project.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@ -103,7 +103,7 @@ dependencies {
implementation 'androidx.navigation:navigation-fragment:2.5.3' implementation 'androidx.navigation:navigation-fragment:2.5.3'
implementation 'androidx.navigation:navigation-ui:2.5.3' implementation 'androidx.navigation:navigation-ui:2.5.3'
implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.core:core-ktx:+' implementation 'androidx.core:core-ktx:1.9.0'
testImplementation "junit:junit:$junitVer" testImplementation "junit:junit:$junitVer"
androidTestImplementation "androidx.test.ext:junit:$androidJunitVer" androidTestImplementation "androidx.test.ext:junit:$androidJunitVer"
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVer" androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVer"

View file

@ -2,11 +2,11 @@
"formatVersion": 1, "formatVersion": 1,
"database": { "database": {
"version": 1, "version": 1,
"identityHash": "db5c9df3e43ddd5a5131bbd372a7c92b", "identityHash": "eddd8d0408b9e321c902948a418e6c92",
"entities": [ "entities": [
{ {
"tableName": "results", "tableName": "results",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `duration` TEXT NOT NULL, `thumb` TEXT NOT NULL, `website` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL, `creationTime` INTEGER NOT NULL)", "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `duration` TEXT NOT NULL, `thumb` TEXT NOT NULL, `website` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL, `formats` TEXT NOT NULL, `creationTime` INTEGER NOT NULL)",
"fields": [ "fields": [
{ {
"fieldPath": "id", "fieldPath": "id",
@ -56,6 +56,12 @@
"affinity": "TEXT", "affinity": "TEXT",
"notNull": true "notNull": true
}, },
{
"fieldPath": "formats",
"columnName": "formats",
"affinity": "TEXT",
"notNull": true
},
{ {
"fieldPath": "creationTime", "fieldPath": "creationTime",
"columnName": "creationTime", "columnName": "creationTime",
@ -72,59 +78,9 @@
"indices": [], "indices": [],
"foreignKeys": [] "foreignKeys": []
}, },
{
"tableName": "formats",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `itemId` INTEGER NOT NULL, `format_id` TEXT NOT NULL, `container` TEXT NOT NULL, `filesize` INTEGER NOT NULL, `format_note` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "format_id",
"columnName": "format_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "container",
"columnName": "container",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "filesize",
"columnName": "filesize",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "format_note",
"columnName": "format_note",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{ {
"tableName": "history", "tableName": "history",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `duration` TEXT NOT NULL, `thumb` TEXT NOT NULL, `type` TEXT NOT NULL, `time` INTEGER NOT NULL, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL)", "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `duration` TEXT NOT NULL, `thumb` TEXT NOT NULL, `type` TEXT NOT NULL, `time` INTEGER NOT NULL, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL, `format` TEXT NOT NULL)",
"fields": [ "fields": [
{ {
"fieldPath": "id", "fieldPath": "id",
@ -185,6 +141,12 @@
"columnName": "website", "columnName": "website",
"affinity": "TEXT", "affinity": "TEXT",
"notNull": true "notNull": true
},
{
"fieldPath": "format",
"columnName": "format",
"affinity": "TEXT",
"notNull": true
} }
], ],
"primaryKey": { "primaryKey": {
@ -198,7 +160,7 @@
}, },
{ {
"tableName": "downloads", "tableName": "downloads",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `thumb` TEXT NOT NULL, `duration` TEXT NOT NULL, `type` TEXT NOT NULL, `audioFormatId` TEXT NOT NULL, `videoFormatId` TEXT NOT NULL, `customTemplateId` INTEGER 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, `embedSubs` INTEGER NOT NULL, `addChapters` INTEGER NOT NULL, `SaveThumb` INTEGER NOT NULL, `ext` TEXT NOT NULL, `filesize` TEXT NOT NULL, `status` TEXT NOT NULL DEFAULT 'Queued', `workID` INTEGER NOT NULL)", "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `thumb` TEXT NOT NULL, `duration` TEXT NOT NULL, `type` TEXT NOT NULL, `format` 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, `embedSubs` INTEGER NOT NULL, `addChapters` INTEGER NOT NULL, `SaveThumb` INTEGER NOT NULL, `ext` TEXT NOT NULL, `filesize` TEXT NOT NULL, `status` TEXT NOT NULL DEFAULT 'Queued', `workID` INTEGER NOT NULL)",
"fields": [ "fields": [
{ {
"fieldPath": "id", "fieldPath": "id",
@ -243,26 +205,8 @@
"notNull": true "notNull": true
}, },
{ {
"fieldPath": "audioFormatId", "fieldPath": "format",
"columnName": "audioFormatId", "columnName": "format",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "videoFormatId",
"columnName": "videoFormatId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "customTemplateId",
"columnName": "customTemplateId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "formatDesc",
"columnName": "formatDesc",
"affinity": "TEXT", "affinity": "TEXT",
"notNull": true "notNull": true
}, },
@ -386,7 +330,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, 'db5c9df3e43ddd5a5131bbd372a7c92b')" "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'eddd8d0408b9e321c902948a418e6c92')"
] ]
} }
} }

View file

@ -5,7 +5,8 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="androidd.permission.MANAGE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.ACTION_OPEN_DOCUMENT" /> <uses-permission android:name="android.permission.ACTION_OPEN_DOCUMENT" />
@ -21,7 +22,9 @@
android:supportsRtl="true" android:supportsRtl="true"
android:enableOnBackInvokedCallback="true" android:enableOnBackInvokedCallback="true"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
tools:targetApi="tiramisu"> tools:targetApi="tiramisu"
android:dataExtractionRules="@xml/data_extraction_rules"
tools:ignore="DataExtractionRules">
<activity android:name=".MainActivity" <activity android:name=".MainActivity"
android:windowSoftInputMode="adjustResize" android:windowSoftInputMode="adjustResize"
android:exported="true"> android:exported="true">

View file

@ -7,6 +7,8 @@ import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.Build.VERSION_CODES
import android.os.Bundle import android.os.Bundle
import android.provider.Settings import android.provider.Settings
import android.util.Log import android.util.Log
@ -272,12 +274,18 @@ class MainActivity : AppCompatActivity() {
} }
private fun askPermissions() { private fun askPermissions() {
if (!checkFilePermission()) { if (Build.VERSION.SDK_INT < VERSION_CODES.TIRAMISU && !checkFilePermission()) {
ActivityCompat.requestPermissions( ActivityCompat.requestPermissions(
this, this,
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
1 1
) )
}else{
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
1
)
} }
} }

View file

@ -0,0 +1,29 @@
package com.deniscerri.ytdlnis.database
import androidx.room.TypeConverter
import com.deniscerri.ytdlnis.database.models.Format
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import java.lang.reflect.Type
class Converters {
@TypeConverter
fun stringToListOfFormats(value: String?): ArrayList<Format> {
val listType: Type = object : TypeToken<ArrayList<Format?>?>() {}.type
return Gson().fromJson(value, listType)
}
@TypeConverter
fun listOfFormatsToString(list: ArrayList<Format?>?): String {
val gson = Gson()
return gson.toJson(list)
}
@TypeConverter
fun formatToString(format: Format): String = Gson().toJson(format)
@TypeConverter
fun stringToFormat(string: String): Format = Gson().fromJson(string, Format::class.java)
}

View file

@ -4,18 +4,19 @@ 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 androidx.room.TypeConverters
import com.deniscerri.ytdlnis.database.dao.* import com.deniscerri.ytdlnis.database.dao.*
import com.deniscerri.ytdlnis.database.models.* import com.deniscerri.ytdlnis.database.models.*
@TypeConverters(Converters::class)
@Database( @Database(
entities = [ResultItem::class, Format::class, HistoryItem::class, DownloadItem::class, CommandTemplate::class], entities = [ResultItem::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

View file

@ -1,25 +0,0 @@
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 WHERE itemId=:itemId")
fun getFormatsByItemId(itemId: Long) : 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: Long)
}

View file

@ -14,10 +14,7 @@ data class DownloadItem(
val thumb: String, val thumb: String,
val duration: String, val duration: String,
var type: String, var type: String,
var audioFormatId: String, var format: Format,
var videoFormatId: String,
var customTemplateId: Long,
var formatDesc: String,
@ColumnInfo(defaultValue = "0") @ColumnInfo(defaultValue = "0")
val removeAudio: Boolean, val removeAudio: Boolean,
var downloadPath: String, var downloadPath: String,

View file

@ -1,20 +1,14 @@
package com.deniscerri.ytdlnis.database.models package com.deniscerri.ytdlnis.database.models
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
@Entity(tableName = "formats")
data class Format( data class Format(
@PrimaryKey(autoGenerate = true)
var id: Long = -1,
var itemId: Long = 0,
@SerializedName(value = "format_id", alternate = ["itag"]) @SerializedName(value = "format_id", alternate = ["itag"])
val format_id: String = "", var format_id: String = "",
@SerializedName(value = "ext", alternate = ["container"]) @SerializedName(value = "ext", alternate = ["container"])
val container: String = "", val container: String = "",
@SerializedName(value = "filesize", alternate = ["clen", "filesize_approx"]) @SerializedName(value = "filesize", alternate = ["clen", "filesize_approx"])
val filesize: Long = 0, val filesize: Long = 0,
@SerializedName(value = "format_note", alternate = ["resolution", "audioQuality"]) @SerializedName(value = "format_note", alternate = ["resolution", "audioQuality"])
val format_note: String = "" var format_note: String = ""
) )

View file

@ -15,5 +15,6 @@ data class HistoryItem(
val type: String, val type: String,
val time: Long, val time: Long,
val downloadPath: String, val downloadPath: String,
val website: String val website: String,
val format: Format
) )

View file

@ -2,6 +2,8 @@ package com.deniscerri.ytdlnis.database.models
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import androidx.room.TypeConverters
import com.deniscerri.ytdlnis.database.Converters
import java.util.* import java.util.*
@Entity(tableName = "results") @Entity(tableName = "results")
@ -15,5 +17,6 @@ data class ResultItem(
val thumb: String, val thumb: String,
val website: String, val website: String,
var playlistTitle: String, var playlistTitle: String,
val formats: ArrayList<Format>,
var creationTime: Long = System.currentTimeMillis() / 1000, var creationTime: Long = System.currentTimeMillis() / 1000,
) )

View file

@ -1,6 +0,0 @@
package com.deniscerri.ytdlnis.database.models
data class ResultItemWithFormats(
val resultItem: ResultItem,
var formats: ArrayList<Format>
)

View file

@ -1,12 +1,8 @@
package com.deniscerri.ytdlnis.database.repository package com.deniscerri.ytdlnis.database.repository
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.DownloadDao
import com.deniscerri.ytdlnis.database.dao.FormatDao
import com.deniscerri.ytdlnis.database.models.DownloadItem import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.Format
import com.deniscerri.ytdlnis.database.models.ResultItem
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()

View file

@ -4,14 +4,13 @@ import android.content.Context
import android.util.Log import android.util.Log
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import com.deniscerri.ytdlnis.database.dao.CommandTemplateDao import com.deniscerri.ytdlnis.database.dao.CommandTemplateDao
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.CommandTemplate import com.deniscerri.ytdlnis.database.models.CommandTemplate
import com.deniscerri.ytdlnis.database.models.Format 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, private val formatDao: FormatDao, private val commandTemplateDao: CommandTemplateDao, private val context: Context) { class ResultRepository(private val resultDao: ResultDao, private val commandTemplateDao: CommandTemplateDao, private val context: Context) {
private val tag: String = "ResultRepository" private val tag: String = "ResultRepository"
val allResults : LiveData<List<ResultItem>> = resultDao.getResults() val allResults : LiveData<List<ResultItem>> = resultDao.getResults()
@ -28,8 +27,7 @@ class ResultRepository(private val resultDao: ResultDao, private val formatDao:
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){
val id = resultDao.insert(i!!.resultItem) resultDao.insert(i!!)
addFormats(i.formats,id)
} }
} }
@ -39,8 +37,7 @@ class ResultRepository(private val resultDao: ResultDao, private val formatDao:
if (resetResults) deleteAll() if (resetResults) deleteAll()
val res = infoUtil.search(inputQuery) val res = infoUtil.search(inputQuery)
res.forEach { res.forEach {
val id = resultDao.insert(it!!.resultItem) resultDao.insert(it!!)
addFormats(it.formats,id)
} }
}catch (ignored: Exception){} }catch (ignored: Exception){}
} }
@ -63,8 +60,7 @@ class ResultRepository(private val resultDao: ResultDao, private val formatDao:
try { try {
val v = infoUtil.getVideo(query!!) val v = infoUtil.getVideo(query!!)
if (resetResults) deleteAll() if (resetResults) deleteAll()
val id = resultDao.insert(v!!.resultItem) resultDao.insert(v!!)
addFormats(v.formats,id)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(tag, e.toString()) Log.e(tag, e.toString())
} }
@ -81,8 +77,7 @@ class ResultRepository(private val resultDao: ResultDao, private val formatDao:
val tmpVids = tmp.videos val tmpVids = tmp.videos
val tmpToken = tmp.nextPageToken val tmpToken = tmp.nextPageToken
tmpVids.forEach { tmpVids.forEach {
val id = resultDao.insert(it!!.resultItem) resultDao.insert(it!!)
addFormats(it.formats,id)
} }
if (tmpToken.isEmpty()) break if (tmpToken.isEmpty()) break
if (tmpToken == nextPageToken) break if (tmpToken == nextPageToken) break
@ -96,35 +91,22 @@ class ResultRepository(private val resultDao: ResultDao, private val formatDao:
if (resetResults) deleteAll() if (resetResults) deleteAll()
val items = infoUtil.getFromYTDL(inputQuery) val items = infoUtil.getFromYTDL(inputQuery)
items.forEach { items.forEach {
val id = resultDao.insert(it!!.resultItem) resultDao.insert(it!!)
addFormats(it.formats,id)
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e(tag, e.toString()) Log.e(tag, e.toString())
} }
} }
private suspend fun addFormats(formats : ArrayList<Format>, id: Long){
formats.forEach { f ->
if (f.filesize == 0L) return@forEach
f.itemId = id
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){
resultDao.update(item) resultDao.update(item)
} }
fun getFormats(item: ResultItem): List<Format> {
return formatDao.getFormatsByItemId(item.id)
}
fun getTemplates() : List<CommandTemplate> { fun getTemplates() : List<CommandTemplate> {
return commandTemplateDao.getAllTemplates() return commandTemplateDao.getAllTemplates()
} }

View file

@ -24,7 +24,6 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
init { init {
val dao = DBManager.getInstance(application).downloadDao val dao = DBManager.getInstance(application).downloadDao
val formatDao = DBManager.getInstance(application).formatDao
repository = DownloadRepository(dao) repository = DownloadRepository(dao)
allDownloads = repository.allDownloads allDownloads = repository.allDownloads
queuedDownloads = repository.queuedDownloads queuedDownloads = repository.queuedDownloads
@ -74,7 +73,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
resultItem.thumb, resultItem.thumb,
resultItem.duration, resultItem.duration,
type, type,
"", "", 0, "", false, Format(), false,
"", resultItem.website, "", resultItem.playlistTitle, embedSubs, addChapters, saveThumb, "", "", resultItem.website, "", resultItem.playlistTitle, embedSubs, addChapters, saveThumb, "",
"", DownloadRepository.status.Processing.toString(), 0 "", DownloadRepository.status.Processing.toString(), 0
) )

View file

@ -1,21 +1,16 @@
package com.deniscerri.ytdlnis.database.viewmodel package com.deniscerri.ytdlnis.database.viewmodel
import android.app.Application import android.app.Application
import android.content.Context
import android.util.Log import android.util.Log
import androidx.lifecycle.* import androidx.lifecycle.*
import com.deniscerri.ytdlnis.App import com.deniscerri.ytdlnis.App
import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.DBManager import com.deniscerri.ytdlnis.database.DBManager
import com.deniscerri.ytdlnis.database.dao.FormatDao
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.repository.DownloadRepository
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 import java.util.regex.Pattern
import kotlin.coroutines.coroutineContext
class ResultViewModel(application: Application) : AndroidViewModel(application) { class ResultViewModel(application: Application) : AndroidViewModel(application) {
private val tag: String = "ResultViewModel" private val tag: String = "ResultViewModel"
@ -25,9 +20,8 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
init { init {
val dao = DBManager.getInstance(application).resultDao val dao = DBManager.getInstance(application).resultDao
val formatDao = DBManager.getInstance(application).formatDao
val commandDao = DBManager.getInstance(application).commandTemplateDao val commandDao = DBManager.getInstance(application).commandTemplateDao
repository = ResultRepository(dao, formatDao, commandDao, getApplication<Application>().applicationContext) repository = ResultRepository(dao, commandDao, getApplication<Application>().applicationContext)
items = repository.allResults items = repository.allResults
loadingItems.postValue(false) loadingItems.postValue(false)
} }
@ -99,32 +93,7 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
repository.update(item); repository.update(item);
} }
fun getFormats(item: ResultItem, type: String) : List<Format> {
var list = repository.getFormats(item)
val formats = mutableListOf<Format>()
val audioFormats = getApplication<App>().resources.getStringArray(R.array.audio_formats)
val videoFormats = getApplication<App>().resources.getStringArray(R.array.video_formats)
when(type){
"audio" -> {
return list.filter { it.format_note.contains("audio", ignoreCase = true) }
}
"video" -> {
list = list.filter { !it.format_note.contains("audio", ignoreCase = true) }
if (list.isEmpty()) {
videoFormats.forEach { formats.add(Format(0, item.id, it, "", 0, it)) }
return formats
}
return list
}
}
val templates = repository.getTemplates()
templates.forEach {
formats.add(Format(0, item.id, it.title, "", 0, it.content))
}
return formats
}
fun getItemByURL(url: String) : ResultItem { fun getItemByURL(url: String) : ResultItem {
return repository.getItemByURL(url) return repository.getItemByURL(url)

View file

@ -22,11 +22,8 @@ import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdlnis.MainActivity import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.adapter.HomeAdapter import com.deniscerri.ytdlnis.adapter.HomeAdapter
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.Format 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.deniscerri.ytdlnis.database.repository.DownloadRepository
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding
@ -42,7 +39,6 @@ import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.google.android.material.chip.ChipGroup import com.google.android.material.chip.ChipGroup
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.progressindicator.LinearProgressIndicator import com.google.android.material.progressindicator.LinearProgressIndicator
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@ -147,7 +143,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
}else{ }else{
downloadAllFabCoordinator!!.visibility = GONE downloadAllFabCoordinator!!.visibility = GONE
} }
}else if(it.size == 1 && !firstBoot && parentFragmentManager.findFragmentByTag("bottomSheet") == null){ }else if(it.size == 1 && !firstBoot && parentFragmentManager.findFragmentByTag("downloadSingleSheet") == null){
showSingleDownloadSheet(it[0], "audio") showSingleDownloadSheet(it[0], "audio")
} }
firstBoot = false firstBoot = false
@ -360,11 +356,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
} }
private fun showSingleDownloadSheet(resultItem : ResultItem, type: String){ private fun showSingleDownloadSheet(resultItem : ResultItem, type: String){
val item = ResultItemWithFormats( val downloadItem = downloadViewModel.createDownloadItemFromResult(resultItem, type)
resultItem,
resultViewModel.getFormats(resultItem, "video") as ArrayList<Format>
)
val downloadItem = downloadViewModel.createDownloadItemFromResult(resultItem, "video")
downloadViewModel.insertDownload(downloadItem).observe(viewLifecycleOwner) { downloadViewModel.insertDownload(downloadItem).observe(viewLifecycleOwner) {
downloadItem.id = it downloadItem.id = it
val bottomSheet = DownloadBottomSheetDialog(downloadItem) val bottomSheet = DownloadBottomSheetDialog(downloadItem)

View file

@ -132,9 +132,7 @@ class DownloadAudioFragment(private val downloadItem: DownloadItem) : Fragment()
audioPathResultLauncher.launch(intent) audioPathResultLauncher.launch(intent)
} }
val formats = withContext(Dispatchers.IO){ val formats = resultItem.formats.filter { it.format_note.contains("audio", ignoreCase = true) }
resultViewModel.getFormats(resultItem, type)
}
val format = view.findViewById<TextInputLayout>(R.id.format) val format = view.findViewById<TextInputLayout>(R.id.format)
if (formats.isEmpty()) { if (formats.isEmpty()) {
format.visibility = View.GONE format.visibility = View.GONE
@ -166,8 +164,8 @@ class DownloadAudioFragment(private val downloadItem: DownloadItem) : Fragment()
} }
(format!!.editText as AutoCompleteTextView?)!!.onItemClickListener = (format!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long -> AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long ->
downloadItem.formatDesc = formats[index].format_note downloadItem.format.format_note = formats[index].format_note
downloadItem.audioFormatId = formats[index].format_id downloadItem.format.format_id = formats[index].format_id
} }
} }
@ -184,7 +182,7 @@ class DownloadAudioFragment(private val downloadItem: DownloadItem) : Fragment()
containers containers
) )
) )
val selectedContainer: String = formats.find { downloadItem.formatDesc == it.format_note }?.container val selectedContainer: String = formats.find { downloadItem.format.format_note == it.format_note }?.container
?: sharedPreferences.getString("audio_format", "mp3")!! ?: sharedPreferences.getString("audio_format", "mp3")!!
containerAutoCompleteTextView!!.setText(selectedContainer, false) containerAutoCompleteTextView!!.setText(selectedContainer, false)
(container!!.editText as AutoCompleteTextView?)!!.onItemClickListener = (container!!.editText as AutoCompleteTextView?)!!.onItemClickListener =

View file

@ -9,6 +9,7 @@ import androidx.fragment.app.Fragment
import com.deniscerri.ytdlnis.MainActivity import com.deniscerri.ytdlnis.MainActivity
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.Format
import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding
class DownloadCommandFragment(item: DownloadItem) : Fragment() { class DownloadCommandFragment(item: DownloadItem) : Fragment() {
@ -28,5 +29,12 @@ class DownloadCommandFragment(item: DownloadItem) : Fragment() {
activity = getActivity() activity = getActivity()
mainActivity = activity as MainActivity? mainActivity = activity as MainActivity?
return fragmentView return fragmentView
//get formats
// val templates = repository.getTemplates()
// templates.forEach {
// formats.add(Format(0, item.id, it.title, "", 0, it.content))
// }
} }
} }

View file

@ -13,9 +13,11 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.deniscerri.ytdlnis.App
import com.deniscerri.ytdlnis.MainActivity import com.deniscerri.ytdlnis.MainActivity
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.Format
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding
@ -132,8 +134,11 @@ class DownloadVideoFragment(private val downloadItem: DownloadItem) : Fragment()
videoPathResultLauncher.launch(intent) videoPathResultLauncher.launch(intent)
} }
val formats = withContext(Dispatchers.IO){ val formats = mutableListOf<Format>()
resultViewModel.getFormats(resultItem, type) formats.addAll(resultItem.formats.filter { !it.format_note.contains("audio", ignoreCase = true) })
if (formats.isEmpty()) {
val videoFormats = resources.getStringArray(R.array.video_formats)
videoFormats.forEach { formats.add(Format(it, "", 0, it)) }
} }
val formatTitles = formats.map { val formatTitles = formats.map {
@ -159,8 +164,8 @@ class DownloadVideoFragment(private val downloadItem: DownloadItem) : Fragment()
} }
(format!!.editText as AutoCompleteTextView?)!!.onItemClickListener = (format!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long -> AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long ->
downloadItem.formatDesc = formats[index].format_note downloadItem.format.format_note = formats[index].format_note
downloadItem.videoFormatId = formats[index].format_id downloadItem.format.format_id = formats[index].format_id
} }
val containers = requireContext().resources.getStringArray(R.array.video_containers) val containers = requireContext().resources.getStringArray(R.array.video_containers)
@ -177,7 +182,7 @@ class DownloadVideoFragment(private val downloadItem: DownloadItem) : Fragment()
) )
) )
val selectedContainer: String = val selectedContainer: String =
formats.find { downloadItem.formatDesc == it.format_note }?.container formats.find { downloadItem.format.format_note == it.format_note }?.container
?: sharedPreferences.getString("video_format", "DEFAULT")!! ?: sharedPreferences.getString("video_format", "DEFAULT")!!
containerAutoCompleteTextView!!.setText(selectedContainer, false) containerAutoCompleteTextView!!.setText(selectedContainer, false)
(container!!.editText as AutoCompleteTextView?)!!.onItemClickListener = (container!!.editText as AutoCompleteTextView?)!!.onItemClickListener =

View file

@ -6,7 +6,6 @@ import android.util.Log
import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.Format 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.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
@ -22,7 +21,7 @@ import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
class InfoUtil(context: Context) { class InfoUtil(context: Context) {
private var items: ArrayList<ResultItemWithFormats?> private var items: ArrayList<ResultItem?>
private var key: String? = null private var key: String? = null
private var useInvidous = false private var useInvidous = false
@ -53,7 +52,7 @@ class InfoUtil(context: Context) {
} }
@Throws(JSONException::class) @Throws(JSONException::class)
fun search(query: String): ArrayList<ResultItemWithFormats?> { fun search(query: String): ArrayList<ResultItem?> {
init() init()
items = ArrayList() items = ArrayList()
return if (key!!.isEmpty()) { return if (key!!.isEmpty()) {
@ -62,7 +61,7 @@ class InfoUtil(context: Context) {
} }
@Throws(JSONException::class) @Throws(JSONException::class)
fun searchFromKey(query: String): ArrayList<ResultItemWithFormats?> { fun searchFromKey(query: String): ArrayList<ResultItem?> {
//short data //short data
val res = val res =
genericRequest("https://youtube.googleapis.com/youtube/v3/search?part=snippet&q=$query&maxResults=25&regionCode=$countryCODE&key=$key") genericRequest("https://youtube.googleapis.com/youtube/v3/search?part=snippet&q=$query&maxResults=25&regionCode=$countryCODE&key=$key")
@ -101,7 +100,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.resultItem.thumb.isEmpty()) { if (v == null || v.thumb.isEmpty()) {
continue continue
} }
items.add(createVideofromJSON(snippet)) items.add(createVideofromJSON(snippet))
@ -111,7 +110,7 @@ class InfoUtil(context: Context) {
} }
@Throws(JSONException::class) @Throws(JSONException::class)
fun searchFromInvidous(query: String): ArrayList<ResultItemWithFormats?> { fun searchFromInvidous(query: String): ArrayList<ResultItem?> {
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()) {
@ -127,7 +126,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.resultItem.thumb.isEmpty()) { if (v == null || v.thumb.isEmpty()) {
continue continue
} }
items.add(v) items.add(v)
@ -182,13 +181,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.resultItem.thumb.isEmpty()) { if (v == null || v.thumb.isEmpty()) {
i++ i++
continue continue
} else { } else {
j++ j++
} }
v.resultItem.playlistTitle = "YTDLnis" v.playlistTitle = "YTDLnis"
items.add(v) items.add(v)
i++ i++
} }
@ -208,8 +207,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.resultItem.thumb.isEmpty()) continue if (v == null || v.thumb.isEmpty()) continue
v.resultItem.playlistTitle = res.getString("title") v.playlistTitle = res.getString("title")
items.add(v) items.add(v)
} }
} catch (e: Exception) { } catch (e: Exception) {
@ -219,7 +218,7 @@ class InfoUtil(context: Context) {
} }
@Throws(JSONException::class) @Throws(JSONException::class)
fun getVideo(id: String): ResultItemWithFormats? { fun getVideo(id: String): ResultItem? {
init() init()
if (key!!.isEmpty()) { if (key!!.isEmpty()) {
return if (useInvidous) { return if (useInvidous) {
@ -246,8 +245,8 @@ class InfoUtil(context: Context) {
return createVideofromJSON(res) return createVideofromJSON(res)
} }
private fun createVideofromJSON(obj: JSONObject): ResultItemWithFormats? { private fun createVideofromJSON(obj: JSONObject): ResultItem? {
var video: ResultItemWithFormats? = null var video: ResultItem? = 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()
@ -255,8 +254,7 @@ class InfoUtil(context: Context) {
val duration = obj.getString("duration") val duration = obj.getString("duration")
val thumb = obj.getString("thumb") val thumb = obj.getString("thumb")
val url = "https://www.youtube.com/watch?v=$id" val url = "https://www.youtube.com/watch?v=$id"
video = ResultItemWithFormats( video = ResultItem(0,
ResultItem(0,
url, url,
title, title,
author, author,
@ -264,7 +262,6 @@ class InfoUtil(context: Context) {
thumb, thumb,
"youtube", "youtube",
"", "",
),
ArrayList() ArrayList()
) )
} catch (e: Exception) { } catch (e: Exception) {
@ -273,8 +270,8 @@ class InfoUtil(context: Context) {
return video return video
} }
private fun createVideofromInvidiousJSON(obj: JSONObject): ResultItemWithFormats? { private fun createVideofromInvidiousJSON(obj: JSONObject): ResultItem? {
var video: ResultItemWithFormats? = null var video: ResultItem? = 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()
@ -292,8 +289,7 @@ class InfoUtil(context: Context) {
} }
} }
video = ResultItemWithFormats( video = ResultItem(0,
ResultItem(0,
url, url,
title, title,
author, author,
@ -301,9 +297,8 @@ class InfoUtil(context: Context) {
thumb, thumb,
"youtube", "youtube",
"", "",
), formats
formats )
)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, e.toString()) Log.e(TAG, e.toString())
} }
@ -394,7 +389,7 @@ class InfoUtil(context: Context) {
} }
@Throws(JSONException::class) @Throws(JSONException::class)
fun getTrending(context: Context): ArrayList<ResultItemWithFormats?> { fun getTrending(context: Context): ArrayList<ResultItem?> {
init() init()
items = ArrayList() items = ArrayList()
return if (key!!.isEmpty()) { return if (key!!.isEmpty()) {
@ -403,7 +398,7 @@ class InfoUtil(context: Context) {
} }
@Throws(JSONException::class) @Throws(JSONException::class)
fun getTrendingFromKey(context: Context): ArrayList<ResultItemWithFormats?> { fun getTrendingFromKey(context: Context): ArrayList<ResultItem?> {
val url = "https://www.googleapis.com/youtube/v3/videos?part=snippet&chart=mostPopular&videoCategoryId=10&regionCode=$countryCODE&maxResults=25&key=$key" val url = "https://www.googleapis.com/youtube/v3/videos?part=snippet&chart=mostPopular&videoCategoryId=10&regionCode=$countryCODE&maxResults=25&key=$key"
//short data //short data
val res = genericRequest(url) val res = genericRequest(url)
@ -423,16 +418,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.resultItem.thumb.isEmpty()) { if (v == null || v.thumb.isEmpty()) {
continue continue
} }
v.resultItem.playlistTitle = context.getString(R.string.trendingPlaylist) v.playlistTitle = context.getString(R.string.trendingPlaylist)
items.add(v) items.add(v)
} }
return items return items
} }
private fun getTrendingFromInvidous(context: Context): ArrayList<ResultItemWithFormats?> { private fun getTrendingFromInvidous(context: Context): ArrayList<ResultItem?> {
val url = invidousURL + "trending?type=music&region=" + countryCODE val url = invidousURL + "trending?type=music&region=" + countryCODE
val res = genericArrayRequest(url) val res = genericArrayRequest(url)
try { try {
@ -440,8 +435,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.resultItem.thumb.isEmpty()) continue if (v == null || v.thumb.isEmpty()) continue
v.resultItem.playlistTitle = context.getString(R.string.trendingPlaylist) v.playlistTitle = context.getString(R.string.trendingPlaylist)
items.add(v) items.add(v)
} }
} catch (e: Exception) { } catch (e: Exception) {
@ -450,7 +445,7 @@ class InfoUtil(context: Context) {
return items return items
} }
fun getFromYTDL(query: String): ArrayList<ResultItemWithFormats?> { fun getFromYTDL(query: String): ArrayList<ResultItem?> {
init() init()
items = ArrayList() items = ArrayList()
try { try {
@ -506,16 +501,14 @@ class InfoUtil(context: Context) {
} }
} }
Log.e(TAG, formats.toString()) Log.e(TAG, formats.toString())
items.add(ResultItemWithFormats( items.add(ResultItem(0,
ResultItem(0,
url, url,
title, title,
author!!, author!!,
duration, duration,
thumb!!, thumb!!,
website, website,
playlistTitle!! playlistTitle!!,
),
formats formats
) )
) )
@ -603,7 +596,7 @@ class InfoUtil(context: Context) {
class PlaylistTuple internal constructor( class PlaylistTuple internal constructor(
var nextPageToken: String, var nextPageToken: String,
var videos: ArrayList<ResultItemWithFormats?> var videos: ArrayList<ResultItem?>
) )
companion object { companion object {

View file

@ -58,11 +58,7 @@ class DownloadWorker(
val downloadLocation = downloadItem.downloadPath val downloadLocation = downloadItem.downloadPath
val tempFolder = StringBuilder(context.cacheDir.absolutePath + """/${downloadItem.title}##${downloadItem.type}""") val tempFolder = StringBuilder(context.cacheDir.absolutePath + """/${downloadItem.title}##${downloadItem.type}""")
when(type){ tempFolder.append("##${downloadItem.format.format_id}")
"audio" -> tempFolder.append("##${downloadItem.audioFormatId}")
"video" -> tempFolder.append("##${downloadItem.videoFormatId}")
"command" -> tempFolder.append("##${downloadItem.customTemplateId}")
}
var tempFileDir = File(tempFolder.toString()) var tempFileDir = File(tempFolder.toString())
tempFileDir.delete() tempFileDir.delete()
tempFileDir.mkdir() tempFileDir.mkdir()
@ -95,7 +91,7 @@ class DownloadWorker(
when(type){ when(type){
"audio" -> { "audio" -> {
request.addOption("-x") request.addOption("-x")
var audioQualityId : String = downloadItem.audioFormatId var audioQualityId : String = downloadItem.format.format_id
if (audioQualityId == "0") audioQualityId = "ba" if (audioQualityId == "0") audioQualityId = "ba"
var ext = downloadItem.ext var ext = downloadItem.ext
request.addOption("-f", audioQualityId) request.addOption("-f", audioQualityId)
@ -107,7 +103,7 @@ class DownloadWorker(
request.addOption("--embed-thumbnail") request.addOption("--embed-thumbnail")
request.addOption("--convert-thumbnails", "png") request.addOption("--convert-thumbnails", "png")
try { try {
val config = File(context.cacheDir, "config" + downloadItem.title + "##" + downloadItem.audioFormatId + ".txt") val config = File(context.cacheDir, "config" + downloadItem.title + "##" + downloadItem.format.format_id + ".txt")
val configData = val configData =
"--ppa \"ffmpeg: -c:v png -vf crop=\\\"'if(gt(ih,iw),iw,ih)':'if(gt(iw,ih),ih,iw)'\\\"\"" "--ppa \"ffmpeg: -c:v png -vf crop=\\\"'if(gt(ih,iw),iw,ih)':'if(gt(iw,ih),ih,iw)'\\\"\""
config.writeText(configData) config.writeText(configData)
@ -139,11 +135,10 @@ class DownloadWorker(
if (embedSubs) { if (embedSubs) {
request.addOption("--embed-subs", "") request.addOption("--embed-subs", "")
} }
var videoFormatID = downloadItem.videoFormatId var videoFormatID = downloadItem.format.format_id
val audioFormatID = downloadItem.audioFormatId
if (videoFormatID.isEmpty()) videoFormatID = "bestvideo" if (videoFormatID.isEmpty()) videoFormatID = "bestvideo"
val formatArgument = StringBuilder(videoFormatID) val formatArgument = StringBuilder(videoFormatID)
if (audioFormatID != "0") formatArgument.append("+", audioFormatID, "/best") formatArgument.append("+bestaudio/best")
request.addOption("-f", formatArgument.toString()) request.addOption("-f", formatArgument.toString())
var format = downloadItem.ext var format = downloadItem.ext
if (format.isNotEmpty()) { if (format.isNotEmpty()) {
@ -161,7 +156,7 @@ class DownloadWorker(
} }
"command" -> { "command" -> {
val commandRegex = "\"([^\"]*)\"|(\\S+)" val commandRegex = "\"([^\"]*)\"|(\\S+)"
val command = commandTemplateDao.getTemplate(downloadItem.customTemplateId) val command = commandTemplateDao.getTemplate(downloadItem.format.format_id.toLong())
val m = Pattern.compile(commandRegex).matcher(command.content) val m = Pattern.compile(commandRegex).matcher(command.content)
while (m.find()) { while (m.find()) {
if (m.group(1) != null) { if (m.group(1) != null) {
@ -192,7 +187,7 @@ class DownloadWorker(
val incognito = sharedPreferences.getBoolean("incognito", false) val incognito = sharedPreferences.getBoolean("incognito", false)
if (!incognito) { if (!incognito) {
val unixtime = System.currentTimeMillis() / 1000 val unixtime = System.currentTimeMillis() / 1000
val historyItem = HistoryItem(0, downloadItem.url, downloadItem.title, downloadItem.author, downloadItem.duration, downloadItem.thumb, downloadItem.type, unixtime, downloadItem.downloadPath, downloadItem.website) val historyItem = HistoryItem(0, downloadItem.url, downloadItem.title, downloadItem.author, downloadItem.duration, downloadItem.thumb, downloadItem.type, unixtime, downloadItem.downloadPath, downloadItem.website, downloadItem.format)
runBlocking { runBlocking {
historyDao.insert(historyItem) historyDao.insert(historyItem)
} }

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Sample data extraction rules file; uncomment and customize as necessary.
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
for details.
-->
<data-extraction-rules>
<cloud-backup>
<!--
TODO: Use <include> and <exclude> to control what is backed up.
The domain can be file, database, sharedpref, external or root.
Examples:
<include domain="file" path="file_to_include"/>
<exclude domain="file" path="file_to_exclude"/>
<include domain="file" path="include_folder"/>
<exclude domain="file" path="include_folder/file_to_exclude"/>
<exclude domain="file" path="exclude_folder"/>
<include domain="file" path="exclude_folder/file_to_include"/>
<include domain="sharedpref" path="include_shared_pref1.xml"/>
<include domain="database" path="db_name/file_to_include"/>
<exclude domain="database" path="db_name/include_folder/file_to_exclude"/>
<include domain="external" path="file_to_include"/>
<exclude domain="external" path="file_to_exclude"/>
<include domain="root" path="file_to_include"/>
<exclude domain="root" path="file_to_exclude"/>
-->
</cloud-backup>
<!--
<device-transfer>
<include .../>
<exclude .../>
</device-transfer>
-->
</data-extraction-rules>

View file

@ -23,8 +23,8 @@ buildscript {
commonsCompressVer = '1.12' commonsCompressVer = '1.12'
youtubedlAndroidVer = "a3b971724d" youtubedlAndroidVer = "a3b971724d"
workVer = "2.7.1" workVer = "2.7.1"
composeVer = "1.3.2" composeVer = "1.4.0-alpha02"
kotlinVer = "1.7.20" kotlinVer = "1.7.21"
coroutineVer = "1.6.4" coroutineVer = "1.6.4"
retrofitVer = "2.9.0" retrofitVer = "2.9.0"
kodeinVer = "7.16.0" kodeinVer = "7.16.0"
@ -33,12 +33,13 @@ buildscript {
mavenCentral() mavenCentral()
google() google()
} }
} }
plugins { plugins {
id 'com.android.application' version '7.4.0' apply false id 'com.android.application' version '7.4.0' apply false
id 'com.android.library' version '7.4.0' apply false id 'com.android.library' version '7.4.0' apply false
id 'org.jetbrains.kotlin.android' version '1.7.20' apply false id 'org.jetbrains.kotlin.android' version '1.7.21' apply false
} }

View file

@ -4,6 +4,8 @@ pluginManagement {
google() google()
jcenter() jcenter()
mavenCentral() mavenCentral()
maven { url 'https://jitpack.io' }
maven { url "https://dl.bintray.com/kodein-framework/Kodein-DI" }
} }
} }
dependencyResolutionManagement { dependencyResolutionManagement {