more more fixes lol

fixed Snackbar overlapping with buttons on settings
fixed locale not translating right in download card
fixed format updating the container when re-selecting it
made the video tab disabled when the item only has audio formats
made the app fetch streaming url and chapters from the start so it loads faster when trying to cut a video
added url in format item
This commit is contained in:
deniscerri 2023-05-28 13:19:35 +02:00
parent bfdc7df88a
commit 0f6d1fa765
No known key found for this signature in database
GPG key ID: 95C43D517D830350
46 changed files with 1056 additions and 459 deletions

View file

@ -0,0 +1,448 @@
{
"formatVersion": 1,
"database": {
"version": 6,
"identityHash": "d34711f8239a4a966885543cd5454eab",
"entities": [
{
"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, `formats` TEXT NOT NULL, `urls` TEXT NOT NULL DEFAULT '', `chapters` TEXT, `creationTime` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "author",
"columnName": "author",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "duration",
"columnName": "duration",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "thumb",
"columnName": "thumb",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "website",
"columnName": "website",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "playlistTitle",
"columnName": "playlistTitle",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "formats",
"columnName": "formats",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "urls",
"columnName": "urls",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "chapters",
"columnName": "chapters",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "creationTime",
"columnName": "creationTime",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"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, `format` TEXT NOT NULL, `downloadId` INTEGER NOT NULL DEFAULT 0)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "author",
"columnName": "author",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "duration",
"columnName": "duration",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "thumb",
"columnName": "thumb",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "time",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "downloadPath",
"columnName": "downloadPath",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "website",
"columnName": "website",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "format",
"columnName": "format",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "downloadId",
"columnName": "downloadId",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"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, `format` TEXT NOT NULL, `container` TEXT NOT NULL DEFAULT 'Default', `downloadSections` TEXT NOT NULL DEFAULT '', `allFormats` TEXT NOT NULL, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL, `downloadSize` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL, `audioPreferences` TEXT NOT NULL, `videoPreferences` TEXT NOT NULL, `customFileNameTemplate` TEXT NOT NULL, `SaveThumb` INTEGER NOT NULL, `status` TEXT NOT NULL DEFAULT 'Queued', `downloadStartTime` INTEGER NOT NULL DEFAULT 0)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "author",
"columnName": "author",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "thumb",
"columnName": "thumb",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "duration",
"columnName": "duration",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "format",
"columnName": "format",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "container",
"columnName": "container",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'Default'"
},
{
"fieldPath": "downloadSections",
"columnName": "downloadSections",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "allFormats",
"columnName": "allFormats",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "downloadPath",
"columnName": "downloadPath",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "website",
"columnName": "website",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "downloadSize",
"columnName": "downloadSize",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "playlistTitle",
"columnName": "playlistTitle",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "audioPreferences",
"columnName": "audioPreferences",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "videoPreferences",
"columnName": "videoPreferences",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "customFileNameTemplate",
"columnName": "customFileNameTemplate",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "SaveThumb",
"columnName": "SaveThumb",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "status",
"columnName": "status",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'Queued'"
},
{
"fieldPath": "downloadStartTime",
"columnName": "downloadStartTime",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "commandTemplates",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "content",
"columnName": "content",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "searchHistory",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `query` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "query",
"columnName": "query",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "templateShortcuts",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `content` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "content",
"columnName": "content",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "cookies",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `content` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "content",
"columnName": "content",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd34711f8239a4a966885543cd5454eab')"
]
}
}

View file

@ -38,6 +38,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
import com.deniscerri.ytdlnis.ui.BaseActivity import com.deniscerri.ytdlnis.ui.BaseActivity
import com.deniscerri.ytdlnis.ui.HomeFragment import com.deniscerri.ytdlnis.ui.HomeFragment
import com.deniscerri.ytdlnis.ui.more.settings.SettingsActivity import com.deniscerri.ytdlnis.ui.more.settings.SettingsActivity
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.ThemeUtil import com.deniscerri.ytdlnis.util.ThemeUtil
import com.deniscerri.ytdlnis.util.UpdateUtil import com.deniscerri.ytdlnis.util.UpdateUtil
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
@ -67,7 +68,6 @@ class MainActivity : BaseActivity() {
private lateinit var navHostFragment : NavHostFragment private lateinit var navHostFragment : NavHostFragment
private lateinit var navController : NavController private lateinit var navController : NavController
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
ThemeUtil.updateTheme(this) ThemeUtil.updateTheme(this)
@ -338,9 +338,9 @@ class MainActivity : BaseActivity() {
private fun createDefaultFolders(){ private fun createDefaultFolders(){
val audio = File(getString(R.string.music_path)) val audio = File(FileUtil.getDefautAudioPath())
val video = File(getString(R.string.video_path)) val video = File(FileUtil.getDefautVideoPath())
val command = File(getString(R.string.command_path)) val command = File(FileUtil.getDefaultCommandPath())
audio.mkdirs() audio.mkdirs()
video.mkdirs() video.mkdirs()

View file

@ -28,12 +28,10 @@ import com.squareup.picasso.Picasso
class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter<DownloadItem?, ActiveDownloadAdapter.ViewHolder>(AsyncDifferConfig.Builder(DIFF_CALLBACK).build()) { class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter<DownloadItem?, ActiveDownloadAdapter.ViewHolder>(AsyncDifferConfig.Builder(DIFF_CALLBACK).build()) {
private val onItemClickListener: OnItemClickListener private val onItemClickListener: OnItemClickListener
private val activity: Activity private val activity: Activity
private val fileUtil: FileUtil
init { init {
this.onItemClickListener = onItemClickListener this.onItemClickListener = onItemClickListener
this.activity = activity this.activity = activity
fileUtil = FileUtil()
} }
class ViewHolder(itemView: View, onItemClickListener: OnItemClickListener?) : RecyclerView.ViewHolder(itemView) { class ViewHolder(itemView: View, onItemClickListener: OnItemClickListener?) : RecyclerView.ViewHolder(itemView) {
@ -113,7 +111,7 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
} }
val fileSize = card.findViewById<Chip>(R.id.file_size) val fileSize = card.findViewById<Chip>(R.id.file_size)
fileSize.text = fileUtil.convertFileSize(item.format.filesize) fileSize.text = FileUtil.convertFileSize(item.format.filesize)
if (fileSize.text == "?") fileSize.visibility = View.GONE if (fileSize.text == "?") fileSize.visibility = View.GONE
if (fileSize.visibility == View.VISIBLE && codec.visibility == View.GONE){ if (fileSize.visibility == View.VISIBLE && codec.visibility == View.GONE){

View file

@ -24,12 +24,10 @@ import java.util.Locale
class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter<DownloadItem?, ConfigureMultipleDownloadsAdapter.ViewHolder>(AsyncDifferConfig.Builder(DIFF_CALLBACK).build()) { class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter<DownloadItem?, ConfigureMultipleDownloadsAdapter.ViewHolder>(AsyncDifferConfig.Builder(DIFF_CALLBACK).build()) {
private val onItemClickListener: OnItemClickListener private val onItemClickListener: OnItemClickListener
private val activity: Activity private val activity: Activity
private var fileUtil: FileUtil
init { init {
this.onItemClickListener = onItemClickListener this.onItemClickListener = onItemClickListener
this.activity = activity this.activity = activity
fileUtil = FileUtil()
} }
class ViewHolder(itemView: View, onItemClickListener: OnItemClickListener?) : RecyclerView.ViewHolder(itemView) { class ViewHolder(itemView: View, onItemClickListener: OnItemClickListener?) : RecyclerView.ViewHolder(itemView) {
@ -96,7 +94,7 @@ class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener
} }
val fileSize = card.findViewById<TextView>(R.id.file_size) val fileSize = card.findViewById<TextView>(R.id.file_size)
val fileSizeReadable = fileUtil.convertFileSize(item.format.filesize) val fileSizeReadable = FileUtil.convertFileSize(item.format.filesize)
if (fileSizeReadable == "?") fileSize.visibility = View.GONE if (fileSizeReadable == "?") fileSize.visibility = View.GONE
else { else {
fileSize.text = fileSizeReadable fileSize.text = fileSizeReadable

View file

@ -24,14 +24,12 @@ import com.squareup.picasso.Picasso
class GenericDownloadAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter<DownloadItem?, GenericDownloadAdapter.ViewHolder>(AsyncDifferConfig.Builder(DIFF_CALLBACK).build()) { class GenericDownloadAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter<DownloadItem?, GenericDownloadAdapter.ViewHolder>(AsyncDifferConfig.Builder(DIFF_CALLBACK).build()) {
private val onItemClickListener: OnItemClickListener private val onItemClickListener: OnItemClickListener
private val activity: Activity private val activity: Activity
private var fileUtil: FileUtil
private val checkedItems: ArrayList<Long> private val checkedItems: ArrayList<Long>
init { init {
checkedItems = ArrayList() checkedItems = ArrayList()
this.onItemClickListener = onItemClickListener this.onItemClickListener = onItemClickListener
this.activity = activity this.activity = activity
fileUtil = FileUtil()
} }
class ViewHolder(itemView: View, onItemClickListener: OnItemClickListener?) : RecyclerView.ViewHolder(itemView) { class ViewHolder(itemView: View, onItemClickListener: OnItemClickListener?) : RecyclerView.ViewHolder(itemView) {
@ -94,7 +92,7 @@ class GenericDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
} }
val fileSize = card.findViewById<TextView>(R.id.file_size) val fileSize = card.findViewById<TextView>(R.id.file_size)
val fileSizeReadable = fileUtil.convertFileSize(item.format.filesize) val fileSizeReadable = FileUtil.convertFileSize(item.format.filesize)
if (fileSizeReadable == "?") fileSize.visibility = View.GONE if (fileSizeReadable == "?") fileSize.visibility = View.GONE
else fileSize.text = fileSizeReadable else fileSize.text = fileSizeReadable
@ -107,7 +105,7 @@ class GenericDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
DownloadRepository.Status.Queued.toString() -> actionButton.setIconResource(R.drawable.ic_baseline_delete_outline_24) DownloadRepository.Status.Queued.toString() -> actionButton.setIconResource(R.drawable.ic_baseline_delete_outline_24)
else -> { else -> {
actionButton.setIconResource(R.drawable.ic_baseline_file_open_24) actionButton.setIconResource(R.drawable.ic_baseline_file_open_24)
val logFile = fileUtil.getLogFile(activity, item) val logFile = FileUtil.getLogFile(activity, item)
if (!logFile.exists()){ if (!logFile.exists()){
actionButton.visibility = View.GONE actionButton.visibility = View.GONE
} }

View file

@ -2,6 +2,7 @@ package com.deniscerri.ytdlnis.database
import androidx.room.TypeConverter import androidx.room.TypeConverter
import com.deniscerri.ytdlnis.database.models.AudioPreferences import com.deniscerri.ytdlnis.database.models.AudioPreferences
import com.deniscerri.ytdlnis.database.models.ChapterItem
import com.deniscerri.ytdlnis.database.models.Format import com.deniscerri.ytdlnis.database.models.Format
import com.deniscerri.ytdlnis.database.models.VideoPreferences import com.deniscerri.ytdlnis.database.models.VideoPreferences
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
@ -50,4 +51,16 @@ class Converters {
fun videoPreferencesToString(videoPreferences: VideoPreferences): String = Gson().toJson(videoPreferences) fun videoPreferencesToString(videoPreferences: VideoPreferences): String = Gson().toJson(videoPreferences)
@TypeConverter @TypeConverter
fun stringToVideoPreferences(string: String): VideoPreferences = Gson().fromJson(string, VideoPreferences::class.java) fun stringToVideoPreferences(string: String): VideoPreferences = Gson().fromJson(string, VideoPreferences::class.java)
@TypeConverter
fun stringToListOfChapters(value: String?): ArrayList<ChapterItem>? {
val listType: Type = object : TypeToken<ArrayList<ChapterItem?>?>() {}.type
return Gson().fromJson(value, listType)
}
@TypeConverter
fun listOfChaptersToString(list: ArrayList<ChapterItem?>?): String {
val gson = Gson()
return gson.toJson(list)
}
} }

View file

@ -8,12 +8,13 @@ import com.deniscerri.ytdlnis.database.models.*
@TypeConverters(Converters::class) @TypeConverters(Converters::class)
@Database( @Database(
entities = [ResultItem::class, HistoryItem::class, DownloadItem::class, CommandTemplate::class, SearchHistoryItem::class, TemplateShortcut::class, CookieItem::class], entities = [ResultItem::class, HistoryItem::class, DownloadItem::class, CommandTemplate::class, SearchHistoryItem::class, TemplateShortcut::class, CookieItem::class],
version = 5, version = 6,
autoMigrations = [ autoMigrations = [
AutoMigration (from = 1, to = 2), AutoMigration (from = 1, to = 2),
AutoMigration (from = 2, to = 3), AutoMigration (from = 2, to = 3),
AutoMigration (from = 3, to = 4), AutoMigration (from = 3, to = 4),
AutoMigration (from = 4, to = 5) AutoMigration (from = 4, to = 5),
AutoMigration (from = 5, to = 6)
] ]
) )
abstract class DBManager : RoomDatabase(){ abstract class DBManager : RoomDatabase(){

View file

@ -16,6 +16,8 @@ data class DownloadItem(
var duration: String, var duration: String,
var type: DownloadViewModel.Type, var type: DownloadViewModel.Type,
var format: Format, var format: Format,
@ColumnInfo(defaultValue = "Default")
var container: String,
@ColumnInfo(defaultValue = "") @ColumnInfo(defaultValue = "")
var downloadSections: String, var downloadSections: String,
val allFormats: ArrayList<Format>, val allFormats: ArrayList<Format>,

View file

@ -13,7 +13,7 @@ data class Format(
var acodec: String = "", var acodec: String = "",
@SerializedName(value = "encoding") @SerializedName(value = "encoding")
var encoding: String = "", var encoding: String = "",
@SerializedName(value = "filesize", alternate = ["clen", "filesize_approx"]) @SerializedName(value = "filesize", alternate = ["clen", "filesize_approx", "contentLength"])
var filesize: Long = 0, var filesize: Long = 0,
@SerializedName(value = "format_note", alternate = ["resolution", "audioQuality", "quality"]) @SerializedName(value = "format_note", alternate = ["resolution", "audioQuality", "quality"])
var format_note: String = "", var format_note: String = "",
@ -21,5 +21,6 @@ data class Format(
var fps: String? = "", var fps: String? = "",
@SerializedName(value = "asr", alternate = ["audioSampleRate"]) @SerializedName(value = "asr", alternate = ["audioSampleRate"])
var asr: String? = "", var asr: String? = "",
@SerializedName(value = "url")
var url: String? = ""
) )

View file

@ -1,5 +1,6 @@
package com.deniscerri.ytdlnis.database.models package com.deniscerri.ytdlnis.database.models
import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
@ -15,5 +16,8 @@ data class ResultItem(
val website: String, val website: String,
var playlistTitle: String, var playlistTitle: String,
var formats: ArrayList<Format>, var formats: ArrayList<Format>,
@ColumnInfo(defaultValue = "")
var urls: String,
var chapters: ArrayList<ChapterItem>?,
var creationTime: Long = System.currentTimeMillis() / 1000, var creationTime: Long = System.currentTimeMillis() / 1000,
) )

View file

@ -10,7 +10,7 @@ class CommandTemplateRepository(private val commandDao: CommandTemplateDao) {
val shortcuts : LiveData<List<TemplateShortcut>> = commandDao.getAllShortcutsLiveData() val shortcuts : LiveData<List<TemplateShortcut>> = commandDao.getAllShortcutsLiveData()
fun getAll() : List<CommandTemplate> { fun getAll() : List<CommandTemplate> {
return commandDao.getAllTemplates(); return commandDao.getAllTemplates()
} }
fun getTotalNumber() : Int { fun getTotalNumber() : Int {
@ -34,7 +34,7 @@ class CommandTemplateRepository(private val commandDao: CommandTemplateDao) {
} }
fun getAllShortCuts() : List<TemplateShortcut> { fun getAllShortCuts() : List<TemplateShortcut> {
return commandDao.getAllShortcuts(); return commandDao.getAllShortcuts()
} }
suspend fun insertShortcut(item: TemplateShortcut){ suspend fun insertShortcut(item: TemplateShortcut){

View file

@ -59,9 +59,8 @@ class HistoryRepository(private val historyDao: HistoryDao) {
} }
suspend fun clearDeletedHistory(){ suspend fun clearDeletedHistory(){
val fileUtil = FileUtil()
items.value?.forEach { item -> items.value?.forEach { item ->
if (!fileUtil.exists(item.downloadPath)){ if (!FileUtil.exists(item.downloadPath)){
historyDao.delete(item.id) historyDao.delete(item.id)
} }
} }

View file

@ -33,6 +33,7 @@ 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.models.VideoPreferences import com.deniscerri.ytdlnis.database.models.VideoPreferences
import com.deniscerri.ytdlnis.database.repository.DownloadRepository import com.deniscerri.ytdlnis.database.repository.DownloadRepository
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.InfoUtil import com.deniscerri.ytdlnis.util.InfoUtil
import com.deniscerri.ytdlnis.work.DownloadWorker import com.deniscerri.ytdlnis.work.DownloadWorker
import com.google.gson.Gson import com.google.gson.Gson
@ -46,7 +47,7 @@ import java.util.Locale
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class DownloadViewModel(private val application: Application) : AndroidViewModel(application) { class DownloadViewModel(application: Application) : AndroidViewModel(application) {
private val repository : DownloadRepository private val repository : DownloadRepository
private val sharedPreferences: SharedPreferences private val sharedPreferences: SharedPreferences
private val commandTemplateDao: CommandTemplateDao private val commandTemplateDao: CommandTemplateDao
@ -152,9 +153,9 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
} }
val downloadPath = when(type){ val downloadPath = when(type){
Type.audio -> sharedPreferences.getString("music_path", File(Environment.DIRECTORY_DOWNLOADS, "YTDLnis/Audio").absolutePath) Type.audio -> sharedPreferences.getString("music_path", FileUtil.getDefautAudioPath())
Type.video -> sharedPreferences.getString("video_path", File(Environment.DIRECTORY_DOWNLOADS, "YTDLnis/Video").absolutePath) Type.video -> sharedPreferences.getString("video_path", FileUtil.getDefautVideoPath())
else -> sharedPreferences.getString("command_path", File(Environment.DIRECTORY_DOWNLOADS, "YTDLnis/Command").absolutePath) else -> sharedPreferences.getString("command_path", FileUtil.getDefaultCommandPath())
} }
val sponsorblock = sharedPreferences.getStringSet("sponsorblock_filters", emptySet()) val sponsorblock = sharedPreferences.getStringSet("sponsorblock_filters", emptySet())
@ -170,6 +171,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
resultItem.duration, resultItem.duration,
type, type,
getFormat(resultItem.formats, type), getFormat(resultItem.formats, type),
sharedPreferences.getString("video_format", "Default")!!,
"", "",
resultItem.formats, resultItem.formats,
downloadPath!!, resultItem.website, "", resultItem.playlistTitle, audioPreferences, videoPreferences,customFileNameTemplate!!, saveThumb, DownloadRepository.Status.Processing.toString(), 0 downloadPath!!, resultItem.website, "", resultItem.playlistTitle, audioPreferences, videoPreferences,customFileNameTemplate!!, saveThumb, DownloadRepository.Status.Processing.toString(), 0
@ -188,6 +190,8 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
downloadItem.website, downloadItem.website,
downloadItem.playlistTitle, downloadItem.playlistTitle,
downloadItem.allFormats, downloadItem.allFormats,
"",
arrayListOf(),
System.currentTimeMillis() System.currentTimeMillis()
) )
@ -204,6 +208,8 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
downloadItem.website, downloadItem.website,
"", "",
arrayListOf(), arrayListOf(),
"",
arrayListOf(),
System.currentTimeMillis() System.currentTimeMillis()
) )
@ -220,6 +226,8 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
"", "",
"", "",
arrayListOf(), arrayListOf(),
"",
arrayListOf(),
System.currentTimeMillis() System.currentTimeMillis()
) )
@ -247,9 +255,15 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
} }
val downloadPath = when(historyItem.type){ val downloadPath = when(historyItem.type){
Type.audio -> sharedPreferences.getString("music_path", File(Environment.DIRECTORY_DOWNLOADS, "YTDLnis/Audio").absolutePath) Type.audio -> sharedPreferences.getString("music_path", FileUtil.getDefautAudioPath())
Type.video -> sharedPreferences.getString("video_path", File(Environment.DIRECTORY_DOWNLOADS, "YTDLnis/Video").absolutePath) Type.video -> sharedPreferences.getString("video_path", FileUtil.getDefautVideoPath())
else -> sharedPreferences.getString("command_path", File(Environment.DIRECTORY_DOWNLOADS, "YTDLnis/Command").absolutePath) else -> sharedPreferences.getString("command_path", FileUtil.getDefaultCommandPath())
}
val container = when(historyItem.type){
Type.audio -> sharedPreferences.getString("audio_format", "Default")!!
Type.video -> sharedPreferences.getString("video_format", "Default")!!
else -> ""
} }
val sponsorblock = sharedPreferences.getStringSet("sponsorblock_filters", emptySet()) val sponsorblock = sharedPreferences.getStringSet("sponsorblock_filters", emptySet())
@ -265,6 +279,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
historyItem.duration, historyItem.duration,
historyItem.type, historyItem.type,
historyItem.format, historyItem.format,
container,
"", "",
ArrayList(), ArrayList(),
downloadPath!!, historyItem.website, "", "", audioPreferences, videoPreferences,customFileNameTemplate!!, saveThumb, DownloadRepository.Status.Processing.toString(), 0 downloadPath!!, historyItem.website, "", "", audioPreferences, videoPreferences,customFileNameTemplate!!, saveThumb, DownloadRepository.Status.Processing.toString(), 0
@ -438,24 +453,6 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
} }
} }
//if the download was quick downloaded and are incomplete
if (queuedItems.count() == 1){
val item = queuedItems.first()
if (item.title.isEmpty() || item.author.isEmpty() || item.thumb.isEmpty()){
runCatching {
val info = infoUtil.getMissingInfo(item.url)
if (item.title.isEmpty()) item.title = info?.title.toString()
if (item.author.isEmpty()) item.author = info?.author.toString()
item.duration = info?.duration.toString()
item.website = info?.website.toString()
if (item.thumb.isEmpty()) item.thumb = info?.thumb.toString()
repository.update(item)
}.onFailure { err ->
Log.e("error", err.message.toString())
}
}
}
queuedItems.forEach { queuedItems.forEach {
val currentTime = System.currentTimeMillis() val currentTime = System.currentTimeMillis()
var delay = if (it.downloadStartTime != 0L){ var delay = if (it.downloadStartTime != 0L){

View file

@ -90,8 +90,7 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application)
fun delete(item: HistoryItem, deleteFile: Boolean) = viewModelScope.launch(Dispatchers.IO){ fun delete(item: HistoryItem, deleteFile: Boolean) = viewModelScope.launch(Dispatchers.IO){
repository.delete(item) repository.delete(item)
if (deleteFile){ if (deleteFile){
val fileUtil = FileUtil() FileUtil.deleteFile(item.downloadPath)
fileUtil.deleteFile(item.downloadPath)
} }
} }

View file

@ -17,7 +17,7 @@ class SharedDownloadNotificationReceiver : AppCompatActivity() {
val notificationId = intent.getIntExtra("notificationID", 0) val notificationId = intent.getIntExtra("notificationID", 0)
if (message != null && path != null) { if (message != null && path != null) {
if (notificationId != 0) NotificationUtil(this).cancelDownloadNotification(notificationId) if (notificationId != 0) NotificationUtil(this).cancelDownloadNotification(notificationId)
val uiUtil = UiUtil(FileUtil()) val uiUtil = UiUtil()
uiUtil.shareFileIntent(this, listOf(path)) uiUtil.shareFileIntent(this, listOf(path))
this.finishAffinity() this.finishAffinity()
} }

View file

@ -11,10 +11,5 @@ open class BaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
ThemeUtil.updateTheme(this) ThemeUtil.updateTheme(this)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
ViewCompat.setOnApplyWindowInsetsListener(window.decorView) { v, insets ->
v.setPadding(0, 0, 0, 0)
insets
}
} }
} }

View file

@ -37,7 +37,6 @@ import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding
import com.deniscerri.ytdlnis.ui.downloadcard.DownloadBottomSheetDialog import com.deniscerri.ytdlnis.ui.downloadcard.DownloadBottomSheetDialog
import com.deniscerri.ytdlnis.ui.downloadcard.DownloadMultipleBottomSheetDialog import com.deniscerri.ytdlnis.ui.downloadcard.DownloadMultipleBottomSheetDialog
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.InfoUtil import com.deniscerri.ytdlnis.util.InfoUtil
import com.facebook.shimmer.ShimmerFrameLayout import com.facebook.shimmer.ShimmerFrameLayout
import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout
@ -88,7 +87,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
private var uiHandler: Handler? = null private var uiHandler: Handler? = null
private var resultsList: List<ResultItem?>? = null private var resultsList: List<ResultItem?>? = null
private var selectedObjects: ArrayList<ResultItem>? = null private var selectedObjects: ArrayList<ResultItem>? = null
private var fileUtil: FileUtil? = null
private var quickLaunchSheet = false private var quickLaunchSheet = false
private var sharedPreferences: SharedPreferences? = null private var sharedPreferences: SharedPreferences? = null
private var _binding : FragmentHomeBinding? = null private var _binding : FragmentHomeBinding? = null
@ -114,7 +112,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
fragmentContext = context fragmentContext = context
layoutinflater = LayoutInflater.from(context) layoutinflater = LayoutInflater.from(context)
fileUtil = FileUtil()
uiHandler = Handler(Looper.getMainLooper()) uiHandler = Handler(Looper.getMainLooper())
selectedObjects = ArrayList() selectedObjects = ArrayList()

View file

@ -50,7 +50,7 @@ class ConfigureDownloadBottomSheetDialog(private val resultItem: ResultItem, pri
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java] resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
commandTemplateDao = DBManager.getInstance(requireContext()).commandTemplateDao commandTemplateDao = DBManager.getInstance(requireContext()).commandTemplateDao
onDownloadItemUpdateListener = listener onDownloadItemUpdateListener = listener
uiUtil = UiUtil(FileUtil()) uiUtil = UiUtil()
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -94,6 +94,13 @@ class ConfigureDownloadBottomSheetDialog(private val resultItem: ResultItem, pri
} }
} }
//check if the item has formats and its audio-only
val isAudioOnly = resultItem.formats.isNotEmpty() && resultItem.formats.none { !it.format_note.contains("audio") }
if (isAudioOnly){
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(1)?.isClickable = true
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(1)?.alpha = 0.3f
}
val fragmentManager = parentFragmentManager val fragmentManager = parentFragmentManager
fragmentAdapter = DownloadFragmentAdapter( fragmentAdapter = DownloadFragmentAdapter(
@ -111,8 +118,14 @@ class ConfigureDownloadBottomSheetDialog(private val resultItem: ResultItem, pri
viewPager2.setCurrentItem(0, false) viewPager2.setCurrentItem(0, false)
} }
Type.video -> { Type.video -> {
tabLayout.selectTab(tabLayout.getTabAt(1)) if (isAudioOnly){
viewPager2.setCurrentItem(1, false) tabLayout.getTabAt(0)!!.select()
viewPager2.setCurrentItem(0, false)
Toast.makeText(context, getString(R.string.audio_only_item), Toast.LENGTH_SHORT).show()
}else{
tabLayout.getTabAt(1)!!.select()
viewPager2.setCurrentItem(1, false)
}
} }
else -> { else -> {
tabLayout.selectTab(tabLayout.getTabAt(2)) tabLayout.selectTab(tabLayout.getTabAt(2))
@ -127,6 +140,9 @@ class ConfigureDownloadBottomSheetDialog(private val resultItem: ResultItem, pri
if (tab!!.position == 2 && commandTemplateNr == 0){ if (tab!!.position == 2 && commandTemplateNr == 0){
tabLayout.selectTab(tabLayout.getTabAt(1)) tabLayout.selectTab(tabLayout.getTabAt(1))
Toast.makeText(context, getString(R.string.add_template_first), Toast.LENGTH_SHORT).show() Toast.makeText(context, getString(R.string.add_template_first), Toast.LENGTH_SHORT).show()
}else if (tab.position == 1 && isAudioOnly){
tabLayout.selectTab(tabLayout.getTabAt(0))
Toast.makeText(context, getString(R.string.audio_only_item), Toast.LENGTH_SHORT).show()
}else{ }else{
viewPager2.setCurrentItem(tab.position, false) viewPager2.setCurrentItem(tab.position, false)
} }

View file

@ -60,9 +60,8 @@ import java.util.*
import kotlin.properties.Delegates import kotlin.properties.Delegates
class CutVideoBottomSheetDialog(private val item: DownloadItem, private val listener: VideoCutListener) : BottomSheetDialogFragment() { class CutVideoBottomSheetDialog(private val item: DownloadItem, private val urls : String?, private var chapters: List<ChapterItem>?, private val listener: VideoCutListener) : BottomSheetDialogFragment() {
private lateinit var behavior: BottomSheetBehavior<View> private lateinit var behavior: BottomSheetBehavior<View>
private lateinit var fileUtil: FileUtil
private lateinit var infoUtil: InfoUtil private lateinit var infoUtil: InfoUtil
private lateinit var uiUtil: UiUtil private lateinit var uiUtil: UiUtil
private lateinit var player: Player private lateinit var player: Player
@ -86,14 +85,12 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
private lateinit var chipGroup : ChipGroup private lateinit var chipGroup : ChipGroup
private var timeSeconds by Delegates.notNull<Int>() private var timeSeconds by Delegates.notNull<Int>()
private lateinit var chapters: List<ChapterItem>
private lateinit var selectedCuts: MutableList<String> private lateinit var selectedCuts: MutableList<String>
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
fileUtil = FileUtil() uiUtil = UiUtil()
uiUtil = UiUtil(fileUtil)
infoUtil = InfoUtil(requireActivity().applicationContext) infoUtil = InfoUtil(requireActivity().applicationContext)
} }
@ -150,7 +147,7 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
val videoView = view.findViewById<StyledPlayerView>(R.id.video_view) val videoView = view.findViewById<StyledPlayerView>(R.id.video_view)
videoView.player = player videoView.player = player
timeSeconds = convertStringToTimestamp(item.duration) timeSeconds = convertStringToTimestamp(item.duration)
chapters = emptyList() if (chapters == null) chapters = emptyList()
//cut section //cut section
cutSection = view.findViewById(R.id.cut_section) cutSection = view.findViewById(R.id.cut_section)
@ -166,7 +163,7 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
okBtn = view.findViewById(R.id.okButton) okBtn = view.findViewById(R.id.okButton)
suggestedChips = view.findViewById(R.id.chapters) suggestedChips = view.findViewById(R.id.chapters)
suggestedChapters = view.findViewById(R.id.suggested_cuts) suggestedChapters = view.findViewById(R.id.suggested_cuts)
initCutSection()
//cut list section //cut list section
cutListSection = view.findViewById(R.id.list_section) cutListSection = view.findViewById(R.id.list_section)
@ -174,6 +171,7 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
resetBtn = view.findViewById(R.id.reset_all) resetBtn = view.findViewById(R.id.reset_all)
chipGroup = view.findViewById(R.id.cut_list_chip_group) chipGroup = view.findViewById(R.id.cut_list_chip_group)
selectedCuts = if (chipGroup.childCount == 0){ selectedCuts = if (chipGroup.childCount == 0){
mutableListOf() mutableListOf()
}else { }else {
@ -183,6 +181,7 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
chipGroup.children.map { (it as Chip).text.toString() }.toMutableList() chipGroup.children.map { (it as Chip).text.toString() }.toMutableList()
} }
initCutSection()
initCutListSection() initCutListSection()
if (item.downloadSections.isBlank()) cutSection.visibility = View.VISIBLE if (item.downloadSections.isBlank()) cutSection.visibility = View.VISIBLE
@ -190,16 +189,22 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
lifecycleScope.launch { lifecycleScope.launch {
try { try {
val data = withContext(Dispatchers.IO){ val data : MutableList<String?> = withContext(Dispatchers.IO){
infoUtil.getStreamingUrlAndChapters(item.url) if (urls.isNullOrEmpty() || chapters.isNullOrEmpty()) {
infoUtil.getStreamingUrlAndChapters(item.url)
}else {
urls.split("\n").toMutableList()
}
} }
if (data.isEmpty()) throw Exception("No Data found!")
try{
val listType: Type = object : TypeToken<List<ChapterItem>>() {}.type
chapters = Gson().fromJson(data.first().toString(), listType)
}catch (ignored: Exception) {}
data.removeFirst()
if (data.isEmpty()) throw Exception("No Data found!")
if (chapters!!.isEmpty()){
try{
val listType: Type = object : TypeToken<List<ChapterItem>>() {}.type
chapters = Gson().fromJson(data.first().toString(), listType)
}catch (ignored: Exception) {}
data.removeFirst()
}
if (data.isEmpty()) throw Exception("No Streaming URL found!") if (data.isEmpty()) throw Exception("No Streaming URL found!")
if (data.size == 2){ if (data.size == 2){
@ -375,11 +380,11 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
} }
private fun populateSuggestedChapters(){ private fun populateSuggestedChapters(){
if (chapters.isEmpty()) suggestedChapters.visibility = View.GONE if (chapters!!.isEmpty()) suggestedChapters.visibility = View.GONE
else { else {
suggestedChapters.visibility = View.VISIBLE suggestedChapters.visibility = View.VISIBLE
suggestedChips.removeAllViews() suggestedChips.removeAllViews()
chapters.forEach { chapters!!.forEach {
val chip = layoutInflater.inflate(R.layout.suggestion_chip, chipGroup, false) as Chip val chip = layoutInflater.inflate(R.layout.suggestion_chip, chipGroup, false) as Chip
chip.text = it.title chip.text = it.title
chip.chipBackgroundColor = ColorStateList.valueOf(MaterialColors.getColor(requireContext(), R.attr.colorSecondaryContainer, Color.BLACK)) chip.chipBackgroundColor = ColorStateList.valueOf(MaterialColors.getColor(requireContext(), R.attr.colorSecondaryContainer, Color.BLACK))

View file

@ -51,7 +51,6 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
private var activity: Activity? = null private var activity: Activity? = null
private lateinit var downloadViewModel : DownloadViewModel private lateinit var downloadViewModel : DownloadViewModel
private lateinit var resultViewModel : ResultViewModel private lateinit var resultViewModel : ResultViewModel
private lateinit var fileUtil : FileUtil
private lateinit var uiUtil : UiUtil private lateinit var uiUtil : UiUtil
private lateinit var title : TextInputLayout private lateinit var title : TextInputLayout
@ -71,8 +70,7 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java] resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
fileUtil = FileUtil() uiUtil = UiUtil()
uiUtil = UiUtil(fileUtil)
return fragmentView return fragmentView
} }
@ -112,7 +110,7 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
}) })
saveDir = view.findViewById(R.id.outputPath) saveDir = view.findViewById(R.id.outputPath)
saveDir.editText!!.setText( saveDir.editText!!.setText(
fileUtil.formatPath(downloadItem.downloadPath) FileUtil.formatPath(downloadItem.downloadPath)
) )
saveDir.editText!!.isFocusable = false saveDir.editText!!.isFocusable = false
saveDir.editText!!.isClickable = true saveDir.editText!!.isClickable = true
@ -124,8 +122,8 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
audioPathResultLauncher.launch(intent) audioPathResultLauncher.launch(intent)
} }
freeSpace = view.findViewById(R.id.freespace) freeSpace = view.findViewById(R.id.freespace)
val free = fileUtil.convertFileSize( val free = FileUtil.convertFileSize(
File(fileUtil.formatPath(downloadItem.downloadPath)).freeSpace) File(FileUtil.formatPath(downloadItem.downloadPath)).freeSpace)
freeSpace.text = String.format( getString(R.string.freespace) + ": " + free) freeSpace.text = String.format( getString(R.string.freespace) + ": " + free)
if (free == "?") freeSpace.visibility = View.GONE if (free == "?") freeSpace.visibility = View.GONE
@ -157,7 +155,6 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
} }
} }
formats = allFormats.first().toMutableList() formats = allFormats.first().toMutableList()
downloadItem.format.container = container.editText?.text.toString()
} }
} }
formatCard.setOnClickListener{ formatCard.setOnClickListener{
@ -181,13 +178,13 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
) )
) )
downloadItem.format.container = if (containerPreference == getString(R.string.defaultValue)) "" else containerPreference!! downloadItem.container = if (containerPreference == getString(R.string.defaultValue)) "" else containerPreference!!
containerAutoCompleteTextView.setText(downloadItem.format.container.ifEmpty { getString(R.string.defaultValue) }, false) containerAutoCompleteTextView.setText(downloadItem.container.ifEmpty { getString(R.string.defaultValue) }, false)
(container!!.editText as AutoCompleteTextView?)!!.onItemClickListener = (container!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long -> AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long ->
downloadItem.format.container = containers[index] downloadItem.container = containers[index]
if (containers[index] == getString(R.string.defaultValue)) downloadItem.format.container = "" if (containers[index] == getString(R.string.defaultValue)) downloadItem.container = ""
} }
val embedThumb = view.findViewById<Chip>(R.id.embed_thumb) val embedThumb = view.findViewById<Chip>(R.id.embed_thumb)
@ -313,7 +310,7 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
} }
cut.setOnClickListener { cut.setOnClickListener {
if (parentFragmentManager.findFragmentByTag("cutVideoSheet") == null){ if (parentFragmentManager.findFragmentByTag("cutVideoSheet") == null){
val bottomSheet = CutVideoBottomSheetDialog(downloadItem, cutVideoListener) val bottomSheet = CutVideoBottomSheetDialog(downloadItem, resultItem.urls, resultItem.chapters, cutVideoListener)
bottomSheet.show(parentFragmentManager, "cutVideoSheet") bottomSheet.show(parentFragmentManager, "cutVideoSheet")
} }
} }
@ -340,10 +337,10 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
} }
downloadItem.downloadPath = result.data?.data.toString() downloadItem.downloadPath = result.data?.data.toString()
//downloadViewModel.updateDownload(downloadItem) //downloadViewModel.updateDownload(downloadItem)
saveDir.editText?.setText(fileUtil.formatPath(result.data?.data.toString()), TextView.BufferType.EDITABLE) saveDir.editText?.setText(FileUtil.formatPath(result.data?.data.toString()), TextView.BufferType.EDITABLE)
val free = fileUtil.convertFileSize( val free = FileUtil.convertFileSize(
File(fileUtil.formatPath(downloadItem.downloadPath)).freeSpace) File(FileUtil.formatPath(downloadItem.downloadPath)).freeSpace)
freeSpace.text = String.format( getString(R.string.freespace) + ": " + free) freeSpace.text = String.format( getString(R.string.freespace) + ": " + free)
if (free == "?") freeSpace.visibility = View.GONE if (free == "?") freeSpace.visibility = View.GONE
} }

