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.HomeFragment
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.UpdateUtil
import com.google.android.material.bottomnavigation.BottomNavigationView
@ -67,7 +68,6 @@ class MainActivity : BaseActivity() {
private lateinit var navHostFragment : NavHostFragment
private lateinit var navController : NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ThemeUtil.updateTheme(this)
@ -338,9 +338,9 @@ class MainActivity : BaseActivity() {
private fun createDefaultFolders(){
val audio = File(getString(R.string.music_path))
val video = File(getString(R.string.video_path))
val command = File(getString(R.string.command_path))
val audio = File(FileUtil.getDefautAudioPath())
val video = File(FileUtil.getDefautVideoPath())
val command = File(FileUtil.getDefaultCommandPath())
audio.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()) {
private val onItemClickListener: OnItemClickListener
private val activity: Activity
private val fileUtil: FileUtil
init {
this.onItemClickListener = onItemClickListener
this.activity = activity
fileUtil = FileUtil()
}
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)
fileSize.text = fileUtil.convertFileSize(item.format.filesize)
fileSize.text = FileUtil.convertFileSize(item.format.filesize)
if (fileSize.text == "?") fileSize.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()) {
private val onItemClickListener: OnItemClickListener
private val activity: Activity
private var fileUtil: FileUtil
init {
this.onItemClickListener = onItemClickListener
this.activity = activity
fileUtil = FileUtil()
}
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 fileSizeReadable = fileUtil.convertFileSize(item.format.filesize)
val fileSizeReadable = FileUtil.convertFileSize(item.format.filesize)
if (fileSizeReadable == "?") fileSize.visibility = View.GONE
else {
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()) {
private val onItemClickListener: OnItemClickListener
private val activity: Activity
private var fileUtil: FileUtil
private val checkedItems: ArrayList<Long>
init {
checkedItems = ArrayList()
this.onItemClickListener = onItemClickListener
this.activity = activity
fileUtil = FileUtil()
}
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 fileSizeReadable = fileUtil.convertFileSize(item.format.filesize)
val fileSizeReadable = FileUtil.convertFileSize(item.format.filesize)
if (fileSizeReadable == "?") fileSize.visibility = View.GONE
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)
else -> {
actionButton.setIconResource(R.drawable.ic_baseline_file_open_24)
val logFile = fileUtil.getLogFile(activity, item)
val logFile = FileUtil.getLogFile(activity, item)
if (!logFile.exists()){
actionButton.visibility = View.GONE
}

View file

@ -2,6 +2,7 @@ package com.deniscerri.ytdlnis.database
import androidx.room.TypeConverter
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.VideoPreferences
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
@ -50,4 +51,16 @@ class Converters {
fun videoPreferencesToString(videoPreferences: VideoPreferences): String = Gson().toJson(videoPreferences)
@TypeConverter
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)
@Database(
entities = [ResultItem::class, HistoryItem::class, DownloadItem::class, CommandTemplate::class, SearchHistoryItem::class, TemplateShortcut::class, CookieItem::class],
version = 5,
version = 6,
autoMigrations = [
AutoMigration (from = 1, to = 2),
AutoMigration (from = 2, to = 3),
AutoMigration (from = 3, to = 4),
AutoMigration (from = 4, to = 5)
AutoMigration (from = 4, to = 5),
AutoMigration (from = 5, to = 6)
]
)
abstract class DBManager : RoomDatabase(){

View file

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

View file

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

View file

@ -1,5 +1,6 @@
package com.deniscerri.ytdlnis.database.models
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@ -15,5 +16,8 @@ data class ResultItem(
val website: String,
var playlistTitle: String,
var formats: ArrayList<Format>,
@ColumnInfo(defaultValue = "")
var urls: String,
var chapters: ArrayList<ChapterItem>?,
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()
fun getAll() : List<CommandTemplate> {
return commandDao.getAllTemplates();
return commandDao.getAllTemplates()
}
fun getTotalNumber() : Int {
@ -34,7 +34,7 @@ class CommandTemplateRepository(private val commandDao: CommandTemplateDao) {
}
fun getAllShortCuts() : List<TemplateShortcut> {
return commandDao.getAllShortcuts();
return commandDao.getAllShortcuts()
}
suspend fun insertShortcut(item: TemplateShortcut){

View file

@ -59,9 +59,8 @@ class HistoryRepository(private val historyDao: HistoryDao) {
}
suspend fun clearDeletedHistory(){
val fileUtil = FileUtil()
items.value?.forEach { item ->
if (!fileUtil.exists(item.downloadPath)){
if (!FileUtil.exists(item.downloadPath)){
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.VideoPreferences
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.InfoUtil
import com.deniscerri.ytdlnis.work.DownloadWorker
import com.google.gson.Gson
@ -46,7 +47,7 @@ import java.util.Locale
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 sharedPreferences: SharedPreferences
private val commandTemplateDao: CommandTemplateDao
@ -152,9 +153,9 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
}
val downloadPath = when(type){
Type.audio -> sharedPreferences.getString("music_path", File(Environment.DIRECTORY_DOWNLOADS, "YTDLnis/Audio").absolutePath)
Type.video -> sharedPreferences.getString("video_path", File(Environment.DIRECTORY_DOWNLOADS, "YTDLnis/Video").absolutePath)
else -> sharedPreferences.getString("command_path", File(Environment.DIRECTORY_DOWNLOADS, "YTDLnis/Command").absolutePath)
Type.audio -> sharedPreferences.getString("music_path", FileUtil.getDefautAudioPath())
Type.video -> sharedPreferences.getString("video_path", FileUtil.getDefautVideoPath())
else -> sharedPreferences.getString("command_path", FileUtil.getDefaultCommandPath())
}
val sponsorblock = sharedPreferences.getStringSet("sponsorblock_filters", emptySet())
@ -170,6 +171,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
resultItem.duration,
type,
getFormat(resultItem.formats, type),
sharedPreferences.getString("video_format", "Default")!!,
"",
resultItem.formats,
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.playlistTitle,
downloadItem.allFormats,
"",
arrayListOf(),
System.currentTimeMillis()
)
@ -204,6 +208,8 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
downloadItem.website,
"",
arrayListOf(),
"",
arrayListOf(),
System.currentTimeMillis()
)
@ -220,6 +226,8 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
"",
"",
arrayListOf(),
"",
arrayListOf(),
System.currentTimeMillis()
)
@ -247,9 +255,15 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
}
val downloadPath = when(historyItem.type){
Type.audio -> sharedPreferences.getString("music_path", File(Environment.DIRECTORY_DOWNLOADS, "YTDLnis/Audio").absolutePath)
Type.video -> sharedPreferences.getString("video_path", File(Environment.DIRECTORY_DOWNLOADS, "YTDLnis/Video").absolutePath)
else -> sharedPreferences.getString("command_path", File(Environment.DIRECTORY_DOWNLOADS, "YTDLnis/Command").absolutePath)
Type.audio -> sharedPreferences.getString("music_path", FileUtil.getDefautAudioPath())
Type.video -> sharedPreferences.getString("video_path", FileUtil.getDefautVideoPath())
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())
@ -265,6 +279,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
historyItem.duration,
historyItem.type,
historyItem.format,
container,
"",
ArrayList(),
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 {
val currentTime = System.currentTimeMillis()
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){
repository.delete(item)
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)
if (message != null && path != null) {
if (notificationId != 0) NotificationUtil(this).cancelDownloadNotification(notificationId)
val uiUtil = UiUtil(FileUtil())
val uiUtil = UiUtil()
uiUtil.shareFileIntent(this, listOf(path))
this.finishAffinity()
}

View file

@ -11,10 +11,5 @@ open class BaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
ThemeUtil.updateTheme(this)
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.ui.downloadcard.DownloadBottomSheetDialog
import com.deniscerri.ytdlnis.ui.downloadcard.DownloadMultipleBottomSheetDialog
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.InfoUtil
import com.facebook.shimmer.ShimmerFrameLayout
import com.google.android.material.appbar.AppBarLayout
@ -88,7 +87,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
private var uiHandler: Handler? = null
private var resultsList: List<ResultItem?>? = null
private var selectedObjects: ArrayList<ResultItem>? = null
private var fileUtil: FileUtil? = null
private var quickLaunchSheet = false
private var sharedPreferences: SharedPreferences? = null
private var _binding : FragmentHomeBinding? = null
@ -114,7 +112,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
fragmentContext = context
layoutinflater = LayoutInflater.from(context)
fileUtil = FileUtil()
uiHandler = Handler(Looper.getMainLooper())
selectedObjects = ArrayList()

View file

@ -50,7 +50,7 @@ class ConfigureDownloadBottomSheetDialog(private val resultItem: ResultItem, pri
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
commandTemplateDao = DBManager.getInstance(requireContext()).commandTemplateDao
onDownloadItemUpdateListener = listener
uiUtil = UiUtil(FileUtil())
uiUtil = UiUtil()
}
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
fragmentAdapter = DownloadFragmentAdapter(
@ -111,8 +118,14 @@ class ConfigureDownloadBottomSheetDialog(private val resultItem: ResultItem, pri
viewPager2.setCurrentItem(0, false)
}
Type.video -> {
tabLayout.selectTab(tabLayout.getTabAt(1))
viewPager2.setCurrentItem(1, false)
if (isAudioOnly){
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 -> {
tabLayout.selectTab(tabLayout.getTabAt(2))
@ -127,6 +140,9 @@ class ConfigureDownloadBottomSheetDialog(private val resultItem: ResultItem, pri
if (tab!!.position == 2 && commandTemplateNr == 0){
tabLayout.selectTab(tabLayout.getTabAt(1))
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{
viewPager2.setCurrentItem(tab.position, false)
}

View file

@ -60,9 +60,8 @@ import java.util.*
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 fileUtil: FileUtil
private lateinit var infoUtil: InfoUtil
private lateinit var uiUtil: UiUtil
private lateinit var player: Player
@ -86,14 +85,12 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
private lateinit var chipGroup : ChipGroup
private var timeSeconds by Delegates.notNull<Int>()
private lateinit var chapters: List<ChapterItem>
private lateinit var selectedCuts: MutableList<String>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
fileUtil = FileUtil()
uiUtil = UiUtil(fileUtil)
uiUtil = UiUtil()
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)
videoView.player = player
timeSeconds = convertStringToTimestamp(item.duration)
chapters = emptyList()
if (chapters == null) chapters = emptyList()
//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)
suggestedChips = view.findViewById(R.id.chapters)
suggestedChapters = view.findViewById(R.id.suggested_cuts)
initCutSection()
//cut 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)
chipGroup = view.findViewById(R.id.cut_list_chip_group)
selectedCuts = if (chipGroup.childCount == 0){
mutableListOf()
}else {
@ -183,6 +181,7 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
chipGroup.children.map { (it as Chip).text.toString() }.toMutableList()
}
initCutSection()
initCutListSection()
if (item.downloadSections.isBlank()) cutSection.visibility = View.VISIBLE
@ -190,16 +189,22 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
lifecycleScope.launch {
try {
val data = withContext(Dispatchers.IO){
infoUtil.getStreamingUrlAndChapters(item.url)
val data : MutableList<String?> = withContext(Dispatchers.IO){
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.size == 2){
@ -375,11 +380,11 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
}
private fun populateSuggestedChapters(){
if (chapters.isEmpty()) suggestedChapters.visibility = View.GONE
if (chapters!!.isEmpty()) suggestedChapters.visibility = View.GONE
else {
suggestedChapters.visibility = View.VISIBLE
suggestedChips.removeAllViews()
chapters.forEach {
chapters!!.forEach {
val chip = layoutInflater.inflate(R.layout.suggestion_chip, chipGroup, false) as Chip
chip.text = it.title
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 lateinit var downloadViewModel : DownloadViewModel
private lateinit var resultViewModel : ResultViewModel
private lateinit var fileUtil : FileUtil
private lateinit var uiUtil : UiUtil
private lateinit var title : TextInputLayout
@ -71,8 +70,7 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
fileUtil = FileUtil()
uiUtil = UiUtil(fileUtil)
uiUtil = UiUtil()
return fragmentView
}
@ -112,7 +110,7 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
})
saveDir = view.findViewById(R.id.outputPath)
saveDir.editText!!.setText(
fileUtil.formatPath(downloadItem.downloadPath)
FileUtil.formatPath(downloadItem.downloadPath)
)
saveDir.editText!!.isFocusable = false
saveDir.editText!!.isClickable = true
@ -124,8 +122,8 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
audioPathResultLauncher.launch(intent)
}
freeSpace = view.findViewById(R.id.freespace)
val free = fileUtil.convertFileSize(
File(fileUtil.formatPath(downloadItem.downloadPath)).freeSpace)
val free = FileUtil.convertFileSize(
File(FileUtil.formatPath(downloadItem.downloadPath)).freeSpace)
freeSpace.text = String.format( getString(R.string.freespace) + ": " + free)
if (free == "?") freeSpace.visibility = View.GONE
@ -157,7 +155,6 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
}
}
formats = allFormats.first().toMutableList()
downloadItem.format.container = container.editText?.text.toString()
}
}
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!!
containerAutoCompleteTextView.setText(downloadItem.format.container.ifEmpty { getString(R.string.defaultValue) }, false)
downloadItem.container = if (containerPreference == getString(R.string.defaultValue)) "" else containerPreference!!
containerAutoCompleteTextView.setText(downloadItem.container.ifEmpty { getString(R.string.defaultValue) }, false)
(container!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long ->
downloadItem.format.container = containers[index]
if (containers[index] == getString(R.string.defaultValue)) downloadItem.format.container = ""
downloadItem.container = containers[index]
if (containers[index] == getString(R.string.defaultValue)) downloadItem.container = ""
}
val embedThumb = view.findViewById<Chip>(R.id.embed_thumb)
@ -313,7 +310,7 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
}
cut.setOnClickListener {
if (parentFragmentManager.findFragmentByTag("cutVideoSheet") == null){
val bottomSheet = CutVideoBottomSheetDialog(downloadItem, cutVideoListener)
val bottomSheet = CutVideoBottomSheetDialog(downloadItem, resultItem.urls, resultItem.chapters, cutVideoListener)
bottomSheet.show(parentFragmentManager, "cutVideoSheet")
}
}
@ -340,10 +337,10 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
}
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(
File(fileUtil.formatPath(downloadItem.downloadPath)).freeSpace)
val free = FileUtil.convertFileSize(
File(FileUtil.formatPath(downloadItem.downloadPath)).freeSpace)
freeSpace.text = String.format( getString(R.string.freespace) + ": " + free)
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]
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
commandTemplateDao = DBManager.getInstance(requireContext()).commandTemplateDao
uiUtil = UiUtil(FileUtil())
uiUtil = UiUtil()
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
fragmentAdapter = DownloadFragmentAdapter(
@ -121,8 +128,14 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
viewPager2.setCurrentItem(0, false)
}
Type.video -> {
tabLayout.getTabAt(1)!!.select()
viewPager2.setCurrentItem(1, false)
if (isAudioOnly){
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 -> {
tabLayout.getTabAt(2)!!.select()
@ -138,7 +151,11 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
if (tab!!.position == 2 && commandTemplateNr == 0){
tabLayout.selectTab(tabLayout.getTabAt(1))
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)
}
}

View file

@ -42,7 +42,6 @@ class DownloadCommandFragment(private val resultItem: ResultItem, private var cu
private var activity: Activity? = null
private lateinit var downloadViewModel : DownloadViewModel
private lateinit var commandTemplateViewModel : CommandTemplateViewModel
private lateinit var fileUtil : FileUtil
private lateinit var uiUtil : UiUtil
private lateinit var saveDir : TextInputLayout
private lateinit var freeSpace : TextView
@ -59,8 +58,7 @@ class DownloadCommandFragment(private val resultItem: ResultItem, private var cu
activity = getActivity()
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
commandTemplateViewModel = ViewModelProvider(this)[CommandTemplateViewModel::class.java]
fileUtil = FileUtil()
uiUtil = UiUtil(fileUtil)
uiUtil = UiUtil()
return fragmentView
}
@ -138,7 +136,7 @@ class DownloadCommandFragment(private val resultItem: ResultItem, private var cu
saveDir = view.findViewById(R.id.outputPath)
saveDir.editText!!.setText(
fileUtil.formatPath(downloadItem.downloadPath)
FileUtil.formatPath(downloadItem.downloadPath)
)
saveDir.editText!!.isFocusable = false
saveDir.editText!!.isClickable = true
@ -151,8 +149,8 @@ class DownloadCommandFragment(private val resultItem: ResultItem, private var cu
}
freeSpace = view.findViewById(R.id.freespace)
freeSpace.text = String.format(getString(R.string.freespace) + ": " + fileUtil.convertFileSize(
File(fileUtil.formatPath(downloadItem.downloadPath)).freeSpace
freeSpace.text = String.format(getString(R.string.freespace) + ": " + FileUtil.convertFileSize(
File(FileUtil.formatPath(downloadItem.downloadPath)).freeSpace
))
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()
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(
File(fileUtil.formatPath(downloadItem.downloadPath)).freeSpace
freeSpace.text = String.format(getString(R.string.freespace) + ": " + FileUtil.convertFileSize(
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 recyclerView: RecyclerView
private lateinit var behavior: BottomSheetBehavior<View>
private lateinit var fileUtil: FileUtil
private lateinit var uiUtil: UiUtil
private lateinit var bottomAppBar: BottomAppBar
@ -69,8 +68,7 @@ class DownloadMultipleBottomSheetDialog(private var results: List<ResultItem?>,
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
commandTemplateViewModel = ViewModelProvider(this)[CommandTemplateViewModel::class.java]
fileUtil = FileUtil()
uiUtil = UiUtil(fileUtil)
uiUtil = UiUtil()
}
@SuppressLint("RestrictedApi", "NotifyDataSetChanged")
@ -291,7 +289,7 @@ class DownloadMultipleBottomSheetDialog(private var results: List<ResultItem?>,
items.forEach {
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 lateinit var downloadViewModel : DownloadViewModel
private lateinit var resultViewModel: ResultViewModel
private lateinit var fileUtil : FileUtil
private lateinit var uiUtil : UiUtil
private lateinit var title : TextInputLayout
@ -72,8 +71,7 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
resultViewModel = ViewModelProvider(this@DownloadVideoFragment)[ResultViewModel::class.java]
fileUtil = FileUtil()
uiUtil = UiUtil(fileUtil)
uiUtil = UiUtil()
return fragmentView
}
@ -114,7 +112,7 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
saveDir = view.findViewById(R.id.outputPath)
saveDir.editText!!.setText(
fileUtil.formatPath(downloadItem.downloadPath)
FileUtil.formatPath(downloadItem.downloadPath)
)
saveDir.editText!!.isFocusable = false
saveDir.editText!!.isClickable = true
@ -127,8 +125,8 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
}
freeSpace = view.findViewById(R.id.freespace)
val free = fileUtil.convertFileSize(
File(fileUtil.formatPath(downloadItem.downloadPath)).freeSpace)
val free = FileUtil.convertFileSize(
File(FileUtil.formatPath(downloadItem.downloadPath)).freeSpace)
freeSpace.text = String.format( getString(R.string.freespace) + ": " + free)
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 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 {
override fun onFormatClick(allFormats: List<List<Format>>, item: List<Format>) {
downloadItem.format = item.first()
@ -162,7 +160,6 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
}
formats = allFormats.first().toMutableList()
uiUtil.populateFormatCard(formatCard, item.first(), item.drop(1))
downloadItem.format.container = container.editText?.text.toString()
}
}
formatCard.setOnClickListener{
@ -185,15 +182,15 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
containers
)
)
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) },
downloadItem.container.ifEmpty { getString(R.string.defaultValue) },
false)
(container!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long ->
downloadItem.format.container = containers[index]
if (containers[index] == getString(R.string.defaultValue)) downloadItem.format.container = ""
downloadItem.container = containers[index]
if (containers[index] == getString(R.string.defaultValue)) downloadItem.container = ""
}
@ -312,7 +309,7 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
}
cut.setOnClickListener {
if (parentFragmentManager.findFragmentByTag("cutVideoSheet") == null){
val bottomSheet = CutVideoBottomSheetDialog(downloadItem, cutVideoListener)
val bottomSheet = CutVideoBottomSheetDialog(downloadItem, resultItem.urls, resultItem.chapters, cutVideoListener)
bottomSheet.show(parentFragmentManager, "cutVideoSheet")
}
}
@ -332,17 +329,17 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
builder.setView(inputLayout)
builder.setPositiveButton(
getString(R.string.ok)
) { dialog: DialogInterface?, which: Int ->
) { _: DialogInterface?, _: Int ->
downloadItem.customFileNameTemplate = editText.text.toString()
}
// handle the negative button of the alert dialog
builder.setNegativeButton(
getString(R.string.cancel)
) { dialog: DialogInterface?, which: Int -> }
) { _: DialogInterface?, _: Int -> }
val dialog = builder.create()
editText.doOnTextChanged { text, start, before, count ->
editText.doOnTextChanged { _, _, _, _ ->
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = editText.text.isNotEmpty()
}
dialog.show()
@ -363,7 +360,7 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
subtitleLanguages.visibility = View.VISIBLE
}
saveSubtitles.setOnCheckedChangeListener { compoundButton, b ->
saveSubtitles.setOnCheckedChangeListener { _, _ ->
if (saveSubtitles.isChecked) subtitleLanguages.visibility = View.VISIBLE
else subtitleLanguages.visibility = View.GONE
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)
removeAudio.setOnCheckedChangeListener { compoundButton, b ->
removeAudio.setOnCheckedChangeListener { _, _ ->
downloadItem.videoPreferences.removeAudio = removeAudio.isChecked
}
@ -398,11 +395,10 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
)
}
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(
File(fileUtil.formatPath(downloadItem.downloadPath)).freeSpace)
val free = FileUtil.convertFileSize(
File(FileUtil.formatPath(downloadItem.downloadPath)).freeSpace)
freeSpace.text = String.format( getString(R.string.freespace) + ": " + free)
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() {
private lateinit var behavior: BottomSheetBehavior<View>
private lateinit var fileUtil: FileUtil
private lateinit var infoUtil: InfoUtil
private lateinit var uiUtil: UiUtil
private lateinit var sharedPreferences: SharedPreferences
@ -48,8 +47,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
fileUtil = FileUtil()
uiUtil = UiUtil(fileUtil)
uiUtil = UiUtil()
infoUtil = InfoUtil(requireActivity().applicationContext)
formatCollection = mutableListOf()
chosenFormats = listOf()
@ -116,11 +114,12 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
}
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 {
lifecycleScope.launch {
chosenFormats = emptyList()
try {
refreshBtn.isEnabled = false
formatListLinearLayout.visibility = View.GONE
@ -132,9 +131,11 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
val res = withContext(Dispatchers.IO){
infoUtil.getFormats(items.first()!!.url)
}
chosenFormats = res.formats.filter { it.filesize != 0L }
res.filter { it.format_note != "storyboard" }
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()
//playlist format filtering

View file

@ -25,7 +25,7 @@ import com.google.android.material.progressindicator.LinearProgressIndicator
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 fragmentView: View? = null
private var activity: Activity? = null
@ -34,7 +34,6 @@ class ActiveDownloadsFragment() : Fragment(), ActiveDownloadAdapter.OnItemClickL
private lateinit var activeDownloads : ActiveDownloadAdapter
lateinit var downloadItem: DownloadItem
private lateinit var notificationUtil: NotificationUtil
private lateinit var fileUtil: FileUtil
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)
activity = getActivity()
notificationUtil = NotificationUtil(requireContext())
fileUtil = FileUtil()
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
list = listOf()
return fragmentView
@ -102,7 +100,7 @@ class ActiveDownloadsFragment() : Fragment(), ActiveDownloadAdapter.OnItemClickL
}
override fun onOutputClick(item: DownloadItem) {
val logFile = fileUtil.getLogFile(requireContext(), item)
val logFile = FileUtil.getLogFile(requireContext(), item)
if (logFile.exists()) {
val bundle = Bundle()
bundle.putString("logpath", logFile.absolutePath)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -64,7 +64,6 @@ class TerminalActivity : BaseActivity() {
private lateinit var observer: FileObserver
private lateinit var imm : InputMethodManager
private lateinit var uiUtil: UiUtil
private lateinit var fileUtil: FileUtil
var context: Context? = null
public override fun onCreate(savedInstanceState: Bundle?) {
@ -75,8 +74,7 @@ class TerminalActivity : BaseActivity() {
downloadFile = File(cacheDir.absolutePath + "/$downloadID.txt")
if (! downloadFile.exists()) downloadFile.createNewFile()
imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
fileUtil = FileUtil()
uiUtil = UiUtil(fileUtil)
uiUtil = UiUtil()
context = baseContext
scrollView = findViewById(R.id.custom_command_scrollview)
@ -311,7 +309,7 @@ class TerminalActivity : BaseActivity() {
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 clearCache: Preference? = null
private var fileUtil: FileUtil? = null
private var activeDownloadCount = 0
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
@ -33,7 +32,6 @@ class FolderSettingsFragment : BaseSettingsFragment() {
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
val editor = preferences.edit()
fileUtil = FileUtil()
musicPath = findPreference("music_path")
videoPath = findPreference("video_path")
@ -42,13 +40,13 @@ class FolderSettingsFragment : BaseSettingsFragment() {
clearCache = findPreference("clear_cache")
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()) {
editor.putString("video_path", getString(R.string.video_path))
editor.putString("video_path", FileUtil.getDefautVideoPath())
}
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()) ||
@ -58,7 +56,7 @@ class FolderSettingsFragment : BaseSettingsFragment() {
editor.apply()
musicPath!!.summary = fileUtil?.formatPath(preferences.getString("music_path", "")!!)
musicPath!!.summary = FileUtil.formatPath(preferences.getString("music_path", "")!!)
musicPath!!.onPreferenceClickListener =
Preference.OnPreferenceClickListener {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
@ -68,7 +66,7 @@ class FolderSettingsFragment : BaseSettingsFragment() {
musicPathResultLauncher.launch(intent)
true
}
videoPath!!.summary = fileUtil?.formatPath(preferences.getString("video_path", "")!!)
videoPath!!.summary = FileUtil.formatPath(preferences.getString("video_path", "")!!)
videoPath!!.onPreferenceClickListener =
Preference.OnPreferenceClickListener {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
@ -78,7 +76,7 @@ class FolderSettingsFragment : BaseSettingsFragment() {
videoPathResultLauncher.launch(intent)
true
}
commandPath!!.summary = fileUtil?.formatPath(preferences.getString("command_path", "")!!)
commandPath!!.summary = FileUtil.formatPath(preferences.getString("command_path", "")!!)
commandPath!!.onPreferenceClickListener =
Preference.OnPreferenceClickListener {
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() }
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 =
Preference.OnPreferenceClickListener {
if (activeDownloadCount == 0){
File(requireContext().cacheDir.absolutePath + "/downloads").deleteRecursively()
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() }
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{
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) {
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 editor = sharedPreferences.edit()
when (requestCode) {

View file

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

View file

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

View file

@ -4,6 +4,7 @@ import android.content.Context
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.DocumentsContract
import android.util.Log
import android.webkit.MimeTypeMap
@ -21,7 +22,7 @@ import kotlin.math.log10
import kotlin.math.pow
class FileUtil() {
object FileUtil {
fun deleteFile(path: String){
val file = File(path)
if (file.exists()) {
@ -144,13 +145,26 @@ class FileUtil() {
fun getLogFile(context: Context, item: DownloadItem) : File {
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 {
val titleRegex = Regex("[^A-Za-z\\d ]")
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{
if (s <= 0) return "?"
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.preference.PreferenceManager
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.ResultItem
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.yausername.youtubedl_android.YoutubeDL
import com.yausername.youtubedl_android.YoutubeDLRequest
import org.json.JSONArray
@ -20,6 +22,7 @@ import org.json.JSONObject
import java.io.BufferedReader
import java.io.File
import java.io.InputStreamReader
import java.lang.reflect.Type
import java.net.HttpURLConnection
import java.net.URL
import java.util.*
@ -272,7 +275,9 @@ class InfoUtil(private val context: Context) {
thumb,
"youtube",
"",
ArrayList()
ArrayList(),
"",
ArrayList()
)
} catch (e: Exception) {
Log.e(TAG, e.toString())
@ -326,7 +331,9 @@ class InfoUtil(private val context: Context) {
thumb,
"youtube",
"",
formats
formats,
"",
ArrayList()
)
} catch (e: Exception) {
Log.e(TAG, e.toString())
@ -337,13 +344,8 @@ class InfoUtil(private val context: Context) {
private fun createVideoFromPipedJSON(obj: JSONObject, id: String): ResultItem? {
var video: ResultItem? = null
try {
val title = obj.getString("title").toString()
title.replace("&#39;s", "'")
title.replace("&amp", "&")
val author = obj.getString("uploader").toString()
author.replace("&#39;s", "'")
author.replace("&amp", "&")
val title = Html.fromHtml(obj.getString("title").toString()).toString()
val author = Html.fromHtml(obj.getString("uploader").toString()).toString()
if (author.isBlank()) throw Exception()
val duration = formatIntegerDuration(obj.getInt("duration"), Locale.getDefault())
@ -360,7 +362,6 @@ class InfoUtil(private val context: Context) {
formatObj.acodec = format.getString("codec")
val streamURL = format.getString("url")
formatObj.format_id = streamURL.substringAfter("itag=").substringBefore("&")
formatObj.filesize = (format.getInt("bitrate") / 8 * obj.getInt("duration")).toLong()
formatObj.asr = format.getString("quality")
if (! format.getString("audioTrackName").equals("null", ignoreCase = true)){
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")
val streamURL = format.getString("url")
formatObj.format_id = streamURL.substringAfter("itag=").substringBefore("&")
formatObj.filesize = (format.getInt("bitrate") / 8 * obj.getInt("duration")).toLong()
}catch (e: Exception) {
e.printStackTrace()
}
@ -394,6 +394,17 @@ class InfoUtil(private val context: Context) {
}
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,
url,
title,
@ -402,7 +413,9 @@ class InfoUtil(private val context: Context) {
thumb,
"youtube",
"",
formats
formats,
if (obj.has("hls")) obj.getString("hls") else "",
chapters
)
} catch (e: Exception) {
Log.e(TAG, e.toString())
@ -567,7 +580,7 @@ class InfoUtil(private val context: Context) {
return query!!
}
fun getFormats(url: String) : ResultItem {
fun getFormats(url: String) : List<Format> {
init()
val p = Pattern.compile("^(https?)://(www.)?youtu(.be)?")
val m = p.matcher(url)
@ -576,14 +589,70 @@ class InfoUtil(private val context: Context) {
val id = getIDFromYoutubeURL(url)
val res = genericRequest(pipedURL + "streams/" + id)
if (res.length() == 0) getFromYTDL(url)[0]!!
else createVideoFromPipedJSON(
res, id
)!!
val item = createVideoFromPipedJSON(res, id)
item!!.formats
}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){
init()
val urlsFile = File(context.cacheDir, "urls.txt")
@ -623,16 +692,19 @@ class InfoUtil(private val context: Context) {
try{
if (format.getString("filesize") == "None") continue
}catch (e: Exception) { continue }
val formatProper = Gson().fromJson(format.toString(), Format::class.java)
if (formatProper.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)
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 ( !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)
}catch (e: Exception){
@ -725,20 +797,28 @@ class InfoUtil(private val context: Context) {
if (formatsInJSON != null) {
for (f in 0 until formatsInJSON.length()){
val format = formatsInJSON.getJSONObject(f)
val formatProper = Gson().fromJson(format.toString(), Format::class.java)
if (formatProper.filesize > 0){
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"
}
}
formatProper.container = format.getString("ext")
formats.add(formatProper)
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") && 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())
items.add(ResultItem(0,
url,
@ -748,7 +828,9 @@ class InfoUtil(private val context: Context) {
thumb!!,
website,
playlistTitle!!,
formats
formats,
jsonObject.getString("urls") ?: "",
chapters
)
)
}
@ -821,6 +903,8 @@ class InfoUtil(private val context: Context) {
jsonObject.getString("extractor"),
if (isPlaylist) jsonObject.getString("title") else "",
arrayListOf(),
"",
arrayListOf(),
System.currentTimeMillis()
)
} catch (e: Exception) {
@ -945,7 +1029,7 @@ class InfoUtil(private val context: Context) {
companion object {
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 var countryCODE: String = ""
}

View file

@ -52,7 +52,7 @@ import kotlinx.coroutines.withContext
import java.io.File
import java.util.Calendar
class UiUtil(private val fileUtil: FileUtil) {
class UiUtil() {
@SuppressLint("SetTextI18n")
fun populateFormatCard(formatCard : MaterialCardView, chosenFormat: Format, audioFormats: List<Format>?){
formatCard.findViewById<TextView>(R.id.container).text = chosenFormat.container.uppercase()
@ -79,7 +79,7 @@ class UiUtil(private val fileUtil: FileUtil) {
}
var filesize = chosenFormat.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()
it.forEach {shortcut ->
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
}
commandTemplateViewModel.shortcuts.observe(lifeCycle){ it ->
commandTemplateViewModel.shortcuts.observe(lifeCycle){
shortcutsChipGroup.removeAllViews()
it.forEach {shortcut ->
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)
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 codecParent = bottomSheet.findViewById<LinearLayout>(R.id.codec_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
else {
@ -367,9 +376,9 @@ class UiUtil(private val fileUtil: FileUtil) {
if (format.filesize != 0L) filesizeParent?.visibility = View.GONE
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 {
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()
val notificationUtil = NotificationUtil(context)
val fileUtil = FileUtil()
val infoUtil = InfoUtil(context)
val dbManager = DBManager.getInstance(context)
val dao = dbManager.downloadDao
@ -68,13 +67,9 @@ class DownloadWorker(
}
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
if (sharedPreferences.getBoolean("log_downloads", false)){
updateDownloadItem(downloadItem, infoUtil, dao, resultDao, true, notificationUtil)
}else{
CoroutineScope(Dispatchers.IO).launch {
//update item if its incomplete
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)
@ -174,13 +169,13 @@ class DownloadWorker(
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"
val ext = downloadItem.format.container
val ext = downloadItem.container
if (audioQualityId.isNotBlank()) request.addOption("-f", audioQualityId)
request.addOption("-x")
if(ext.isNotBlank()){
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)
request.addOption("-f", formatArgument)
val format = downloadItem.format.container
if(format.isNotEmpty() && format != "Default" && format != context.getString(R.string.defaultValue)){
request.addOption("--merge-output-format", format.lowercase())
if (format != "webm") {
val outputFormat = downloadItem.container
if(outputFormat.isNotEmpty() && outputFormat != "Default" && outputFormat != context.getString(R.string.defaultValue)){
request.addOption("--merge-output-format", outputFormat.lowercase())
if (outputFormat != "webm") {
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
if (embedThumb) {
request.addOption("--embed-thumbnail")
@ -293,7 +288,7 @@ class DownloadWorker(
val logDownloads = sharedPreferences.getBoolean("log_downloads", false) && !sharedPreferences.getBoolean("incognito", false)
val logFolder = File(context.filesDir.absolutePath + "/logs")
val logFile = fileUtil.getLogFile(context, downloadItem)
val logFile = FileUtil.getLogFile(context, downloadItem)
if (logDownloads){
logFolder.mkdirs()
@ -320,11 +315,11 @@ class DownloadWorker(
}
}.onSuccess {
//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?
try {
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))
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 100, "output" to "Moved file to $finalPath", "id" to downloadItem.id, "log" to logDownloads))
}catch (e: Exception){

View file

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

View file

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

View file

@ -1,217 +1,254 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
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"
<LinearLayout 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" >
android:orientation="vertical">
<TextView
android:layout_width="0dp"
android:layout_weight="50"
android:text="@string/format_id"
android:textSize="18sp"
android:layout_margin="10dp"
android:layout_width="wrap_content"
android:text="@string/format"
android:textSize="12sp"
android:layout_margin="20dp"
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
android:id="@+id/format_id_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/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
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>
</ScrollView>

View file

@ -271,4 +271,6 @@
<string name="swipe_gestures_summary">Swipe items for certain actions</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="locale">Locale</string>
<string name="audio_only_item">This item only contains audio formats</string>
</resources>

View file

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

View file

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