View file

@ -59,7 +59,7 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java] resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
commandTemplateDao = DBManager.getInstance(requireContext()).commandTemplateDao commandTemplateDao = DBManager.getInstance(requireContext()).commandTemplateDao
uiUtil = UiUtil(FileUtil()) uiUtil = UiUtil()
infoUtil = InfoUtil(requireContext()) infoUtil = InfoUtil(requireContext())
} }
@ -103,6 +103,13 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
} }
} }
//check if the item has formats and its audio-only
val isAudioOnly = resultItem.formats.isNotEmpty() && resultItem.formats.none { !it.format_note.contains("audio") }
if (isAudioOnly){
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(1)?.isClickable = true
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(1)?.alpha = 0.3f
}
val fragmentManager = parentFragmentManager val fragmentManager = parentFragmentManager
fragmentAdapter = DownloadFragmentAdapter( fragmentAdapter = DownloadFragmentAdapter(
@ -121,8 +128,14 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
viewPager2.setCurrentItem(0, false) viewPager2.setCurrentItem(0, false)
} }
Type.video -> { Type.video -> {
tabLayout.getTabAt(1)!!.select() if (isAudioOnly){
viewPager2.setCurrentItem(1, false) tabLayout.getTabAt(0)!!.select()
viewPager2.setCurrentItem(0, false)
Toast.makeText(context, getString(R.string.audio_only_item), Toast.LENGTH_SHORT).show()
}else{
tabLayout.getTabAt(1)!!.select()
viewPager2.setCurrentItem(1, false)
}
} }
else -> { else -> {
tabLayout.getTabAt(2)!!.select() tabLayout.getTabAt(2)!!.select()
@ -138,7 +151,11 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
if (tab!!.position == 2 && commandTemplateNr == 0){ if (tab!!.position == 2 && commandTemplateNr == 0){
tabLayout.selectTab(tabLayout.getTabAt(1)) tabLayout.selectTab(tabLayout.getTabAt(1))
Toast.makeText(context, getString(R.string.add_template_first), Toast.LENGTH_SHORT).show() Toast.makeText(context, getString(R.string.add_template_first), Toast.LENGTH_SHORT).show()
}else{ }else if (tab.position == 1 && isAudioOnly){
tabLayout.selectTab(tabLayout.getTabAt(0))
Toast.makeText(context, getString(R.string.audio_only_item), Toast.LENGTH_SHORT).show()
}
else{
viewPager2.setCurrentItem(tab.position, false) viewPager2.setCurrentItem(tab.position, false)
} }
} }

View file

@ -42,7 +42,6 @@ class DownloadCommandFragment(private val resultItem: ResultItem, private var cu
private var activity: Activity? = null private var activity: Activity? = null
private lateinit var downloadViewModel : DownloadViewModel private lateinit var downloadViewModel : DownloadViewModel
private lateinit var commandTemplateViewModel : CommandTemplateViewModel private lateinit var commandTemplateViewModel : CommandTemplateViewModel
private lateinit var fileUtil : FileUtil
private lateinit var uiUtil : UiUtil private lateinit var uiUtil : UiUtil
private lateinit var saveDir : TextInputLayout private lateinit var saveDir : TextInputLayout
private lateinit var freeSpace : TextView private lateinit var freeSpace : TextView
@ -59,8 +58,7 @@ class DownloadCommandFragment(private val resultItem: ResultItem, private var cu
activity = getActivity() activity = getActivity()
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
commandTemplateViewModel = ViewModelProvider(this)[CommandTemplateViewModel::class.java] commandTemplateViewModel = ViewModelProvider(this)[CommandTemplateViewModel::class.java]
fileUtil = FileUtil() uiUtil = UiUtil()
uiUtil = UiUtil(fileUtil)
return fragmentView return fragmentView
} }
@ -138,7 +136,7 @@ class DownloadCommandFragment(private val resultItem: ResultItem, private var cu
saveDir = view.findViewById(R.id.outputPath) saveDir = view.findViewById(R.id.outputPath)
saveDir.editText!!.setText( saveDir.editText!!.setText(
fileUtil.formatPath(downloadItem.downloadPath) FileUtil.formatPath(downloadItem.downloadPath)
) )
saveDir.editText!!.isFocusable = false saveDir.editText!!.isFocusable = false
saveDir.editText!!.isClickable = true saveDir.editText!!.isClickable = true
@ -151,8 +149,8 @@ class DownloadCommandFragment(private val resultItem: ResultItem, private var cu
} }
freeSpace = view.findViewById(R.id.freespace) freeSpace = view.findViewById(R.id.freespace)
freeSpace.text = String.format(getString(R.string.freespace) + ": " + fileUtil.convertFileSize( freeSpace.text = String.format(getString(R.string.freespace) + ": " + FileUtil.convertFileSize(
File(fileUtil.formatPath(downloadItem.downloadPath)).freeSpace File(FileUtil.formatPath(downloadItem.downloadPath)).freeSpace
)) ))
val newTemplate : Chip = view.findViewById(R.id.newTemplate) val newTemplate : Chip = view.findViewById(R.id.newTemplate)
@ -226,10 +224,10 @@ class DownloadCommandFragment(private val resultItem: ResultItem, private var cu
) )
} }
downloadItem.downloadPath = result.data?.data.toString() downloadItem.downloadPath = result.data?.data.toString()
saveDir.editText?.setText(fileUtil.formatPath(result.data?.data.toString()), TextView.BufferType.EDITABLE) saveDir.editText?.setText(FileUtil.formatPath(result.data?.data.toString()), TextView.BufferType.EDITABLE)
freeSpace.text = String.format(getString(R.string.freespace) + ": " + fileUtil.convertFileSize( freeSpace.text = String.format(getString(R.string.freespace) + ": " + FileUtil.convertFileSize(
File(fileUtil.formatPath(downloadItem.downloadPath)).freeSpace File(FileUtil.formatPath(downloadItem.downloadPath)).freeSpace
)) ))
} }
} }

View file

@ -59,7 +59,6 @@ class DownloadMultipleBottomSheetDialog(private var results: List<ResultItem?>,
private lateinit var listAdapter : ConfigureMultipleDownloadsAdapter private lateinit var listAdapter : ConfigureMultipleDownloadsAdapter
private lateinit var recyclerView: RecyclerView private lateinit var recyclerView: RecyclerView
private lateinit var behavior: BottomSheetBehavior<View> private lateinit var behavior: BottomSheetBehavior<View>
private lateinit var fileUtil: FileUtil
private lateinit var uiUtil: UiUtil private lateinit var uiUtil: UiUtil
private lateinit var bottomAppBar: BottomAppBar private lateinit var bottomAppBar: BottomAppBar
@ -69,8 +68,7 @@ class DownloadMultipleBottomSheetDialog(private var results: List<ResultItem?>,
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java] resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
commandTemplateViewModel = ViewModelProvider(this)[CommandTemplateViewModel::class.java] commandTemplateViewModel = ViewModelProvider(this)[CommandTemplateViewModel::class.java]
fileUtil = FileUtil() uiUtil = UiUtil()
uiUtil = UiUtil(fileUtil)
} }
@SuppressLint("RestrictedApi", "NotifyDataSetChanged") @SuppressLint("RestrictedApi", "NotifyDataSetChanged")
@ -291,7 +289,7 @@ class DownloadMultipleBottomSheetDialog(private var results: List<ResultItem?>,
items.forEach { items.forEach {
it.downloadPath = result.data?.data.toString() it.downloadPath = result.data?.data.toString()
} }
Toast.makeText(requireContext(), "Changed every item's download path to: ${fileUtil.formatPath(result.data!!.data.toString())}", Toast.LENGTH_LONG).show() Toast.makeText(requireContext(), "Changed every item's download path to: ${FileUtil.formatPath(result.data!!.data.toString())}", Toast.LENGTH_LONG).show()
} }
} }

View file

@ -51,7 +51,6 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
private var activity: Activity? = null private var activity: Activity? = null
private lateinit var downloadViewModel : DownloadViewModel private lateinit var downloadViewModel : DownloadViewModel
private lateinit var resultViewModel: ResultViewModel private lateinit var resultViewModel: ResultViewModel
private lateinit var fileUtil : FileUtil
private lateinit var uiUtil : UiUtil private lateinit var uiUtil : UiUtil
private lateinit var title : TextInputLayout private lateinit var title : TextInputLayout
@ -72,8 +71,7 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
resultViewModel = ViewModelProvider(this@DownloadVideoFragment)[ResultViewModel::class.java] resultViewModel = ViewModelProvider(this@DownloadVideoFragment)[ResultViewModel::class.java]
fileUtil = FileUtil() uiUtil = UiUtil()
uiUtil = UiUtil(fileUtil)
return fragmentView return fragmentView
} }
@ -114,7 +112,7 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
saveDir = view.findViewById(R.id.outputPath) saveDir = view.findViewById(R.id.outputPath)
saveDir.editText!!.setText( saveDir.editText!!.setText(
fileUtil.formatPath(downloadItem.downloadPath) FileUtil.formatPath(downloadItem.downloadPath)
) )
saveDir.editText!!.isFocusable = false saveDir.editText!!.isFocusable = false
saveDir.editText!!.isClickable = true saveDir.editText!!.isClickable = true
@ -127,8 +125,8 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
} }
freeSpace = view.findViewById(R.id.freespace) freeSpace = view.findViewById(R.id.freespace)
val free = fileUtil.convertFileSize( val free = FileUtil.convertFileSize(
File(fileUtil.formatPath(downloadItem.downloadPath)).freeSpace) File(FileUtil.formatPath(downloadItem.downloadPath)).freeSpace)
freeSpace.text = String.format( getString(R.string.freespace) + ": " + free) freeSpace.text = String.format( getString(R.string.freespace) + ": " + free)
if (free == "?") freeSpace.visibility = View.GONE if (free == "?") freeSpace.visibility = View.GONE
@ -148,7 +146,7 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
val formatCard = view.findViewById<MaterialCardView>(R.id.format_card_constraintLayout) val formatCard = view.findViewById<MaterialCardView>(R.id.format_card_constraintLayout)
val chosenFormat = downloadItem.format val chosenFormat = downloadItem.format
uiUtil.populateFormatCard(formatCard, chosenFormat, null) uiUtil.populateFormatCard(formatCard, chosenFormat, downloadItem.allFormats.filter { downloadItem.videoPreferences.audioFormatIDs.contains(it.format_id) })
val listener = object : OnFormatClickListener { val listener = object : OnFormatClickListener {
override fun onFormatClick(allFormats: List<List<Format>>, item: List<Format>) { override fun onFormatClick(allFormats: List<List<Format>>, item: List<Format>) {
downloadItem.format = item.first() downloadItem.format = item.first()
@ -162,7 +160,6 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
} }
formats = allFormats.first().toMutableList() formats = allFormats.first().toMutableList()
uiUtil.populateFormatCard(formatCard, item.first(), item.drop(1)) uiUtil.populateFormatCard(formatCard, item.first(), item.drop(1))
downloadItem.format.container = container.editText?.text.toString()
} }
} }
formatCard.setOnClickListener{ formatCard.setOnClickListener{
@ -185,15 +182,15 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
containers containers
) )
) )
downloadItem.format.container = if (containerPreference == getString(R.string.defaultValue)) "" else containerPreference!! downloadItem.container = if (containerPreference == getString(R.string.defaultValue)) "" else containerPreference!!
containerAutoCompleteTextView!!.setText( containerAutoCompleteTextView!!.setText(
downloadItem.format.container.ifEmpty { getString(R.string.defaultValue) }, downloadItem.container.ifEmpty { getString(R.string.defaultValue) },
false) false)
(container!!.editText as AutoCompleteTextView?)!!.onItemClickListener = (container!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long -> AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long ->
downloadItem.format.container = containers[index] downloadItem.container = containers[index]
if (containers[index] == getString(R.string.defaultValue)) downloadItem.format.container = "" if (containers[index] == getString(R.string.defaultValue)) downloadItem.container = ""
} }
@ -312,7 +309,7 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
} }
cut.setOnClickListener { cut.setOnClickListener {
if (parentFragmentManager.findFragmentByTag("cutVideoSheet") == null){ if (parentFragmentManager.findFragmentByTag("cutVideoSheet") == null){
val bottomSheet = CutVideoBottomSheetDialog(downloadItem, cutVideoListener) val bottomSheet = CutVideoBottomSheetDialog(downloadItem, resultItem.urls, resultItem.chapters, cutVideoListener)
bottomSheet.show(parentFragmentManager, "cutVideoSheet") bottomSheet.show(parentFragmentManager, "cutVideoSheet")
} }
} }
@ -332,17 +329,17 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
builder.setView(inputLayout) builder.setView(inputLayout)
builder.setPositiveButton( builder.setPositiveButton(
getString(R.string.ok) getString(R.string.ok)
) { dialog: DialogInterface?, which: Int -> ) { _: DialogInterface?, _: Int ->
downloadItem.customFileNameTemplate = editText.text.toString() downloadItem.customFileNameTemplate = editText.text.toString()
} }
// handle the negative button of the alert dialog // handle the negative button of the alert dialog
builder.setNegativeButton( builder.setNegativeButton(
getString(R.string.cancel) getString(R.string.cancel)
) { dialog: DialogInterface?, which: Int -> } ) { _: DialogInterface?, _: Int -> }
val dialog = builder.create() val dialog = builder.create()
editText.doOnTextChanged { text, start, before, count -> editText.doOnTextChanged { _, _, _, _ ->
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = editText.text.isNotEmpty() dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = editText.text.isNotEmpty()
} }
dialog.show() dialog.show()
@ -363,7 +360,7 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
subtitleLanguages.visibility = View.VISIBLE subtitleLanguages.visibility = View.VISIBLE
} }
saveSubtitles.setOnCheckedChangeListener { compoundButton, b -> saveSubtitles.setOnCheckedChangeListener { _, _ ->
if (saveSubtitles.isChecked) subtitleLanguages.visibility = View.VISIBLE if (saveSubtitles.isChecked) subtitleLanguages.visibility = View.VISIBLE
else subtitleLanguages.visibility = View.GONE else subtitleLanguages.visibility = View.GONE
downloadItem.videoPreferences.writeSubs = saveSubtitles.isChecked downloadItem.videoPreferences.writeSubs = saveSubtitles.isChecked
@ -376,7 +373,7 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
} }
val removeAudio = view.findViewById<Chip>(R.id.remove_audio) val removeAudio = view.findViewById<Chip>(R.id.remove_audio)
removeAudio.setOnCheckedChangeListener { compoundButton, b -> removeAudio.setOnCheckedChangeListener { _, _ ->
downloadItem.videoPreferences.removeAudio = removeAudio.isChecked downloadItem.videoPreferences.removeAudio = removeAudio.isChecked
} }
@ -398,11 +395,10 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
) )
} }
downloadItem.downloadPath = result.data?.data.toString() downloadItem.downloadPath = result.data?.data.toString()
//downloadviewmodel.updateDownload(downloadItem) saveDir.editText?.setText(FileUtil.formatPath(result.data?.data.toString()), TextView.BufferType.EDITABLE)
saveDir.editText?.setText(fileUtil.formatPath(result.data?.data.toString()), TextView.BufferType.EDITABLE)
val free = fileUtil.convertFileSize( val free = FileUtil.convertFileSize(
File(fileUtil.formatPath(downloadItem.downloadPath)).freeSpace) File(FileUtil.formatPath(downloadItem.downloadPath)).freeSpace)
freeSpace.text = String.format( getString(R.string.freespace) + ": " + free) freeSpace.text = String.format( getString(R.string.freespace) + ": " + free)
if (free == "?") freeSpace.visibility = View.GONE if (free == "?") freeSpace.visibility = View.GONE
} }

View file

@ -31,7 +31,6 @@ import java.util.*
class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, private var formats: List<List<Format>>, private val listener: OnFormatClickListener) : BottomSheetDialogFragment() { class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, private var formats: List<List<Format>>, private val listener: OnFormatClickListener) : BottomSheetDialogFragment() {
private lateinit var behavior: BottomSheetBehavior<View> private lateinit var behavior: BottomSheetBehavior<View>
private lateinit var fileUtil: FileUtil
private lateinit var infoUtil: InfoUtil private lateinit var infoUtil: InfoUtil
private lateinit var uiUtil: UiUtil private lateinit var uiUtil: UiUtil
private lateinit var sharedPreferences: SharedPreferences private lateinit var sharedPreferences: SharedPreferences
@ -48,8 +47,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
fileUtil = FileUtil() uiUtil = UiUtil()
uiUtil = UiUtil(fileUtil)
infoUtil = InfoUtil(requireActivity().applicationContext) infoUtil = InfoUtil(requireActivity().applicationContext)
formatCollection = mutableListOf() formatCollection = mutableListOf()
chosenFormats = listOf() chosenFormats = listOf()
@ -116,11 +114,12 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
} }
val refreshBtn = view.findViewById<Button>(R.id.format_refresh) val refreshBtn = view.findViewById<Button>(R.id.format_refresh)
if (formats.flatten().none { it.filesize == 0L } || items.isEmpty()) refreshBtn.visibility = View.GONE if (!hasGenericFormats || items.isEmpty()) refreshBtn.visibility = View.GONE
refreshBtn.setOnClickListener { refreshBtn.setOnClickListener {
lifecycleScope.launch { lifecycleScope.launch {
chosenFormats = emptyList()
try { try {
refreshBtn.isEnabled = false refreshBtn.isEnabled = false
formatListLinearLayout.visibility = View.GONE formatListLinearLayout.visibility = View.GONE
@ -132,9 +131,11 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
val res = withContext(Dispatchers.IO){ val res = withContext(Dispatchers.IO){
infoUtil.getFormats(items.first()!!.url) infoUtil.getFormats(items.first()!!.url)
} }
chosenFormats = res.formats.filter { it.filesize != 0L } res.filter { it.format_note != "storyboard" }
if(items.first()?.type == Type.audio){ if(items.first()?.type == Type.audio){
chosenFormats = chosenFormats.filter { it.format_note.contains("audio", ignoreCase = true) } chosenFormats = res.filter { it.format_note.contains("audio", ignoreCase = true) }
}else{
chosenFormats = res
} }
if (chosenFormats.isEmpty()) throw Exception() if (chosenFormats.isEmpty()) throw Exception()
//playlist format filtering //playlist format filtering

View file

@ -25,7 +25,7 @@ import com.google.android.material.progressindicator.LinearProgressIndicator
import com.yausername.youtubedl_android.YoutubeDL import com.yausername.youtubedl_android.YoutubeDL
class ActiveDownloadsFragment() : Fragment(), ActiveDownloadAdapter.OnItemClickListener, OnClickListener { class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickListener, OnClickListener {
private var _binding : FragmentHomeBinding? = null private var _binding : FragmentHomeBinding? = null
private var fragmentView: View? = null private var fragmentView: View? = null
private var activity: Activity? = null private var activity: Activity? = null
@ -34,7 +34,6 @@ class ActiveDownloadsFragment() : Fragment(), ActiveDownloadAdapter.OnItemClickL
private lateinit var activeDownloads : ActiveDownloadAdapter private lateinit var activeDownloads : ActiveDownloadAdapter
lateinit var downloadItem: DownloadItem lateinit var downloadItem: DownloadItem
private lateinit var notificationUtil: NotificationUtil private lateinit var notificationUtil: NotificationUtil
private lateinit var fileUtil: FileUtil
private lateinit var list: List<DownloadItem> private lateinit var list: List<DownloadItem>
@ -47,7 +46,6 @@ class ActiveDownloadsFragment() : Fragment(), ActiveDownloadAdapter.OnItemClickL
fragmentView = inflater.inflate(R.layout.fragment_generic_download_queue, container, false) fragmentView = inflater.inflate(R.layout.fragment_generic_download_queue, container, false)
activity = getActivity() activity = getActivity()
notificationUtil = NotificationUtil(requireContext()) notificationUtil = NotificationUtil(requireContext())
fileUtil = FileUtil()
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
list = listOf() list = listOf()
return fragmentView return fragmentView
@ -102,7 +100,7 @@ class ActiveDownloadsFragment() : Fragment(), ActiveDownloadAdapter.OnItemClickL
} }
override fun onOutputClick(item: DownloadItem) { override fun onOutputClick(item: DownloadItem) {
val logFile = fileUtil.getLogFile(requireContext(), item) val logFile = FileUtil.getLogFile(requireContext(), item)
if (logFile.exists()) { if (logFile.exists()) {
val bundle = Bundle() val bundle = Bundle()
bundle.putString("logpath", logFile.absolutePath) bundle.putString("logpath", logFile.absolutePath)

View file

@ -52,7 +52,6 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
private var selectedObjects: ArrayList<DownloadItem>? = null private var selectedObjects: ArrayList<DownloadItem>? = null
private var actionMode : ActionMode? = null private var actionMode : ActionMode? = null
private lateinit var items : MutableList<DownloadItem> private lateinit var items : MutableList<DownloadItem>
private lateinit var fileUtil: FileUtil
private lateinit var uiUtil : UiUtil private lateinit var uiUtil : UiUtil
override fun onCreateView( override fun onCreateView(
@ -66,8 +65,7 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
selectedObjects = arrayListOf() selectedObjects = arrayListOf()
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
items = mutableListOf() items = mutableListOf()
fileUtil = FileUtil() uiUtil = UiUtil()
uiUtil = UiUtil(fileUtil)
return fragmentView return fragmentView
} }
@ -163,7 +161,7 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
codec.text = codecText codec.text = codecText
} }
val fileSizeReadable = fileUtil.convertFileSize(item.format.filesize) val fileSizeReadable = FileUtil.convertFileSize(item.format.filesize)
if (fileSizeReadable == "?") fileSize!!.visibility = View.GONE if (fileSizeReadable == "?") fileSize!!.visibility = View.GONE
else fileSize!!.text = fileSizeReadable else fileSize!!.text = fileSizeReadable

View file

@ -42,6 +42,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import java.io.File
class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickListener, OnItemClickListener { class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickListener, OnItemClickListener {
@ -54,7 +55,6 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
private lateinit var items : MutableList<DownloadItem> private lateinit var items : MutableList<DownloadItem>
private var selectedObjects: ArrayList<DownloadItem>? = null private var selectedObjects: ArrayList<DownloadItem>? = null
private var actionMode : ActionMode? = null private var actionMode : ActionMode? = null
private lateinit var fileUtil: FileUtil
private lateinit var uiUtil : UiUtil private lateinit var uiUtil : UiUtil
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -67,8 +67,7 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
items = mutableListOf() items = mutableListOf()
selectedObjects = arrayListOf() selectedObjects = arrayListOf()
fileUtil = FileUtil() uiUtil = UiUtil()
uiUtil = UiUtil(fileUtil)
return fragmentView return fragmentView
} }
@ -98,7 +97,7 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
override fun onActionButtonClick(itemID: Long) { override fun onActionButtonClick(itemID: Long) {
val item = items.find { it.id == itemID } ?: return val item = items.find { it.id == itemID } ?: return
val logFile = fileUtil.getLogFile(requireContext(), item) val logFile = FileUtil.getLogFile(requireContext(), item)
if (logFile.exists()) { if (logFile.exists()) {
val bundle = Bundle() val bundle = Bundle()
bundle.putString("logpath", logFile.absolutePath) bundle.putString("logpath", logFile.absolutePath)
@ -165,7 +164,7 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
codec.text = codecText codec.text = codecText
} }
val fileSizeReadable = fileUtil.convertFileSize(item.format.filesize) val fileSizeReadable = FileUtil.convertFileSize(item.format.filesize)
if (fileSizeReadable == "?") fileSize!!.visibility = View.GONE if (fileSizeReadable == "?") fileSize!!.visibility = View.GONE
else fileSize!!.text = fileSizeReadable else fileSize!!.text = fileSizeReadable

View file

@ -87,7 +87,6 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
private var historyList: List<HistoryItem?>? = null private var historyList: List<HistoryItem?>? = null
private var allhistoryList: List<HistoryItem?>? = null private var allhistoryList: List<HistoryItem?>? = null
private var selectedObjects: ArrayList<HistoryItem>? = null private var selectedObjects: ArrayList<HistoryItem>? = null
private var fileUtil: FileUtil? = null
private var uiUtil: UiUtil? = null private var uiUtil: UiUtil? = null
private var _binding : FragmentHistoryBinding? = null private var _binding : FragmentHistoryBinding? = null
private var actionMode : ActionMode? = null private var actionMode : ActionMode? = null
@ -113,8 +112,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
noResults = view.findViewById(R.id.no_results) noResults = view.findViewById(R.id.no_results)
selectionChips = view.findViewById(R.id.history_selection_chips) selectionChips = view.findViewById(R.id.history_selection_chips)
websiteGroup = view.findViewById(R.id.website_chip_group) websiteGroup = view.findViewById(R.id.website_chip_group)
fileUtil = FileUtil() uiUtil = UiUtil()
uiUtil = UiUtil(FileUtil())
uiHandler = Handler(Looper.getMainLooper()) uiHandler = Handler(Looper.getMainLooper())
selectedObjects = ArrayList() selectedObjects = ArrayList()
@ -462,7 +460,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
codec.text = codecText codec.text = codecText
} }
val fileSizeReadable = fileUtil!!.convertFileSize(item.format.filesize) val fileSizeReadable = FileUtil.convertFileSize(item.format.filesize)
if (fileSizeReadable == "?") fileSize!!.visibility = GONE if (fileSizeReadable == "?") fileSize!!.visibility = GONE
else fileSize!!.text = fileSizeReadable else fileSize!!.text = fileSizeReadable

View file

@ -63,7 +63,6 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
private var selectedObjects: ArrayList<DownloadItem>? = null private var selectedObjects: ArrayList<DownloadItem>? = null
private var actionMode : ActionMode? = null private var actionMode : ActionMode? = null
private lateinit var items : MutableList<DownloadItem> private lateinit var items : MutableList<DownloadItem>
private lateinit var fileUtil: FileUtil
private lateinit var uiUtil: UiUtil private lateinit var uiUtil: UiUtil
override fun onCreateView( override fun onCreateView(
@ -78,8 +77,7 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
items = mutableListOf() items = mutableListOf()
selectedObjects = arrayListOf() selectedObjects = arrayListOf()
fileUtil = FileUtil() uiUtil = UiUtil()
uiUtil = UiUtil(fileUtil)
return fragmentView return fragmentView
} }
@ -185,7 +183,7 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
codec.text = codecText codec.text = codecText
} }
val fileSizeReadable = fileUtil.convertFileSize(item.format.filesize) val fileSizeReadable = FileUtil.convertFileSize(item.format.filesize)
if (fileSizeReadable == "?") fileSize!!.visibility = View.GONE if (fileSizeReadable == "?") fileSize!!.visibility = View.GONE
else fileSize!!.text = fileSizeReadable else fileSize!!.text = fileSizeReadable

View file

@ -75,7 +75,7 @@ class CommandTemplatesFragment : Fragment(), TemplatesAdapter.OnItemClickListene
itemTouchHelper.attachToRecyclerView(recyclerView) itemTouchHelper.attachToRecyclerView(recyclerView)
} }
uiUtil = UiUtil(FileUtil()) uiUtil = UiUtil()
commandTemplateViewModel = ViewModelProvider(this)[CommandTemplateViewModel::class.java] commandTemplateViewModel = ViewModelProvider(this)[CommandTemplateViewModel::class.java]
commandTemplateViewModel.items.observe(viewLifecycleOwner) { commandTemplateViewModel.items.observe(viewLifecycleOwner) {

View file

@ -79,7 +79,7 @@ class CookiesFragment : Fragment(), CookieAdapter.OnItemClickListener {
val itemTouchHelper = ItemTouchHelper(simpleCallback) val itemTouchHelper = ItemTouchHelper(simpleCallback)
itemTouchHelper.attachToRecyclerView(recyclerView) itemTouchHelper.attachToRecyclerView(recyclerView)
} }
uiUtil = UiUtil(FileUtil()) uiUtil = UiUtil()
cookiesViewModel = ViewModelProvider(this)[CookieViewModel::class.java] cookiesViewModel = ViewModelProvider(this)[CookieViewModel::class.java]
cookiesViewModel.items.observe(viewLifecycleOwner) { cookiesViewModel.items.observe(viewLifecycleOwner) {

View file

@ -64,7 +64,6 @@ class TerminalActivity : BaseActivity() {
private lateinit var observer: FileObserver private lateinit var observer: FileObserver
private lateinit var imm : InputMethodManager private lateinit var imm : InputMethodManager
private lateinit var uiUtil: UiUtil private lateinit var uiUtil: UiUtil
private lateinit var fileUtil: FileUtil
var context: Context? = null var context: Context? = null
public override fun onCreate(savedInstanceState: Bundle?) { public override fun onCreate(savedInstanceState: Bundle?) {
@ -75,8 +74,7 @@ class TerminalActivity : BaseActivity() {
downloadFile = File(cacheDir.absolutePath + "/$downloadID.txt") downloadFile = File(cacheDir.absolutePath + "/$downloadID.txt")
if (! downloadFile.exists()) downloadFile.createNewFile() if (! downloadFile.exists()) downloadFile.createNewFile()
imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
fileUtil = FileUtil() uiUtil = UiUtil()
uiUtil = UiUtil(fileUtil)
context = baseContext context = baseContext
scrollView = findViewById(R.id.custom_command_scrollview) scrollView = findViewById(R.id.custom_command_scrollview)
@ -311,7 +309,7 @@ class TerminalActivity : BaseActivity() {
Intent.FLAG_GRANT_WRITE_URI_PERMISSION Intent.FLAG_GRANT_WRITE_URI_PERMISSION
) )
} }
input!!.text.insert(input!!.selectionStart, fileUtil.formatPath(result.data?.data.toString())) input!!.text.insert(input!!.selectionStart, FileUtil.formatPath(result.data?.data.toString()))
} }
} }

View file

@ -25,7 +25,6 @@ class FolderSettingsFragment : BaseSettingsFragment() {
private var accessAllFiles : Preference? = null private var accessAllFiles : Preference? = null
private var clearCache: Preference? = null private var clearCache: Preference? = null
private var fileUtil: FileUtil? = null
private var activeDownloadCount = 0 private var activeDownloadCount = 0
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
@ -33,7 +32,6 @@ class FolderSettingsFragment : BaseSettingsFragment() {
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
val editor = preferences.edit() val editor = preferences.edit()
fileUtil = FileUtil()
musicPath = findPreference("music_path") musicPath = findPreference("music_path")
videoPath = findPreference("video_path") videoPath = findPreference("video_path")
@ -42,13 +40,13 @@ class FolderSettingsFragment : BaseSettingsFragment() {
clearCache = findPreference("clear_cache") clearCache = findPreference("clear_cache")
if (preferences.getString("music_path", "")!!.isEmpty()) { if (preferences.getString("music_path", "")!!.isEmpty()) {
editor.putString("music_path", getString(R.string.music_path)) editor.putString("music_path", FileUtil.getDefautAudioPath())
} }
if (preferences.getString("video_path", "")!!.isEmpty()) { if (preferences.getString("video_path", "")!!.isEmpty()) {
editor.putString("video_path", getString(R.string.video_path)) editor.putString("video_path", FileUtil.getDefautVideoPath())
} }
if (preferences.getString("command_path", "")!!.isEmpty()) { if (preferences.getString("command_path", "")!!.isEmpty()) {
editor.putString("command_path", getString(R.string.command_path)) editor.putString("command_path", FileUtil.getDefaultCommandPath())
} }
if((VERSION.SDK_INT >= 30 && Environment.isExternalStorageManager()) || if((VERSION.SDK_INT >= 30 && Environment.isExternalStorageManager()) ||
@ -58,7 +56,7 @@ class FolderSettingsFragment : BaseSettingsFragment() {
editor.apply() editor.apply()
musicPath!!.summary = fileUtil?.formatPath(preferences.getString("music_path", "")!!) musicPath!!.summary = FileUtil.formatPath(preferences.getString("music_path", "")!!)
musicPath!!.onPreferenceClickListener = musicPath!!.onPreferenceClickListener =
Preference.OnPreferenceClickListener { Preference.OnPreferenceClickListener {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
@ -68,7 +66,7 @@ class FolderSettingsFragment : BaseSettingsFragment() {
musicPathResultLauncher.launch(intent) musicPathResultLauncher.launch(intent)
true true
} }
videoPath!!.summary = fileUtil?.formatPath(preferences.getString("video_path", "")!!) videoPath!!.summary = FileUtil.formatPath(preferences.getString("video_path", "")!!)
videoPath!!.onPreferenceClickListener = videoPath!!.onPreferenceClickListener =
Preference.OnPreferenceClickListener { Preference.OnPreferenceClickListener {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
@ -78,7 +76,7 @@ class FolderSettingsFragment : BaseSettingsFragment() {
videoPathResultLauncher.launch(intent) videoPathResultLauncher.launch(intent)
true true
} }
commandPath!!.summary = fileUtil?.formatPath(preferences.getString("command_path", "")!!) commandPath!!.summary = FileUtil.formatPath(preferences.getString("command_path", "")!!)
commandPath!!.onPreferenceClickListener = commandPath!!.onPreferenceClickListener =
Preference.OnPreferenceClickListener { Preference.OnPreferenceClickListener {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
@ -98,14 +96,14 @@ class FolderSettingsFragment : BaseSettingsFragment() {
} }
var cacheSize = File(requireContext().cacheDir.absolutePath + "/downloads").walkBottomUp().fold(0L) { acc, file -> acc + file.length() } var cacheSize = File(requireContext().cacheDir.absolutePath + "/downloads").walkBottomUp().fold(0L) { acc, file -> acc + file.length() }
clearCache!!.summary = "(${fileUtil!!.convertFileSize(cacheSize)}) ${resources.getString(R.string.clear_temporary_files_summary)}" clearCache!!.summary = "(${FileUtil.convertFileSize(cacheSize)}) ${resources.getString(R.string.clear_temporary_files_summary)}"
clearCache!!.onPreferenceClickListener = clearCache!!.onPreferenceClickListener =
Preference.OnPreferenceClickListener { Preference.OnPreferenceClickListener {
if (activeDownloadCount == 0){ if (activeDownloadCount == 0){
File(requireContext().cacheDir.absolutePath + "/downloads").deleteRecursively() File(requireContext().cacheDir.absolutePath + "/downloads").deleteRecursively()
Snackbar.make(requireView(), getString(R.string.cache_cleared), Snackbar.LENGTH_SHORT).show() Snackbar.make(requireView(), getString(R.string.cache_cleared), Snackbar.LENGTH_SHORT).show()
cacheSize = File(requireContext().cacheDir.absolutePath + "/downloads").walkBottomUp().fold(0L) { acc, file -> acc + file.length() } cacheSize = File(requireContext().cacheDir.absolutePath + "/downloads").walkBottomUp().fold(0L) { acc, file -> acc + file.length() }
clearCache!!.summary = "(${fileUtil!!.convertFileSize(cacheSize)}) ${resources.getString(R.string.clear_temporary_files_summary)}" clearCache!!.summary = "(${FileUtil.convertFileSize(cacheSize)}) ${resources.getString(R.string.clear_temporary_files_summary)}"
}else{ }else{
Snackbar.make(requireView(), getString(R.string.downloads_running_try_later), Snackbar.LENGTH_SHORT).show() Snackbar.make(requireView(), getString(R.string.downloads_running_try_later), Snackbar.LENGTH_SHORT).show()
} }
@ -159,7 +157,7 @@ class FolderSettingsFragment : BaseSettingsFragment() {
private fun changePath(p: Preference?, data: Intent?, requestCode: Int) { private fun changePath(p: Preference?, data: Intent?, requestCode: Int) {
val path = data!!.data.toString() val path = data!!.data.toString()
p!!.summary = fileUtil?.formatPath(data.data.toString()) p!!.summary = FileUtil.formatPath(data.data.toString())
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
val editor = sharedPreferences.edit() val editor = sharedPreferences.edit()
when (requestCode) { when (requestCode) {

View file

@ -30,13 +30,11 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
private var locale: ListPreference? = null private var locale: ListPreference? = null
private var updateUtil: UpdateUtil? = null private var updateUtil: UpdateUtil? = null
private var fileUtil: FileUtil? = null
private var activeDownloadCount = 0 private var activeDownloadCount = 0
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.general_preferences, rootKey) setPreferencesFromResource(R.xml.general_preferences, rootKey)
updateUtil = UpdateUtil(requireContext()) updateUtil = UpdateUtil(requireContext())
fileUtil = FileUtil()
WorkManager.getInstance(requireContext()).getWorkInfosByTagLiveData("download").observe(this){ WorkManager.getInstance(requireContext()).getWorkInfosByTagLiveData("download").observe(this){
activeDownloadCount = 0 activeDownloadCount = 0

View file

@ -54,7 +54,6 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
private var restore : Preference? = null private var restore : Preference? = null
private var updateUtil: UpdateUtil? = null private var updateUtil: UpdateUtil? = null
private var fileUtil: FileUtil? = null
private var activeDownloadCount = 0 private var activeDownloadCount = 0
private lateinit var resultViewModel: ResultViewModel private lateinit var resultViewModel: ResultViewModel
@ -105,7 +104,6 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
//about section ------------------------- //about section -------------------------
updateUtil = UpdateUtil(requireContext()) updateUtil = UpdateUtil(requireContext())
fileUtil = FileUtil()
WorkManager.getInstance(requireContext()).getWorkInfosByTagLiveData("download").observe(this){ WorkManager.getInstance(requireContext()).getWorkInfosByTagLiveData("download").observe(this){
activeDownloadCount = 0 activeDownloadCount = 0

View file

@ -4,6 +4,7 @@ import android.content.Context
import android.media.MediaScannerConnection import android.media.MediaScannerConnection
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Environment
import android.provider.DocumentsContract import android.provider.DocumentsContract
import android.util.Log import android.util.Log
import android.webkit.MimeTypeMap import android.webkit.MimeTypeMap
@ -21,7 +22,7 @@ import kotlin.math.log10
import kotlin.math.pow import kotlin.math.pow
class FileUtil() { object FileUtil {
fun deleteFile(path: String){ fun deleteFile(path: String){
val file = File(path) val file = File(path)
if (file.exists()) { if (file.exists()) {
@ -144,13 +145,26 @@ class FileUtil() {
fun getLogFile(context: Context, item: DownloadItem) : File { fun getLogFile(context: Context, item: DownloadItem) : File {
val titleRegex = Regex("[^A-Za-z\\d ]") val titleRegex = Regex("[^A-Za-z\\d ]")
return File(context.filesDir.absolutePath + """/logs/${item.id} - ${titleRegex.replace(item.title, "").take(150)}##${item.type}##${item.format.format_id}.log""") val title = item.title.ifEmpty { item.url }
return File(context.filesDir.absolutePath + """/logs/${item.id} - ${titleRegex.replace(title, "").take(150)}##${item.type}##${item.format.format_id}.log""")
} }
fun getLogFileForTerminal(context: Context, command: String) : File { fun getLogFileForTerminal(context: Context, command: String) : File {
val titleRegex = Regex("[^A-Za-z\\d ]") val titleRegex = Regex("[^A-Za-z\\d ]")
return File(context.filesDir.absolutePath + """/logs/Terminal - ${titleRegex.replace(command.take(30), "")}##terminal.log""") return File(context.filesDir.absolutePath + """/logs/Terminal - ${titleRegex.replace(command.take(30), "")}##terminal.log""")
} }
fun getDefautAudioPath() : String{
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).absolutePath + File.separator + "YTDLnis/Audio"
}
fun getDefautVideoPath() : String{
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).absolutePath + File.separator + "YTDLnis/Video"
}
fun getDefaultCommandPath() : String {
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).absolutePath + File.separator + "YTDLnis/Command"
}
fun convertFileSize(s: Long): String{ fun convertFileSize(s: Long): String{
if (s <= 0) return "?" if (s <= 0) return "?"
val units = arrayOf("B", "kB", "MB", "GB", "TB") val units = arrayOf("B", "kB", "MB", "GB", "TB")

View file

@ -9,9 +9,11 @@ import android.widget.Toast
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.ChapterItem
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.google.gson.Gson import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
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 org.json.JSONArray import org.json.JSONArray
@ -20,6 +22,7 @@ import org.json.JSONObject
import java.io.BufferedReader import java.io.BufferedReader
import java.io.File import java.io.File
import java.io.InputStreamReader import java.io.InputStreamReader
import java.lang.reflect.Type
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.net.URL import java.net.URL
import java.util.* import java.util.*
@ -272,7 +275,9 @@ class InfoUtil(private val context: Context) {
thumb, thumb,
"youtube", "youtube",
"", "",
ArrayList() ArrayList(),
"",
ArrayList()
) )
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, e.toString()) Log.e(TAG, e.toString())
@ -326,7 +331,9 @@ class InfoUtil(private val context: Context) {
thumb, thumb,
"youtube", "youtube",
"", "",
formats formats,
"",
ArrayList()
) )
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, e.toString()) Log.e(TAG, e.toString())
@ -337,13 +344,8 @@ class InfoUtil(private val context: Context) {
private fun createVideoFromPipedJSON(obj: JSONObject, id: String): ResultItem? { private fun createVideoFromPipedJSON(obj: JSONObject, id: String): ResultItem? {
var video: ResultItem? = null var video: ResultItem? = null
try { try {
val title = obj.getString("title").toString() val title = Html.fromHtml(obj.getString("title").toString()).toString()
title.replace("&#39;s", "'") val author = Html.fromHtml(obj.getString("uploader").toString()).toString()
title.replace("&amp", "&")
val author = obj.getString("uploader").toString()
author.replace("&#39;s", "'")
author.replace("&amp", "&")
if (author.isBlank()) throw Exception() if (author.isBlank()) throw Exception()
val duration = formatIntegerDuration(obj.getInt("duration"), Locale.getDefault()) val duration = formatIntegerDuration(obj.getInt("duration"), Locale.getDefault())
@ -360,7 +362,6 @@ class InfoUtil(private val context: Context) {
formatObj.acodec = format.getString("codec") formatObj.acodec = format.getString("codec")
val streamURL = format.getString("url") val streamURL = format.getString("url")
formatObj.format_id = streamURL.substringAfter("itag=").substringBefore("&") formatObj.format_id = streamURL.substringAfter("itag=").substringBefore("&")
formatObj.filesize = (format.getInt("bitrate") / 8 * obj.getInt("duration")).toLong()
formatObj.asr = format.getString("quality") formatObj.asr = format.getString("quality")
if (! format.getString("audioTrackName").equals("null", ignoreCase = true)){ if (! format.getString("audioTrackName").equals("null", ignoreCase = true)){
formatObj.format_note = format.getString("audioTrackName") + " Audio, " + formatObj.format_note formatObj.format_note = format.getString("audioTrackName") + " Audio, " + formatObj.format_note
@ -385,7 +386,6 @@ class InfoUtil(private val context: Context) {
formatObj.vcodec = format.getString("codec") formatObj.vcodec = format.getString("codec")
val streamURL = format.getString("url") val streamURL = format.getString("url")
formatObj.format_id = streamURL.substringAfter("itag=").substringBefore("&") formatObj.format_id = streamURL.substringAfter("itag=").substringBefore("&")
formatObj.filesize = (format.getInt("bitrate") / 8 * obj.getInt("duration")).toLong()
}catch (e: Exception) { }catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
} }
@ -394,6 +394,17 @@ class InfoUtil(private val context: Context) {
} }
formats.sortBy { it.filesize } formats.sortBy { it.filesize }
val chapters = ArrayList<ChapterItem>()
if (obj.has("chapters") && obj.getJSONArray("chapters").length() > 0){
val chaptersJArray = obj.getJSONArray("chapters")
for (c in 0 until chaptersJArray.length()){
val chapter = chaptersJArray.getJSONObject(c)
val end = if (c == chaptersJArray.length() - 1) obj.getInt("duration") else chaptersJArray.getJSONObject(c+1).getInt("start")
val item = ChapterItem(chapter.getInt("start").toLong(), end.toLong(), chapter.getString("title"))
chapters.add(item)
}
}
video = ResultItem(0, video = ResultItem(0,
url, url,
title, title,
@ -402,7 +413,9 @@ class InfoUtil(private val context: Context) {
thumb, thumb,
"youtube", "youtube",
"", "",
formats formats,
if (obj.has("hls")) obj.getString("hls") else "",
chapters
) )
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, e.toString()) Log.e(TAG, e.toString())
@ -567,7 +580,7 @@ class InfoUtil(private val context: Context) {
return query!! return query!!
} }
fun getFormats(url: String) : ResultItem { fun getFormats(url: String) : List<Format> {
init() init()
val p = Pattern.compile("^(https?)://(www.)?youtu(.be)?") val p = Pattern.compile("^(https?)://(www.)?youtu(.be)?")
val m = p.matcher(url) val m = p.matcher(url)
@ -576,14 +589,70 @@ class InfoUtil(private val context: Context) {
val id = getIDFromYoutubeURL(url) val id = getIDFromYoutubeURL(url)
val res = genericRequest(pipedURL + "streams/" + id) val res = genericRequest(pipedURL + "streams/" + id)
if (res.length() == 0) getFromYTDL(url)[0]!! if (res.length() == 0) getFromYTDL(url)[0]!!
else createVideoFromPipedJSON( val item = createVideoFromPipedJSON(res, id)
res, id item!!.formats
)!!
}else{ }else{
getFromYTDL(url)[0]!! getFormatsFromYTDL(url)
} }
} }
private fun getFormatsFromYTDL(url: String) : List<Format> {
try {
val request = YoutubeDLRequest(url)
request.addOption("--print", "%(formats)s")
request.addOption("--skip-download")
request.addOption("-R", "1")
request.addOption("--socket-timeout", "5")
val cookiesFile = File(context.cacheDir, "cookies.txt")
if (cookiesFile.exists()){
request.addOption("--cookies", cookiesFile.absolutePath)
}
val proxy = sharedPreferences.getString("proxy", "")
if (proxy!!.isNotBlank()){
request.addOption("--proxy", proxy)
}
val res = YoutubeDL.getInstance().execute(request)
val results: Array<String?> = try {
val lineSeparator = System.getProperty("line.separator")
res.out.split(lineSeparator!!).toTypedArray()
} catch (e: Exception) {
arrayOf(res.out)
}
val json = results[0]
val jsonArray = JSONArray(json)
val formats : ArrayList<Format> = ArrayList()
for (f in 0 until jsonArray.length()){
val format = jsonArray.getJSONObject(f)
if (format.has("filesize") && format.get("filesize") == "None"){
format.remove("filesize")
format.put("filesize", 0)
}
val formatProper = Gson().fromJson(format.toString(), Format::class.java)
if (format.has("format_note")){
if (!formatProper!!.format_note.contains("audio only", true)) {
formatProper.format_note = format.getString("format_note")
}else{
formatProper.format_note = "${format.getString("format_note")} audio"
}
}
if (formatProper!!.format_note == "storyboard") continue
formatProper.container = format.getString("ext")
formats.add(formatProper)
}
return formats
} catch (e: Exception) {
Looper.prepare().run {
Toast.makeText(context, e.message, Toast.LENGTH_LONG).show()
}
e.printStackTrace()
}
return emptyList()
}
fun getFormatsMultiple(urls: List<String>, progress: (progress: List<Format>) -> Unit){ fun getFormatsMultiple(urls: List<String>, progress: (progress: List<Format>) -> Unit){
init() init()
val urlsFile = File(context.cacheDir, "urls.txt") val urlsFile = File(context.cacheDir, "urls.txt")
@ -623,16 +692,19 @@ class InfoUtil(private val context: Context) {
try{ try{
if (format.getString("filesize") == "None") continue if (format.getString("filesize") == "None") continue
}catch (e: Exception) { continue } }catch (e: Exception) { continue }
val formatProper = Gson().fromJson(format.toString(), Format::class.java) if (format.has("filesize") && format.get("filesize") == "None"){
if (formatProper.filesize > 0){ format.remove("filesize")
if ( !formatProper!!.format_note!!.contains("audio only", true)) { format.put("filesize", 0)
formatProper.format_note = format.getString("format_note")
}else{
formatProper.format_note = "${format.getString("format_note")} audio"
}
formatProper.container = format.getString("ext")
formats.add(formatProper)
} }
val formatProper = Gson().fromJson(format.toString(), Format::class.java)
if ( !formatProper!!.format_note.contains("audio only", true)) {
formatProper.format_note = format.getString("format_note")
}else{
formatProper.format_note = "${format.getString("format_note")} audio"
}
if (formatProper.format_note == "storyboard") continue
formatProper.container = format.getString("ext")
formats.add(formatProper)
} }
progress(formats) progress(formats)
}catch (e: Exception){ }catch (e: Exception){
@ -725,20 +797,28 @@ class InfoUtil(private val context: Context) {
if (formatsInJSON != null) { if (formatsInJSON != null) {
for (f in 0 until formatsInJSON.length()){ for (f in 0 until formatsInJSON.length()){
val format = formatsInJSON.getJSONObject(f) val format = formatsInJSON.getJSONObject(f)
val formatProper = Gson().fromJson(format.toString(), Format::class.java) if (format.has("filesize") && format.get("filesize") == "None"){
if (formatProper.filesize > 0){ format.remove("filesize")
if (format.has("format_note") && formatProper.format_note != null){ format.put("filesize", 0)
if (!formatProper!!.format_note.contains("audio only", true)) {
formatProper.format_note = format.getString("format_note")
}else{
formatProper.format_note = "${format.getString("format_note")} audio"
}
}
formatProper.container = format.getString("ext")
formats.add(formatProper)
} }
val formatProper = Gson().fromJson(format.toString(), Format::class.java)
if (format.has("format_note") && formatProper.format_note != null){
if (!formatProper!!.format_note.contains("audio only", true)) {
formatProper.format_note = format.getString("format_note")
}else{
formatProper.format_note = "${format.getString("format_note")} audio"
}
}
if (formatProper.format_note == "storyboard") continue
formatProper.container = format.getString("ext")
formats.add(formatProper)
} }
} }
val chaptersInJSON = if (jsonObject.has("formats") && jsonObject.get("formats") is JSONArray) jsonObject.getJSONArray("formats") else null
val listType: Type = object : TypeToken<List<ChapterItem>>() {}.type
val chapters : ArrayList<ChapterItem> = Gson().fromJson(chaptersInJSON?.toString(), listType)
Log.e("aa", formats.toString()) Log.e("aa", formats.toString())
items.add(ResultItem(0, items.add(ResultItem(0,
url, url,
@ -748,7 +828,9 @@ class InfoUtil(private val context: Context) {
thumb!!, thumb!!,
website, website,
playlistTitle!!, playlistTitle!!,
formats formats,
jsonObject.getString("urls") ?: "",
chapters
) )
) )
} }
@ -821,6 +903,8 @@ class InfoUtil(private val context: Context) {
jsonObject.getString("extractor"), jsonObject.getString("extractor"),
if (isPlaylist) jsonObject.getString("title") else "", if (isPlaylist) jsonObject.getString("title") else "",
arrayListOf(), arrayListOf(),
"",
arrayListOf(),
System.currentTimeMillis() System.currentTimeMillis()
) )
} catch (e: Exception) { } catch (e: Exception) {
@ -945,7 +1029,7 @@ class InfoUtil(private val context: Context) {
companion object { companion object {
private const val TAG = "API MANAGER" private const val TAG = "API MANAGER"
private const val invidousURL = "https://invidious.nerdvpn.de/api/v1/" private const val invidousURL = "https://invidious.baczek.me/api/v1/"
private const val pipedURL = "https://pipedapi.kavin.rocks/" private const val pipedURL = "https://pipedapi.kavin.rocks/"
private var countryCODE: String = "" private var countryCODE: String = ""
} }

View file

@ -52,7 +52,7 @@ import kotlinx.coroutines.withContext
import java.io.File import java.io.File
import java.util.Calendar import java.util.Calendar
class UiUtil(private val fileUtil: FileUtil) { class UiUtil() {
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
fun populateFormatCard(formatCard : MaterialCardView, chosenFormat: Format, audioFormats: List<Format>?){ fun populateFormatCard(formatCard : MaterialCardView, chosenFormat: Format, audioFormats: List<Format>?){
formatCard.findViewById<TextView>(R.id.container).text = chosenFormat.container.uppercase() formatCard.findViewById<TextView>(R.id.container).text = chosenFormat.container.uppercase()
@ -79,7 +79,7 @@ class UiUtil(private val fileUtil: FileUtil) {
} }
var filesize = chosenFormat.filesize var filesize = chosenFormat.filesize
if (!audioFormats.isNullOrEmpty()) filesize += audioFormats.sumOf { it.filesize } if (!audioFormats.isNullOrEmpty()) filesize += audioFormats.sumOf { it.filesize }
formatCard.findViewById<TextView>(R.id.file_size).text = fileUtil.convertFileSize(filesize) formatCard.findViewById<TextView>(R.id.file_size).text = FileUtil.convertFileSize(filesize)
} }
@ -139,7 +139,7 @@ class UiUtil(private val fileUtil: FileUtil) {
} }
} }
commandTemplateViewModel.shortcuts.observe(lifeCycle){ it -> commandTemplateViewModel.shortcuts.observe(lifeCycle){
shortcutsChipGroup.removeAllViews() shortcutsChipGroup.removeAllViews()
it.forEach {shortcut -> it.forEach {shortcut ->
val chip = context.layoutInflater.inflate(R.layout.suggestion_chip, shortcutsChipGroup, false) as Chip val chip = context.layoutInflater.inflate(R.layout.suggestion_chip, shortcutsChipGroup, false) as Chip
@ -200,7 +200,7 @@ class UiUtil(private val fileUtil: FileUtil) {
title.isEndIconVisible = false title.isEndIconVisible = false
} }
commandTemplateViewModel.shortcuts.observe(lifeCycle){ it -> commandTemplateViewModel.shortcuts.observe(lifeCycle){
shortcutsChipGroup.removeAllViews() shortcutsChipGroup.removeAllViews()
it.forEach {shortcut -> it.forEach {shortcut ->
val chip = context.layoutInflater.inflate(R.layout.input_chip, shortcutsChipGroup, false) as Chip val chip = context.layoutInflater.inflate(R.layout.input_chip, shortcutsChipGroup, false) as Chip
@ -324,6 +324,7 @@ class UiUtil(private val fileUtil: FileUtil) {
bottomSheet.setContentView(R.layout.format_details_sheet) bottomSheet.setContentView(R.layout.format_details_sheet)
val formatIdParent = bottomSheet.findViewById<LinearLayout>(R.id.format_id_parent) val formatIdParent = bottomSheet.findViewById<LinearLayout>(R.id.format_id_parent)
val formatURLParent = bottomSheet.findViewById<LinearLayout>(R.id.format_url_parent)
val containerParent = bottomSheet.findViewById<LinearLayout>(R.id.container_parent) val containerParent = bottomSheet.findViewById<LinearLayout>(R.id.container_parent)
val codecParent = bottomSheet.findViewById<LinearLayout>(R.id.codec_parent) val codecParent = bottomSheet.findViewById<LinearLayout>(R.id.codec_parent)
val filesizeParent = bottomSheet.findViewById<LinearLayout>(R.id.filesize_parent) val filesizeParent = bottomSheet.findViewById<LinearLayout>(R.id.filesize_parent)
@ -339,6 +340,14 @@ class UiUtil(private val fileUtil: FileUtil) {
} }
} }
if (format.url.isNullOrBlank()) formatURLParent?.visibility = View.GONE
else {
formatURLParent?.findViewById<TextView>(R.id.format_url_value)?.text = format.url
formatURLParent?.setOnClickListener {
copyToClipboard(format.url!!, activity)
}
}
if (format.container.isBlank()) containerParent?.visibility = View.GONE if (format.container.isBlank()) containerParent?.visibility = View.GONE
else { else {
@ -367,9 +376,9 @@ class UiUtil(private val fileUtil: FileUtil) {
if (format.filesize != 0L) filesizeParent?.visibility = View.GONE if (format.filesize != 0L) filesizeParent?.visibility = View.GONE
else { else {
filesizeParent?.findViewById<TextView>(R.id.filesize_value)?.text = fileUtil.convertFileSize(format.filesize) filesizeParent?.findViewById<TextView>(R.id.filesize_value)?.text = FileUtil.convertFileSize(format.filesize)
filesizeParent?.setOnClickListener { filesizeParent?.setOnClickListener {
copyToClipboard(fileUtil.convertFileSize(format.filesize), activity) copyToClipboard(FileUtil.convertFileSize(format.filesize), activity)
} }
} }

View file

@ -43,7 +43,6 @@ class DownloadWorker(
if (itemId == 0L) return Result.failure() if (itemId == 0L) return Result.failure()
val notificationUtil = NotificationUtil(context) val notificationUtil = NotificationUtil(context)
val fileUtil = FileUtil()
val infoUtil = InfoUtil(context) val infoUtil = InfoUtil(context)
val dbManager = DBManager.getInstance(context) val dbManager = DBManager.getInstance(context)
val dao = dbManager.downloadDao val dao = dbManager.downloadDao
@ -68,13 +67,9 @@ class DownloadWorker(
} }
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
if (sharedPreferences.getBoolean("log_downloads", false)){ CoroutineScope(Dispatchers.IO).launch {
updateDownloadItem(downloadItem, infoUtil, dao, resultDao, true, notificationUtil) //update item if its incomplete
}else{ updateDownloadItem(downloadItem, infoUtil, dao, resultDao, false, notificationUtil)
CoroutineScope(Dispatchers.IO).launch {
//update item if its incomplete
updateDownloadItem(downloadItem, infoUtil, dao, resultDao, false, notificationUtil)
}
} }
val intent = Intent(context, MainActivity::class.java) val intent = Intent(context, MainActivity::class.java)
@ -174,13 +169,13 @@ class DownloadWorker(
if (audioQualityId.isBlank() || audioQualityId == "0" || audioQualityId == context.getString(R.string.best_quality) || audioQualityId == "best") audioQualityId = "" if (audioQualityId.isBlank() || audioQualityId == "0" || audioQualityId == context.getString(R.string.best_quality) || audioQualityId == "best") audioQualityId = ""
else if (audioQualityId == context.getString(R.string.worst_quality) || audioQualityId == "worst") audioQualityId = "worstaudio" else if (audioQualityId == context.getString(R.string.worst_quality) || audioQualityId == "worst") audioQualityId = "worstaudio"
val ext = downloadItem.format.container val ext = downloadItem.container
if (audioQualityId.isNotBlank()) request.addOption("-f", audioQualityId) if (audioQualityId.isNotBlank()) request.addOption("-f", audioQualityId)
request.addOption("-x") request.addOption("-x")
if(ext.isNotBlank()){ if(ext.isNotBlank()){
if(!ext.matches("(webm)|(Default)|(${context.getString(R.string.defaultValue)})".toRegex())){ if(!ext.matches("(webm)|(Default)|(${context.getString(R.string.defaultValue)})".toRegex())){
request.addOption("--audio-format", downloadItem.format.container) request.addOption("--audio-format", ext)
} }
} }
@ -246,10 +241,10 @@ class DownloadWorker(
} }
Log.e(TAG, formatArgument) Log.e(TAG, formatArgument)
request.addOption("-f", formatArgument) request.addOption("-f", formatArgument)
val format = downloadItem.format.container val outputFormat = downloadItem.container
if(format.isNotEmpty() && format != "Default" && format != context.getString(R.string.defaultValue)){ if(outputFormat.isNotEmpty() && outputFormat != "Default" && outputFormat != context.getString(R.string.defaultValue)){
request.addOption("--merge-output-format", format.lowercase()) request.addOption("--merge-output-format", outputFormat.lowercase())
if (format != "webm") { if (outputFormat != "webm") {
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false) val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
if (embedThumb) { if (embedThumb) {
request.addOption("--embed-thumbnail") request.addOption("--embed-thumbnail")
@ -293,7 +288,7 @@ class DownloadWorker(
val logDownloads = sharedPreferences.getBoolean("log_downloads", false) && !sharedPreferences.getBoolean("incognito", false) val logDownloads = sharedPreferences.getBoolean("log_downloads", false) && !sharedPreferences.getBoolean("incognito", false)
val logFolder = File(context.filesDir.absolutePath + "/logs") val logFolder = File(context.filesDir.absolutePath + "/logs")
val logFile = fileUtil.getLogFile(context, downloadItem) val logFile = FileUtil.getLogFile(context, downloadItem)
if (logDownloads){ if (logDownloads){
logFolder.mkdirs() logFolder.mkdirs()
@ -320,11 +315,11 @@ class DownloadWorker(
} }
}.onSuccess { }.onSuccess {
//move file from internal to set download directory //move file from internal to set download directory
setProgressAsync(workDataOf("progress" to 100, "output" to "Moving file to ${fileUtil.formatPath(downloadLocation)}", "id" to downloadItem.id, "log" to logDownloads)) setProgressAsync(workDataOf("progress" to 100, "output" to "Moving file to ${FileUtil.formatPath(downloadLocation)}", "id" to downloadItem.id, "log" to logDownloads))
var finalPath : String? var finalPath : String?
try { try {
finalPath = fileUtil.moveFile(tempFileDir.absoluteFile,context, downloadLocation, keepCache){ p -> finalPath = FileUtil.moveFile(tempFileDir.absoluteFile,context, downloadLocation, keepCache){ p ->
setProgressAsync(workDataOf("progress" to p, "output" to "Moving file to ${fileUtil.formatPath(downloadLocation)}", "id" to downloadItem.id, "log" to logDownloads)) setProgressAsync(workDataOf("progress" to p, "output" to "Moving file to ${FileUtil.formatPath(downloadLocation)}", "id" to downloadItem.id, "log" to logDownloads))
} }
setProgressAsync(workDataOf("progress" to 100, "output" to "Moved file to $finalPath", "id" to downloadItem.id, "log" to logDownloads)) setProgressAsync(workDataOf("progress" to 100, "output" to "Moved file to $finalPath", "id" to downloadItem.id, "log" to logDownloads))
}catch (e: Exception){ }catch (e: Exception){

View file

@ -32,7 +32,6 @@ class TerminalDownloadWorker(
if (command!!.isEmpty()) return Result.failure() if (command!!.isEmpty()) return Result.failure()
val notificationUtil = NotificationUtil(context) val notificationUtil = NotificationUtil(context)
val fileUtil = FileUtil()
val handler = Handler(Looper.getMainLooper()) val handler = Handler(Looper.getMainLooper())
val intent = Intent(context, MainActivity::class.java) val intent = Intent(context, MainActivity::class.java)
@ -68,7 +67,7 @@ class TerminalDownloadWorker(
val logDownloads = sharedPreferences.getBoolean("log_downloads", false) && !sharedPreferences.getBoolean("incognito", false) val logDownloads = sharedPreferences.getBoolean("log_downloads", false) && !sharedPreferences.getBoolean("incognito", false)
val logFolder = File(context.filesDir.absolutePath + "/logs") val logFolder = File(context.filesDir.absolutePath + "/logs")
val logFile = fileUtil.getLogFileForTerminal(context, command) val logFile = FileUtil.getLogFileForTerminal(context, command)
runCatching { runCatching {
if (logDownloads){ if (logDownloads){
@ -94,7 +93,7 @@ class TerminalDownloadWorker(
}.onSuccess { }.onSuccess {
//move file from internal to set download directory //move file from internal to set download directory
try { try {
fileUtil.moveFile(tempFileDir.absoluteFile,context, downloadLocation!!, false){ p -> FileUtil.moveFile(tempFileDir.absoluteFile,context, downloadLocation!!, false){ p ->
setProgressAsync(workDataOf("progress" to p)) setProgressAsync(workDataOf("progress" to p))
} }
}catch (e: Exception){ }catch (e: Exception){

View file

@ -4,7 +4,6 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
tools:context="com.deniscerri.ytdlnis.ui.more.settings.SettingsActivity"> tools:context="com.deniscerri.ytdlnis.ui.more.settings.SettingsActivity">

View file

@ -1,217 +1,254 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content" android:layout_width="match_parent"
android:orientation="vertical" android:layout_height="wrap_content">
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView <LinearLayout android:layout_width="match_parent"
android:layout_width="wrap_content"
android:text="@string/format"
android:textSize="12sp"
android:layout_margin="20dp"
android:layout_height="wrap_content"/>
<LinearLayout
android:id="@+id/format_id_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:orientation="vertical">
android:paddingHorizontal="10dp"
android:weightSum="100"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal" >
<TextView <TextView
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_weight="50" android:text="@string/format"
android:text="@string/format_id" android:textSize="12sp"
android:textSize="18sp" android:layout_margin="20dp"
android:layout_margin="10dp"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"/>
<TextView <LinearLayout
android:id="@+id/format_id_value" android:id="@+id/format_id_parent"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_weight="50" android:layout_height="wrap_content"
android:textSize="18sp" android:gravity="center"
android:layout_margin="10dp" android:paddingHorizontal="10dp"
android:gravity="end" android:weightSum="100"
android:layout_height="wrap_content"/> android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal" >
<TextView
android:layout_width="0dp"
android:layout_weight="50"
android:text="@string/format_id"
android:textSize="18sp"
android:layout_margin="10dp"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/format_id_value"
android:layout_width="0dp"
android:layout_weight="50"
android:textSize="18sp"
android:layout_margin="10dp"
android:gravity="end"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:id="@+id/format_url_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingHorizontal="10dp"
android:weightSum="100"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal" >
<TextView
android:layout_width="0dp"
android:layout_weight="50"
android:text="@string/url"
android:textSize="18sp"
android:layout_margin="10dp"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/format_url_value"
android:layout_width="0dp"
android:maxLines="1"
android:ellipsize="end"
android:layout_weight="50"
android:textSize="18sp"
android:layout_margin="10dp"
android:gravity="end"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:id="@+id/container_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:weightSum="100"
android:paddingHorizontal="10dp"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal" >
<TextView
android:layout_width="0dp"
android:layout_weight="50"
android:textSize="18sp"
android:text="@string/container"
android:layout_margin="10dp"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/container_value"
android:layout_width="0dp"
android:layout_weight="50"
android:textSize="18sp"
android:layout_margin="10dp"
android:gravity="end"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:id="@+id/codec_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:weightSum="100"
android:paddingHorizontal="10dp"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal" >
<TextView
android:layout_width="0dp"
android:layout_weight="50"
android:textSize="18sp"
android:text="@string/codec"
android:layout_margin="10dp"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/codec_value"
android:layout_width="0dp"
android:layout_weight="50"
android:textSize="18sp"
android:layout_margin="10dp"
android:gravity="end"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:id="@+id/filesize_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:weightSum="100"
android:paddingHorizontal="10dp"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal" >
<TextView
android:layout_width="0dp"
android:layout_weight="50"
android:text="@string/file_size"
android:textSize="18sp"
android:layout_margin="10dp"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/filesize_value"
android:layout_width="0dp"
android:layout_weight="50"
android:textSize="18sp"
android:layout_margin="10dp"
android:gravity="end"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:id="@+id/format_note_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:weightSum="100"
android:paddingHorizontal="10dp"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal" >
<TextView
android:layout_width="0dp"
android:text="@string/quality"
android:layout_weight="50"
android:textSize="18sp"
android:layout_margin="10dp"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/format_note_value"
android:layout_width="0dp"
android:layout_weight="50"
android:textSize="18sp"
android:layout_margin="10dp"
android:gravity="end"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:id="@+id/fps_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:weightSum="100"
android:paddingHorizontal="10dp"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal" >
<TextView
android:layout_width="0dp"
android:layout_weight="50"
android:text="FPS"
android:textSize="18sp"
android:layout_margin="10dp"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/fps_value"
android:layout_width="0dp"
android:layout_weight="50"
android:textSize="18sp"
android:layout_margin="10dp"
android:gravity="end"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:id="@+id/asr_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:weightSum="100"
android:paddingHorizontal="10dp"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal" >
<TextView
android:layout_width="0dp"
android:layout_weight="50"
android:text="@string/audio_samplerate"
android:textSize="18sp"
android:layout_margin="10dp"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/asr_value"
android:layout_width="0dp"
android:layout_weight="50"
android:textSize="18sp"
android:layout_margin="10dp"
android:gravity="end"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout> </LinearLayout>
<LinearLayout </ScrollView>
android:id="@+id/container_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:weightSum="100"
android:paddingHorizontal="10dp"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal" >
<TextView
android:layout_width="0dp"
android:layout_weight="50"
android:textSize="18sp"
android:text="@string/container"
android:layout_margin="10dp"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/container_value"
android:layout_width="0dp"
android:layout_weight="50"
android:textSize="18sp"
android:layout_margin="10dp"
android:gravity="end"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:id="@+id/codec_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:weightSum="100"
android:paddingHorizontal="10dp"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal" >
<TextView
android:layout_width="0dp"
android:layout_weight="50"
android:textSize="18sp"
android:text="@string/codec"
android:layout_margin="10dp"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/codec_value"
android:layout_width="0dp"
android:layout_weight="50"
android:textSize="18sp"
android:layout_margin="10dp"
android:gravity="end"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:id="@+id/filesize_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:weightSum="100"
android:paddingHorizontal="10dp"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal" >
<TextView
android:layout_width="0dp"
android:layout_weight="50"
android:text="@string/file_size"
android:textSize="18sp"
android:layout_margin="10dp"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/filesize_value"
android:layout_width="0dp"
android:layout_weight="50"
android:textSize="18sp"
android:layout_margin="10dp"
android:gravity="end"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:id="@+id/format_note_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:weightSum="100"
android:paddingHorizontal="10dp"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal" >
<TextView
android:layout_width="0dp"
android:text="@string/quality"
android:layout_weight="50"
android:textSize="18sp"
android:layout_margin="10dp"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/format_note_value"
android:layout_width="0dp"
android:layout_weight="50"
android:textSize="18sp"
android:layout_margin="10dp"
android:gravity="end"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:id="@+id/fps_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:weightSum="100"
android:paddingHorizontal="10dp"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal" >
<TextView
android:layout_width="0dp"
android:layout_weight="50"
android:text="FPS"
android:textSize="18sp"
android:layout_margin="10dp"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/fps_value"
android:layout_width="0dp"
android:layout_weight="50"
android:textSize="18sp"
android:layout_margin="10dp"
android:gravity="end"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:id="@+id/asr_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:weightSum="100"
android:paddingHorizontal="10dp"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal" >
<TextView
android:layout_width="0dp"
android:layout_weight="50"
android:text="@string/audio_samplerate"
android:textSize="18sp"
android:layout_margin="10dp"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/asr_value"
android:layout_width="0dp"
android:layout_weight="50"
android:textSize="18sp"
android:layout_margin="10dp"
android:gravity="end"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>

View file

@ -271,4 +271,6 @@
<string name="swipe_gestures_summary">Swipe items for certain actions</string> <string name="swipe_gestures_summary">Swipe items for certain actions</string>
<string name="crop_thumb">Crop Thumbnail</string> <string name="crop_thumb">Crop Thumbnail</string>
<string name="crop_thumb_summary">Crop the thumbnail into a square for audio downloads</string> <string name="crop_thumb_summary">Crop the thumbnail into a square for audio downloads</string>
<string name="locale">Locale</string>
<string name="audio_only_item">This item only contains audio formats</string>
</resources> </resources>

View file

@ -5,19 +5,16 @@
<Preference <Preference
app:icon="@drawable/ic_music_downloaded" app:icon="@drawable/ic_music_downloaded"
app:key="music_path" app:key="music_path"
app:defaultValue="@string/music_path"
app:title="@string/music_directory" /> app:title="@string/music_directory" />
<Preference <Preference
app:icon="@drawable/ic_video_downloaded" app:icon="@drawable/ic_video_downloaded"
app:key="video_path" app:key="video_path"
app:defaultValue="@string/video_path"
app:title="@string/video_directory" /> app:title="@string/video_directory" />
<Preference <Preference
app:icon="@drawable/ic_terminal" app:icon="@drawable/ic_terminal"
app:key="command_path" app:key="command_path"
app:defaultValue="@string/command_path"
app:title="@string/command_directory" /> app:title="@string/command_directory" />
<Preference <Preference

View file

@ -2,7 +2,7 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory android:title="Locale"> <PreferenceCategory android:title="@string/locale">
<ListPreference <ListPreference
android:entries="@array/language_values" android:entries="@array/language_values"
android:entryValues="@array/language_values" android:entryValues="@array/language_values"