CHANGELOG

- added orange theme (its like yellow but slightly darker)
- added enforce keyframes at cuts preferences (it will also apply when you remove sponsorblock items)
- fixed cookies not getting saved on older droids
- fixed app making new folder instead of merging when moving files
- added ability to put multiple preferred format ids separated by commas for both audio and video
- added support for piped links
- fixed bug where if an item has no formats and you update audio formats and then go to video tab it will not show the video formats too
- made terminal share icon hidden by default
This commit is contained in:
deniscerri 2023-07-20 22:38:45 +02:00
parent bb282424a8
commit 4227b1e43c
No known key found for this signature in database
GPG key ID: 95C43D517D830350
23 changed files with 448 additions and 226 deletions

View file

@ -10,7 +10,7 @@ def properties = new Properties()
def versionMajor = 1
def versionMinor = 6
def versionPatch = 3
def versionBuild = 5 // bump for dogfood builds, public betas, etc.
def versionBuild = 9 // bump for dogfood builds, public betas, etc.
def versionExt = ""
if (versionBuild > 0){
@ -206,4 +206,6 @@ dependencies {
// For RTSP playback support with ExoPlayer
implementation("androidx.media3:media3-exoplayer-rtsp:$media3_version")
implementation("androidx.media3:media3-datasource-cronet:$media3_version")
implementation "com.anggrayudi:storage:1.5.5"
}

View file

@ -2,13 +2,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-feature
android:name="android.software.leanback"
android:required="false" />
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
@ -19,7 +12,7 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.ACTION_OPEN_DOCUMENT" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
@ -38,8 +31,7 @@
android:supportsRtl="true"
android:theme="@style/BaseTheme"
tools:ignore="DataExtractionRules"
tools:targetApi="tiramisu"
android:banner="@mipmap/ic_launcher">
tools:targetApi="tiramisu">
<profileable
android:shell="true"
tools:targetApi="29" />
@ -49,18 +41,7 @@
android:configChanges="smallestScreenSize|layoutDirection|locale|orientation|screenSize"
android:exported="true"
android:windowSoftInputMode="adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/txt" />
</intent-filter>
</activity>
<activity
android:name=".receiver.ShareActivity"
@ -114,7 +95,7 @@
<activity-alias
android:name=".terminalShareAlias"
android:targetActivity=".ui.more.TerminalActivity"
android:enabled="true"
android:enabled="false"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.SEND" />
@ -126,17 +107,20 @@
<activity-alias
android:name=".Default"
android:enabled="true"
android:exported="true"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:configChanges="smallestScreenSize|layoutDirection|locale|orientation|screenSize"
android:targetActivity=".MainActivity"
android:windowSoftInputMode="adjustPan">
android:windowSoftInputMode="adjustPan"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/txt" />
</intent-filter>
</activity-alias>
@ -152,9 +136,12 @@
android:windowSoftInputMode="adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/txt" />
</intent-filter>
</activity-alias>
@ -169,9 +156,12 @@
android:windowSoftInputMode="adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/txt" />
</intent-filter>
</activity-alias>

View file

@ -71,8 +71,12 @@ class CookieViewModel(private val application: Application) : AndroidViewModel(a
if (!hasCookies()) throw Exception("There is no cookies in the database!")
flush()
}
var dbPath = "/data/data/com.deniscerri.ytdl/app_webview/Cookies"
if (!File(dbPath).exists()) dbPath = "/data/data/com.deniscerri.ytdl/app_webview/Default/Cookies"
SQLiteDatabase.openDatabase(
"/data/data/com.deniscerri.ytdl/app_webview/Default/Cookies", null, OPEN_READONLY
dbPath, null, OPEN_READONLY
).run {
val projection = arrayOf(
CookieObject.HOST,

View file

@ -68,8 +68,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
private var defaultVideoFormats : MutableList<Format>
private val videoQualityPreference: String
private val formatIDPreference: String
private val audioFormatIDPreference: String
private val formatIDPreference: List<String>
private val audioFormatIDPreference: List<String>
private val resources : Resources
enum class Type {
audio, video, command
@ -92,8 +92,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
erroredDownloads = repository.erroredDownloads.asLiveData()
videoQualityPreference = sharedPreferences.getString("video_quality", application.getString(R.string.best_quality)).toString()
formatIDPreference = sharedPreferences.getString("format_id", "").toString()
audioFormatIDPreference = sharedPreferences.getString("format_id_audio", "").toString()
formatIDPreference = sharedPreferences.getString("format_id", "").toString().split(",")
audioFormatIDPreference = sharedPreferences.getString("format_id_audio", "").toString().split(",")
val confTmp = Configuration(application.resources.configuration)
confTmp.locale = Locale(sharedPreferences.getString("app_language", "en")!!)
@ -170,7 +170,9 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
val sponsorblock = sharedPreferences.getStringSet("sponsorblock_filters", emptySet())
val audioPreferences = AudioPreferences(embedThumb, false, ArrayList(sponsorblock!!))
val videoPreferences = VideoPreferences(embedSubs, addChapters, false, ArrayList(sponsorblock), saveSubs, audioFormatIDs = ArrayList(resultItem.formats.filter { it.format_id == audioFormatIDPreference }.map { it.format_id }))
val hasPreferredAudioFormats = resultItem.formats.filter { audioFormatIDPreference.contains(it.format_id) }
val videoPreferences = VideoPreferences(embedSubs, addChapters, false, ArrayList(sponsorblock), saveSubs, audioFormatIDs = if (hasPreferredAudioFormats.isEmpty()) arrayListOf() else arrayListOf(hasPreferredAudioFormats.map { it.format_id }.first()))
return DownloadItem(0,
resultItem.url,
@ -317,7 +319,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
return cloneFormat (
try {
try{
formats.first { it.format_note.contains("audio", ignoreCase = true) && it.format_id == audioFormatIDPreference }
formats.first { it.format_note.contains("audio", ignoreCase = true) && audioFormatIDPreference.contains(it.format_id)}
}catch (e: Exception){
formats.last { it.format_note.contains("audio", ignoreCase = true) }
}
@ -332,7 +334,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
try {
val theFormats = formats.ifEmpty { defaultVideoFormats }
try {
formats.first { !it.format_note.contains("audio", ignoreCase = true) && it.format_id == formatIDPreference }
formats.first { !it.format_note.contains("audio", ignoreCase = true) && formatIDPreference.contains(it.format_id)}
}catch (e: Exception){
when (videoQualityPreference) {
"worst" -> {
@ -397,7 +399,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
fun turnResultItemsToDownloadItems(items: List<ResultItem?>) : List<DownloadItem> {
val list : MutableList<DownloadItem> = mutableListOf()
val preferredType = sharedPreferences.getString("preferred_download_type", "video");
val preferredType = sharedPreferences.getString("preferred_download_type", "video")
items.forEach {
list.add(createDownloadItemFromResult(it!!, Type.valueOf(preferredType!!)))
}
@ -454,12 +456,9 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
val activeAndQueuedDownloads = repository.getActiveAndQueuedDownloads()
val allowMeteredNetworks = sharedPreferences.getBoolean("metered_networks", true)
val queuedItems = mutableListOf<DownloadItem>()
var lastDownloadId = repository.getLastDownloadId()
var exists = false
items.forEach {
lastDownloadId++
it.status = DownloadRepository.Status.Queued.toString()
if (activeAndQueuedDownloads.firstOrNull{d ->
d.id = 0
@ -469,7 +468,6 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
exists = true
}else{
if (it.id == 0L){
it.id = lastDownloadId
val insert = async {repository.insert(it)}
val id = insert.await()
it.id = id
@ -492,7 +490,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
var delay = if (it.downloadStartTime != 0L){
it.downloadStartTime - currentTime
} else 0
if (delay < 0L) delay = 0L
if (delay < 0L) delay = 1L
val workConstraints = Constraints.Builder()
if (!allowMeteredNetworks) workConstraints.setRequiredNetworkType(NetworkType.UNMETERED)
@ -501,7 +499,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
.setInputData(Data.Builder().putLong("id", it.id).build())
.addTag("download")
.setConstraints(workConstraints.build())
.setInitialDelay(delay, TimeUnit.MILLISECONDS)
.setInitialDelay(delay, TimeUnit.SECONDS)
.build()
WorkManager.getInstance(context).beginUniqueWork(

View file

@ -103,7 +103,7 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
}
private fun getQueryType(inputQuery: String) : String {
var type = "Search"
val p = Pattern.compile("^(https?)://(www.)?(music.)?youtu(.be)?")
val p = Pattern.compile("(^(https?)://(www.)?(music.)?youtu(.be)?)|(^(https?)://(www.)?piped.video)")
val m = p.matcher(inputQuery)
if (m.find()) {
type = "YT_Video"

View file

@ -140,6 +140,9 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
res
}
if (chosenFormats.isEmpty()) throw Exception()
formats = listOf(res)
//playlist format filtering
}else{
var progress = "0/${items.size}"
@ -154,6 +157,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
formatCollection.add(it)
}
}
formats = formatCollection
val flatFormatCollection = formatCollection.flatten()
val commonFormats = flatFormatCollection.groupingBy { it.format_id }.eachCount().filter { it.value == items.size }.mapValues { flatFormatCollection.first { f -> f.format_id == it.key } }.map { it.value }
chosenFormats = commonFormats.filter { it.filesize != 0L }.mapTo(mutableListOf()) {it.copy()}
@ -196,7 +200,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
//simple video format selection
if (items.size == 1){
listener.onFormatClick(List(items.size){chosenFormats}, listOf(FormatTuple(selectedVideo, selectedAudios)))
listener.onFormatClick(formats, listOf(FormatTuple(selectedVideo, selectedAudios)))
}else{
//playlist format selection
val selectedFormats = mutableListOf<Format>()
@ -208,7 +212,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
selectedFormats.add(selectedVideo)
}
}
listener.onFormatClick(formatCollection, selectedFormats.map { FormatTuple(it, selectedAudios) })
listener.onFormatClick(formats, selectedFormats.map { FormatTuple(it, selectedAudios) })
}
dismiss()
@ -260,7 +264,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
val clickedCard = (clickedformat as MaterialCardView)
if (format.vcodec.isNotBlank() && format.vcodec != "none") {
if (clickedCard.isChecked) {
listener.onFormatClick(List(items.size){finalFormats}, listOf(FormatTuple(format, null)))
listener.onFormatClick(formats, listOf(FormatTuple(format, null)))
dismiss()
}
videoFormatList.forEach { (it as MaterialCardView).isChecked = false }
@ -279,7 +283,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
}
}else{
if (items.size == 1){
listener.onFormatClick(List(items.size){finalFormats}, listOf(FormatTuple(format, null)))
listener.onFormatClick(formats, listOf(FormatTuple(format, null)))
}else{
val selectedFormats = mutableListOf<Format>()
formatCollection.forEach {
@ -290,7 +294,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
selectedFormats.add(format)
}
}
listener.onFormatClick(formatCollection, selectedFormats.map { FormatTuple(it, null) })
listener.onFormatClick(formats, selectedFormats.map { FormatTuple(it, null) })
}
dismiss()
}

View file

@ -447,10 +447,15 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), DownloadItem::class.java)
item.id = 0L
cancelled.add(item)
}
cancelled.asReversed().forEach { f ->
withContext(Dispatchers.IO){
downloadViewModel.insert(item)
downloadViewModel.insert(f)
}
}
if(cancelled.isNotEmpty()){
finalMessage.append("${getString(R.string.cancelled)}: ${cancelled.count()}\n")
}
@ -464,10 +469,15 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), DownloadItem::class.java)
item.id = 0L
errored.add(item)
}
errored.asReversed().forEach { f ->
withContext(Dispatchers.IO){
downloadViewModel.insert(item)
downloadViewModel.insert(f)
}
}
if(errored.isNotEmpty()){
finalMessage.append("${getString(R.string.errored)}: ${errored.count()}\n")
}
@ -491,13 +501,20 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
//command template restore
if(json.has("templates")){
val items = json.getAsJsonArray("templates")
val templates = mutableListOf<CommandTemplate>()
items.forEach {
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), CommandTemplate::class.java)
item.id = 0L
templates.add(item)
}
templates.asReversed().forEach { t ->
withContext(Dispatchers.IO){
commandTemplateViewModel.insert(item)
commandTemplateViewModel.insert(t)
}
}
if(items.count() > 0){
finalMessage.append("${getString(R.string.command_templates)}: ${items.count()}\n")
}

View file

@ -2,15 +2,21 @@ package com.deniscerri.ytdlnis.util
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
import androidx.core.net.toUri
import androidx.documentfile.provider.DocumentFile
import com.anggrayudi.storage.callback.FileCallback
import com.anggrayudi.storage.callback.FolderCallback
import com.anggrayudi.storage.file.copyFileTo
import com.anggrayudi.storage.file.copyFolderTo
import com.anggrayudi.storage.file.getAbsolutePath
import com.anggrayudi.storage.file.moveFileTo
import com.anggrayudi.storage.file.moveFolderTo
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.DownloadItem
import okhttp3.internal.closeQuietly
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
import java.net.URLDecoder
import java.nio.charset.StandardCharsets
@ -70,81 +76,105 @@ object FileUtil {
@Throws(Exception::class)
fun moveFile(originDir: File, context: Context, destDir: String, keepCache: Boolean, progress: (p: Int) -> Unit) : List<String> {
val fileList = mutableListOf<File>()
val dir = File(formatPath(destDir))
if (!dir.exists()) dir.mkdirs()
var currentDirectory = dir
originDir.walk().forEach {
var destFile = File(dir.absolutePath + "/${it.absolutePath.removePrefix(originDir.absolutePath)}")
if (it.isDirectory) {
destFile.mkdirs()
currentDirectory = destFile
return@forEach
}
try {
if (it.name.matches("(^config.*.\\.txt\$)|(rList)|(part-Frag)".toRegex())){
it.delete()
suspend fun moveFile(originDir: File, context: Context, destDir: String, keepCache: Boolean, progress: (p: Int) -> Unit) : List<String> {
return withContext(Dispatchers.Main){
val fileList = mutableListOf<String>()
val dir = File(formatPath(destDir))
if (!dir.exists()) dir.mkdirs()
originDir.walk().forEach {
if (it.isDirectory && it.absolutePath == originDir.absolutePath) return@forEach
var destFile: DocumentFile
try {
if (it.name.matches("(^config.*.\\.txt\$)|(rList)|(part-Frag)".toRegex())){
it.delete()
return@forEach
}
if(it.name.contains(".part-Frag")) return@forEach
val curr = DocumentFile.fromFile(it)
val dst = DocumentFile.fromTreeUri(context, destDir.toUri())
if (it.isDirectory){
withContext(Dispatchers.IO){
curr.copyFolderTo(context, dst!!, skipEmptyFiles = false, callback = object : FolderCallback() {
override fun onStart(folder: DocumentFile, totalFilesToCopy: Int, workerThread: Thread): Long {
return 1000 // update progress every 1 second
}
override fun onParentConflict(destinationFolder: DocumentFile, action: ParentFolderConflictAction, canMerge: Boolean) {
if (canMerge){
action.confirmResolution(ConflictResolution.MERGE)
}else{
action.confirmResolution(ConflictResolution.CREATE_NEW)
}
}
override fun onReport(report: Report) {
progress(report.progress.toInt())
}
override fun onCompleted(result: Result) {
fileList.addAll(result.folder.listFiles().map { f -> f.getAbsolutePath(context) })
it.deleteRecursively()
}
})
}
}else{
withContext(Dispatchers.IO){
curr.copyFileTo(context, dst!!, callback = object : FileCallback() {
override fun onStart(file: Any, workerThread: Thread): Long {
return 1000 // update progress every 1 second
}
override fun onReport(report: Report) {
progress(report.progress.toInt())
}
override fun onCompleted(result: Any) {
destFile = (result as DocumentFile)
fileList.add(destFile.getAbsolutePath(context))
it.deleteRecursively()
super.onCompleted(result)
}
})
}
}
}catch (e: Exception) {
Log.e("error", e.message.toString())
val files = it.listFiles()?.filter { fil -> !fil.isDirectory }?.toTypedArray() ?: arrayOf(it)
for (ff in files){
val newFile = File(dir.absolutePath + "/${ff.absolutePath.removePrefix(originDir.absolutePath)}")
newFile.mkdirs()
if (Build.VERSION.SDK_INT >= 26 ) {
Files.move(
ff.toPath(),
newFile.toPath(),
StandardCopyOption.REPLACE_EXISTING
)
}else{
ff.renameTo(newFile)
}
fileList.add(newFile.absolutePath)
}
return@forEach
}
if(it.name.contains(".part-Frag")) return@forEach
val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(it.extension) ?: "*/*"
destFile = File(currentDirectory.absolutePath + "/${it.name}")
val dest = Uri.parse(destDir).run {
DocumentsContract.buildDocumentUriUsingTree(
this,
DocumentsContract.getTreeDocumentId(this)
)
}
val destUri = DocumentsContract.createDocument(
context.contentResolver,
dest,
mimeType,
it.name
) ?: return@forEach
val inputStream = it.inputStream()
val outputStream =
context.contentResolver.openOutputStream(destUri) ?: return@forEach
inputStream.copyTo(outputStream)
inputStream.closeQuietly()
outputStream.closeQuietly()
fileList.add(destFile)
}catch (e: java.lang.Exception) {
Log.e("error", e.message.toString())
if (destFile.absolutePath.contains("/storage/emulated/0/Download")
|| destFile.absolutePath.contains("/storage/emulated/0/Documents")
){
if (Build.VERSION.SDK_INT >= 26 ){
Files.move(it.toPath(), destFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
}else{
it.renameTo(destFile)
}
fileList.add(destFile)
}
return@forEach
}catch(e : Exception){
Log.e("error", e.message.toString())
return@forEach
}
if (!keepCache){
originDir.deleteRecursively()
}
return@withContext scanMedia(fileList, context)
}
if (!keepCache){
originDir.deleteRecursively()
}
return scanMedia(fileList, context)
}
private fun scanMedia(files: List<File>, context: Context) : List<String> {
private fun scanMedia(files: List<String>, context: Context) : List<String> {
try {
val paths = files.map { it.absolutePath }.toTypedArray()
MediaScannerConnection.scanFile(context, paths, null, null)
return files.sortedByDescending { it.length() }.map { it.absolutePath }
val paths = files.sortedByDescending { File(it).length() }
MediaScannerConnection.scanFile(context, paths.toTypedArray(), null, null)
return paths
}catch (e: Exception){
e.printStackTrace()
}

View file

@ -68,7 +68,7 @@ class InfoUtil(private val context: Context) {
@Throws(JSONException::class)
fun searchFromPiped(query: String): ArrayList<ResultItem?> {
val data = genericRequest("$pipedURL/search?q=$query&filter=videos")
val data = genericRequest("$pipedURL/search?q=$query&filter=videos&region=${countryCODE}")
val dataArray = data.getJSONArray("items")
if (dataArray.length() == 0) return getFromYTDL(query)
for (i in 0 until dataArray.length()) {
@ -86,7 +86,7 @@ class InfoUtil(private val context: Context) {
@Throws(JSONException::class)
fun searchFromPipedMusic(query: String): ArrayList<ResultItem?> {
val data = genericRequest("$pipedURL/search?q=$query=&filter=music_songs")
val data = genericRequest("$pipedURL/search?q=$query=&filter=music_songs&region=${countryCODE}")
val dataArray = data.getJSONArray("items")
if (dataArray.length() == 0) return getFromYTDL(query)
for (i in 0 until dataArray.length()) {
@ -350,9 +350,44 @@ class InfoUtil(private val context: Context) {
@Throws(JSONException::class)
fun getTrending(): ArrayList<ResultItem?> {
items = ArrayList()
if (sharedPreferences.getString("api_key", "")!!.isNotBlank()){
return getTrendingFromYoutubeAPI()
}
return getTrendingFromPiped()
}
@Throws(JSONException::class)
fun getTrendingFromYoutubeAPI(): ArrayList<ResultItem?> {
val key = sharedPreferences.getString("api_key", "")!!
val url = "https://www.googleapis.com/youtube/v3/videos?part=snippet&chart=mostPopular&videoCategoryId=10&regionCode=$countryCODE&maxResults=25&key=$key"
//short data
val res = genericRequest(url)
//extra data from the same videos
val contentDetails =
genericRequest("https://www.googleapis.com/youtube/v3/videos?part=contentDetails&chart=mostPopular&videoCategoryId=10&regionCode=$countryCODE&maxResults=25&key=$key")
if (!contentDetails.has("items")) return ArrayList()
val dataArray = res.getJSONArray("items")
val extraDataArray = contentDetails.getJSONArray("items")
for (i in 0 until dataArray.length()) {
val element = dataArray.getJSONObject(i)
val snippet = element.getJSONObject("snippet")
var duration = extraDataArray.getJSONObject(i).getJSONObject("contentDetails")
.getString("duration")
duration = formatDuration(duration)
snippet.put("videoID", element.getString("id"))
snippet.put("duration", duration)
fixThumbnail(snippet)
val v = createVideofromJSON(snippet)
if (v == null || v.thumb.isEmpty()) {
continue
}
v.playlistTitle = context.getString(R.string.trendingPlaylist)
items.add(v)
}
return items
}
private fun getTrendingFromPiped(): ArrayList<ResultItem?> {
val url = "$pipedURL/trending?region=$countryCODE"
val res = genericArrayRequest(url)
@ -881,7 +916,10 @@ class InfoUtil(private val context: Context) {
}
fun buildYoutubeDLRequest(downloadItem: DownloadItem) : YoutubeDLRequest{
val tempFileDir = File(context.cacheDir.absolutePath + "/downloads/" + downloadItem.id)
val cacheDir = File(context.cacheDir.absolutePath + "/downloads/")
val tempFileDir = File(cacheDir, downloadItem.id.toString())
tempFileDir.delete()
tempFileDir.mkdirs()
val url = downloadItem.url
val request = YoutubeDLRequest(url)
@ -925,7 +963,12 @@ class InfoUtil(private val context: Context) {
if (sponsorBlockFilters.isNotEmpty()) {
val filters = java.lang.String.join(",", sponsorBlockFilters.filter { it.isNotBlank() })
if (filters.isNotBlank()) request.addOption("--sponsorblock-remove", filters)
if (filters.isNotBlank()) {
request.addOption("--sponsorblock-remove", filters)
if (sharedPreferences.getBoolean("force_keyframes", false)){
request.addOption("--force-keyframes-at-cuts")
}
}
}
if(downloadItem.title.isNotBlank()){
@ -936,7 +979,7 @@ class InfoUtil(private val context: Context) {
}
request.addCommands(listOf("--replace-in-metadata","uploader"," - Topic$",""))
if (downloadItem.customFileNameTemplate.isBlank()) downloadItem.customFileNameTemplate = "%(uploader|${downloadItem.author})s - %(title)s"
downloadItem.customFileNameTemplate.replace("%(uploader)s", "%(uploader|${downloadItem.author})s")
downloadItem.customFileNameTemplate = downloadItem.customFileNameTemplate.replace("%(uploader)s", "%(uploader|${downloadItem.author})s")
if (downloadItem.downloadSections.isNotBlank()){
downloadItem.downloadSections.split(";").forEach {
@ -946,7 +989,7 @@ class InfoUtil(private val context: Context) {
else
request.addOption("--download-sections", it)
if (sharedPreferences.getBoolean("force_keyframes", false)){
if (sharedPreferences.getBoolean("force_keyframes", false) && !request.hasOption("--force-keyframes-at-cuts")){
request.addOption("--force-keyframes-at-cuts")
}
}
@ -1019,7 +1062,9 @@ class InfoUtil(private val context: Context) {
config.writeText(configData)
request.addOption("--ppa", "ThumbnailsConvertor:-qmin 1 -q:v 1")
request.addOption("--config", config.absolutePath)
} catch (ignored: Exception) {}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
request.addOption("--parse-metadata", "%(release_year,upload_date)s:%(meta_date)s")

View file

@ -48,6 +48,7 @@ object ThemeUtil {
"green" -> activity.setTheme(R.style.Theme_Green)
"purple" -> activity.setTheme(R.style.Theme_Purple)
"yellow" -> activity.setTheme(R.style.Theme_Yellow)
"orange" -> activity.setTheme(R.style.Theme_Orange)
"monochrome" -> activity.setTheme(R.style.Theme_Monochrome)
}
@ -71,37 +72,41 @@ object ThemeUtil {
when (sharedPreferences.getString("ytdlnis_theme", "System")!!) {
"System" -> {
//set dynamic icon
val aliasComponentName = ComponentName(activity, "com.deniscerri.ytdlnis.Default")
activity.packageManager.setComponentEnabledSetting(aliasComponentName,
activity.packageManager.setComponentEnabledSetting(
ComponentName(activity.packageName, "com.deniscerri.ytdlnis.Default"),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP)
PackageManager.DONT_KILL_APP
)
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
}
"Light" -> {
//set light icon
val aliasComponentName = ComponentName(activity, "com.deniscerri.ytdlnis.LightIcon")
activity.packageManager.setComponentEnabledSetting(aliasComponentName,
activity.packageManager.setComponentEnabledSetting(
ComponentName(activity.packageName, "com.deniscerri.ytdlnis.LightIcon"),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP)
PackageManager.DONT_KILL_APP
)
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
}
"Dark" -> {
//set dark icon
val aliasComponentName = ComponentName(activity, "com.deniscerri.ytdlnis.DarkIcon")
activity.packageManager.setComponentEnabledSetting(aliasComponentName,
activity.packageManager.setComponentEnabledSetting(
ComponentName(activity.packageName, "com.deniscerri.ytdlnis.DarkIcon"),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP)
PackageManager.DONT_KILL_APP
)
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
}
else -> {
//set dynamic icon
val aliasComponentName = ComponentName(activity, "com.deniscerri.ytdlnis.Default")
activity.packageManager.setComponentEnabledSetting(aliasComponentName,
activity.packageManager.setComponentEnabledSetting(
ComponentName(activity.packageName, "com.deniscerri.ytdlnis.Default"),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP)
PackageManager.DONT_KILL_APP
)
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
}
@ -109,7 +114,7 @@ object ThemeUtil {
}
fun getThemeColor(context: Context, colorCode: Int): Int {
private fun getThemeColor(context: Context, colorCode: Int): Int {
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
val accent = sharedPreferences.getString("theme_accent", "blue")
return if (accent == "Default" || accent == "blue"){

View file

@ -29,6 +29,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import java.io.File
@ -133,44 +134,47 @@ class DownloadWorker(
}.onSuccess {
val wasQuickDownloaded = updateDownloadItem(downloadItem, infoUtil, dao, resultDao)
var finalPaths : List<String>?
//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))
try {
finalPaths = 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))
}
if (finalPaths.isNotEmpty()){
setProgressAsync(workDataOf("progress" to 100, "output" to "Moved file to $downloadLocation", "id" to downloadItem.id, "log" to logDownloads))
}else{
CoroutineScope(Dispatchers.IO).launch {
var finalPaths : List<String>?
//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))
try {
finalPaths = withContext(Dispatchers.IO){
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))
}
}
if (finalPaths.isNotEmpty()){
setProgressAsync(workDataOf("progress" to 100, "output" to "Moved file to $downloadLocation", "id" to downloadItem.id, "log" to logDownloads))
}else{
finalPaths = listOf(context.getString(R.string.unfound_file))
}
}catch (e: Exception){
finalPaths = listOf(context.getString(R.string.unfound_file))
e.printStackTrace()
handler.postDelayed({
Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show()
}, 1000)
}
}catch (e: Exception){
finalPaths = listOf(context.getString(R.string.unfound_file))
e.printStackTrace()
handler.postDelayed({
Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show()
}, 1000)
}
//put download in history
val incognito = sharedPreferences.getBoolean("incognito", false)
if (!incognito) {
val unixtime = System.currentTimeMillis() / 1000
val file = File(finalPaths?.first()!!)
downloadItem.format.filesize = file.length()
val historyItem = HistoryItem(0, downloadItem.url, downloadItem.title, downloadItem.author, downloadItem.duration, downloadItem.thumb, downloadItem.type, unixtime, finalPaths.first() , downloadItem.website, downloadItem.format, downloadItem.id)
runBlocking {
//put download in history
val incognito = sharedPreferences.getBoolean("incognito", false)
if (!incognito) {
val unixtime = System.currentTimeMillis() / 1000
val file = File(finalPaths?.first()!!)
downloadItem.format.filesize = file.length()
val historyItem = HistoryItem(0, downloadItem.url, downloadItem.title, downloadItem.author, downloadItem.duration, downloadItem.thumb, downloadItem.type, unixtime, finalPaths.first() , downloadItem.website, downloadItem.format, downloadItem.id)
historyDao.insert(historyItem)
}
}
notificationUtil.cancelDownloadNotification(downloadItem.id.toInt())
notificationUtil.createDownloadFinished(
downloadItem.title, if (finalPaths?.first().equals(context.getString(R.string.unfound_file))) null else finalPaths,
NotificationUtil.DOWNLOAD_FINISHED_CHANNEL_ID
)
notificationUtil.cancelDownloadNotification(downloadItem.id.toInt())
notificationUtil.createDownloadFinished(
downloadItem.title, if (finalPaths?.first().equals(context.getString(R.string.unfound_file))) null else finalPaths,
NotificationUtil.DOWNLOAD_FINISHED_CHANNEL_ID
)
}
if (wasQuickDownloaded){
runCatching {

View file

@ -116,16 +116,18 @@ class TerminalDownloadWorker(
}
}
}.onSuccess {
//move file from internal to set download directory
try {
FileUtil.moveFile(tempFileDir.absoluteFile,context, downloadLocation!!, false){ p ->
setProgressAsync(workDataOf("progress" to p))
CoroutineScope(Dispatchers.IO).launch {
//move file from internal to set download directory
try {
FileUtil.moveFile(tempFileDir.absoluteFile,context, downloadLocation!!, false){ p ->
setProgressAsync(workDataOf("progress" to p))
}
}catch (e: Exception){
e.printStackTrace()
handler.postDelayed({
Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show()
}, 1000)
}
}catch (e: Exception){
e.printStackTrace()
handler.postDelayed({
Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show()
}, 1000)
}
if (it.out.length > 200){

View file

@ -3,7 +3,7 @@
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="200dp"
android:layout_marginBottom="200dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">

View file

@ -28,23 +28,18 @@
android:label="MainSettingsFragment" >
<action
android:id="@+id/action_mainSettingsFragment_to_appearanceSettingsFragment"
app:destination="@id/appearanceSettingsFragment"
app:enterAnim="@anim/fade_in" />
app:destination="@id/appearanceSettingsFragment" />
<action
android:id="@+id/action_mainSettingsFragment_to_folderSettingsFragment"
app:destination="@id/folderSettingsFragment"
app:enterAnim="@anim/fade_in" />
app:destination="@id/folderSettingsFragment" />
<action
android:id="@+id/action_mainSettingsFragment_to_downloadSettingsFragment"
app:destination="@id/downloadSettingsFragment"
app:enterAnim="@anim/fade_in" />
app:destination="@id/downloadSettingsFragment" />
<action
android:id="@+id/action_mainSettingsFragment_to_processingSettingsFragment"
app:destination="@id/processingSettingsFragment"
app:enterAnim="@anim/fade_in" />
app:destination="@id/processingSettingsFragment" />
<action
android:id="@+id/action_mainSettingsFragment_to_updateSettingsFragment"
app:destination="@id/updateSettingsFragment"
app:enterAnim="@anim/fade_in" />
app:destination="@id/updateSettingsFragment" />
</fragment>
</navigation>

View file

@ -203,6 +203,35 @@
</style>
<style name="Theme.Orange" parent="BaseTheme">
<item name="colorPrimary">@color/orange_md_theme_dark_primary</item>
<item name="colorOnPrimary">@color/orange_md_theme_dark_onPrimary</item>
<item name="colorPrimaryContainer">@color/orange_md_theme_dark_primaryContainer</item>
<item name="colorOnPrimaryContainer">@color/orange_md_theme_dark_onPrimaryContainer</item>
<item name="colorSecondary">@color/orange_md_theme_dark_secondary</item>
<item name="colorOnSecondary">@color/orange_md_theme_dark_onSecondary</item>
<item name="colorSecondaryContainer">@color/orange_md_theme_dark_secondaryContainer</item>
<item name="colorOnSecondaryContainer">@color/orange_md_theme_dark_onSecondaryContainer</item>
<item name="colorTertiary">@color/orange_md_theme_dark_tertiary</item>
<item name="colorOnTertiary">@color/orange_md_theme_dark_onTertiary</item>
<item name="colorTertiaryContainer">@color/orange_md_theme_dark_tertiaryContainer</item>
<item name="colorOnTertiaryContainer">@color/orange_md_theme_dark_onTertiaryContainer</item>
<item name="colorError">@color/orange_md_theme_dark_error</item>
<item name="colorErrorContainer">@color/orange_md_theme_dark_errorContainer</item>
<item name="colorOnError">@color/orange_md_theme_dark_onError</item>
<item name="colorOnErrorContainer">@color/orange_md_theme_dark_onErrorContainer</item>
<item name="android:colorBackground">@color/orange_md_theme_dark_background</item>
<item name="colorOnBackground">@color/orange_md_theme_dark_onBackground</item>
<item name="colorSurface">@color/orange_md_theme_dark_surface</item>
<item name="colorOnSurface">@color/orange_md_theme_dark_onSurface</item>
<item name="colorSurfaceVariant">@color/orange_md_theme_dark_surfaceVariant</item>
<item name="colorOnSurfaceVariant">@color/orange_md_theme_dark_onSurfaceVariant</item>
<item name="colorOutline">@color/orange_md_theme_dark_outline</item>
<item name="colorOnSurfaceInverse">@color/orange_md_theme_dark_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/orange_md_theme_dark_inverseSurface</item>
<item name="colorPrimaryInverse">@color/orange_md_theme_dark_inversePrimary</item>
</style>
<style name="Pure">
<item name="android:colorBackground">@android:color/black</item>

View file

@ -224,6 +224,7 @@
<string name="red">E Kuqe</string>
<string name="purple">Vjollcë</string>
<string name="yellow">E Verdhë</string>
<string name="orange">Portokalli</string>
<string name="monochrome">Monokrom</string>
<string name="green">Jeshile</string>
<string name="downloaded">"U shkarkua: "</string>
@ -262,7 +263,7 @@
<string name="misc">Të Tjera</string>
<string name="audio_only_item">Ky artikull përmban vetëm formate audio</string>
<string name="locale">Lokal</string>
<string name="general">Gjeneral</string>
<string name="general">Të Përgjithshme</string>
<string name="swipe_gestures">Gjestet e rrëshqitjes</string>
<string name="swipe_gestures_summary">Rrëshqitni artikujt për veprime të caktuara</string>
<string name="crop_thumb">Preje Thumbnailin</string>

View file

@ -226,6 +226,7 @@
<item>@string/green</item>
<item>@string/purple</item>
<item>@string/yellow</item>
<item>@string/orange</item>
<item>@string/monochrome</item>
</array>
@ -236,6 +237,7 @@
<item>green</item>
<item>purple</item>
<item>yellow</item>
<item>orange</item>
<item>monochrome</item>
</array>

View file

@ -114,7 +114,7 @@
<color name="red_md_theme_dark_inverseSurface">#EDE0DE</color>
<color name="red_md_theme_dark_inversePrimary">#9F4034</color>
<color name="yellow_md_theme_light_primary">#745B00</color>
<color name="yellow_md_theme_light_primary">#cf9b02</color>
<color name="yellow_md_theme_light_onPrimary">#FFFFFF</color>
<color name="yellow_md_theme_light_primaryContainer">#FFE08D</color>
<color name="yellow_md_theme_light_onPrimaryContainer">#241A00</color>
@ -273,6 +273,68 @@
<color name="purple_md_theme_dark_inverseSurface">#EAE0E3</color>
<color name="purple_md_theme_dark_inversePrimary">#9B3489</color>
<color name="orange_md_theme_light_primary">#855400</color>
<color name="orange_md_theme_light_onPrimary">#FFFFFF</color>
<color name="orange_md_theme_light_primaryContainer">#FFDDB7</color>
<color name="orange_md_theme_light_onPrimaryContainer">#2A1700</color>
<color name="orange_md_theme_light_secondary">#705B41</color>
<color name="orange_md_theme_light_onSecondary">#FFFFFF</color>
<color name="orange_md_theme_light_secondaryContainer">#FCDEBC</color>
<color name="orange_md_theme_light_onSecondaryContainer">#281905</color>
<color name="orange_md_theme_light_tertiary">#53643E</color>
<color name="orange_md_theme_light_onTertiary">#FFFFFF</color>
<color name="orange_md_theme_light_tertiaryContainer">#D6E9B9</color>
<color name="orange_md_theme_light_onTertiaryContainer">#121F03</color>
<color name="orange_md_theme_light_error">#BA1A1A</color>
<color name="orange_md_theme_light_errorContainer">#FFDAD6</color>
<color name="orange_md_theme_light_onError">#FFFFFF</color>
<color name="orange_md_theme_light_onErrorContainer">#410002</color>
<color name="orange_md_theme_light_background">#FFFBFF</color>
<color name="orange_md_theme_light_onBackground">#1F1B16</color>
<color name="orange_md_theme_light_surface">#FFFBFF</color>
<color name="orange_md_theme_light_onSurface">#1F1B16</color>
<color name="orange_md_theme_light_surfaceVariant">#F0E0D0</color>
<color name="orange_md_theme_light_onSurfaceVariant">#504539</color>
<color name="orange_md_theme_light_outline">#827568</color>
<color name="orange_md_theme_light_inverseOnSurface">#F9EFE7</color>
<color name="orange_md_theme_light_inverseSurface">#352F2A</color>
<color name="orange_md_theme_light_inversePrimary">#FFB95C</color>
<color name="orange_md_theme_light_shadow">#000000</color>
<color name="orange_md_theme_light_surfaceTint">#855400</color>
<color name="orange_md_theme_light_outlineVariant">#D4C4B5</color>
<color name="orange_md_theme_light_scrim">#000000</color>
<color name="orange_md_theme_dark_primary">#FFB95C</color>
<color name="orange_md_theme_dark_onPrimary">#462A00</color>
<color name="orange_md_theme_dark_primaryContainer">#653E00</color>
<color name="orange_md_theme_dark_onPrimaryContainer">#FFDDB7</color>
<color name="orange_md_theme_dark_secondary">#DFC2A2</color>
<color name="orange_md_theme_dark_onSecondary">#3F2D17</color>
<color name="orange_md_theme_dark_secondaryContainer">#57432B</color>
<color name="orange_md_theme_dark_onSecondaryContainer">#FCDEBC</color>
<color name="orange_md_theme_dark_tertiary">#BACD9F</color>
<color name="orange_md_theme_dark_onTertiary">#263514</color>
<color name="orange_md_theme_dark_tertiaryContainer">#3C4C28</color>
<color name="orange_md_theme_dark_onTertiaryContainer">#D6E9B9</color>
<color name="orange_md_theme_dark_error">#FFB4AB</color>
<color name="orange_md_theme_dark_errorContainer">#93000A</color>
<color name="orange_md_theme_dark_onError">#690005</color>
<color name="orange_md_theme_dark_onErrorContainer">#FFDAD6</color>
<color name="orange_md_theme_dark_background">#1F1B16</color>
<color name="orange_md_theme_dark_onBackground">#EBE1D9</color>
<color name="orange_md_theme_dark_surface">#1F1B16</color>
<color name="orange_md_theme_dark_onSurface">#EBE1D9</color>
<color name="orange_md_theme_dark_surfaceVariant">#504539</color>
<color name="orange_md_theme_dark_onSurfaceVariant">#D4C4B5</color>
<color name="orange_md_theme_dark_outline">#9C8E80</color>
<color name="orange_md_theme_dark_inverseOnSurface">#1F1B16</color>
<color name="orange_md_theme_dark_inverseSurface">#EBE1D9</color>
<color name="orange_md_theme_dark_inversePrimary">#855400</color>
<color name="orange_md_theme_dark_shadow">#000000</color>
<color name="orange_md_theme_dark_surfaceTint">#FFB95C</color>
<color name="orange_md_theme_dark_outlineVariant">#504539</color>
<color name="orange_md_theme_dark_scrim">#000000</color>
// Colors without comments seem to be not used
<color name="monochrome_theme_light_primary">#616161</color>
// LibreTube Logo, Hyperlinks and on-off switch (on status: filling) (also influence on the

View file

@ -71,7 +71,7 @@
<string name="url">URL</string>
<string name="link_copied_to_clipboard">Link copied to clipboard</string>
<string name="api_key">Use API Key</string>
<string name="api_key_summary">Faster results from YouTube</string>
<string name="api_key_summary">Video Recommendations from Youtube API</string>
<string name="update_app_summary">Announce new versions of the app when it is opened</string>
<string name="downloads">Downloads</string>
<string name="remove_deleted">Remove Deleted</string>
@ -232,6 +232,7 @@
<string name="green">Green</string>
<string name="purple">Purple</string>
<string name="yellow">Yellow</string>
<string name="orange">Orange</string>
<string name="monochrome">Monochrome</string>
<string name="downloaded">"Downloaded: "</string>
<string name="finished_download_notification_channel_name">Finished Downloads</string>

View file

@ -205,6 +205,35 @@
</style>
<style name="Theme.Orange" parent="BaseTheme">
<item name="colorPrimary">@color/orange_md_theme_light_primary</item>
<item name="colorOnPrimary">@color/orange_md_theme_light_onPrimary</item>
<item name="colorPrimaryContainer">@color/orange_md_theme_light_primaryContainer</item>
<item name="colorOnPrimaryContainer">@color/orange_md_theme_light_onPrimaryContainer</item>
<item name="colorSecondary">@color/orange_md_theme_light_secondary</item>
<item name="colorOnSecondary">@color/orange_md_theme_light_onSecondary</item>
<item name="colorSecondaryContainer">@color/orange_md_theme_light_secondaryContainer</item>
<item name="colorOnSecondaryContainer">@color/orange_md_theme_light_onSecondaryContainer</item>
<item name="colorTertiary">@color/orange_md_theme_light_tertiary</item>
<item name="colorOnTertiary">@color/orange_md_theme_light_onTertiary</item>
<item name="colorTertiaryContainer">@color/orange_md_theme_light_tertiaryContainer</item>
<item name="colorOnTertiaryContainer">@color/orange_md_theme_light_onTertiaryContainer</item>
<item name="colorError">@color/orange_md_theme_light_error</item>
<item name="colorErrorContainer">@color/orange_md_theme_light_errorContainer</item>
<item name="colorOnError">@color/orange_md_theme_light_onError</item>
<item name="colorOnErrorContainer">@color/orange_md_theme_light_onErrorContainer</item>
<item name="android:colorBackground">@color/orange_md_theme_light_background</item>
<item name="colorOnBackground">@color/orange_md_theme_light_onBackground</item>
<item name="colorSurface">@color/orange_md_theme_light_surface</item>
<item name="colorOnSurface">@color/orange_md_theme_light_onSurface</item>
<item name="colorSurfaceVariant">@color/orange_md_theme_light_surfaceVariant</item>
<item name="colorOnSurfaceVariant">@color/orange_md_theme_light_onSurfaceVariant</item>
<item name="colorOutline">@color/orange_md_theme_light_outline</item>
<item name="colorOnSurfaceInverse">@color/orange_md_theme_light_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/orange_md_theme_light_inverseSurface</item>
<item name="colorPrimaryInverse">@color/orange_md_theme_light_inversePrimary</item>
</style>
<style name="Pure">
<item name="android:colorBackground">@android:color/white</item>

View file

@ -35,14 +35,6 @@
app:summary="@string/download_over_metered_networks_summary"
app:title="@string/download_over_metered_networks" />
<MultiSelectListPreference
app:icon="@drawable/baseline_hide_image_24"
app:dialogTitle="@string/hide_thumbnails"
app:entries="@array/hide_thumbnail"
app:entryValues="@array/hide_thumbnail_values"
app:key="hide_thumbnails"
app:title="@string/hide_thumbnails" />
<EditTextPreference
android:icon="@drawable/baseline_network_locked_24"

View file

@ -59,6 +59,12 @@
app:summary="@string/video_recommendations_summary"
app:title="@string/video_recommendations" />
<EditTextPreference
android:icon="@drawable/ic_key"
app:key="api_key"
android:summary="@string/api_key_summary"
app:title="@string/api_key" />
<ListPreference
android:defaultValue=""
@ -106,7 +112,7 @@
<SwitchPreferenceCompat
android:widgetLayout="@layout/preferece_material_switch"
app:defaultValue="false"
app:defaultValue="true"
android:icon="@drawable/baseline_share_24"
android:key="hide_terminal"
app:title="@string/hide_terminal" />

View file

@ -43,6 +43,16 @@
app:defaultValue="all"
app:title="@string/subtitle_languages" />
<ListPreference
android:defaultValue="srt"
android:entries="@array/sub_formats"
android:entryValues="@array/sub_formats"
android:icon="@drawable/baseline_format_quote_24"
app:key="sub_format"
app:useSimpleSummaryProvider="true"
app:title="@string/subtitle_format" />
<SwitchPreferenceCompat
android:widgetLayout="@layout/preferece_material_switch"
app:defaultValue="true"
@ -60,6 +70,7 @@
app:summary="@string/crop_thumb_summary"
app:title="@string/crop_thumb" />
<SwitchPreferenceCompat
android:widgetLayout="@layout/preferece_material_switch"
app:defaultValue="false"
@ -68,22 +79,6 @@
app:summary="@string/force_keyframes_summary"
app:title="@string/force_keyframes" />
<ListPreference
android:defaultValue="srt"
android:entries="@array/sub_formats"
android:entryValues="@array/sub_formats"
android:icon="@drawable/baseline_format_quote_24"
app:key="sub_format"
app:useSimpleSummaryProvider="true"
app:title="@string/subtitle_format" />
<SwitchPreferenceCompat
android:widgetLayout="@layout/preferece_material_switch"
app:defaultValue="true"
app:icon="@drawable/ic_chapters"
app:key="add_chapters"
app:summary="@string/add_chapters_summary"
app:title="@string/add_chapters" />
<SwitchPreferenceCompat
android:widgetLayout="@layout/preferece_material_switch"
@ -93,6 +88,15 @@
app:summary="@string/save_thumb_summary"
app:title="@string/save_thumb" />
<SwitchPreferenceCompat
android:widgetLayout="@layout/preferece_material_switch"
app:defaultValue="true"
app:icon="@drawable/ic_chapters"
app:key="add_chapters"
app:summary="@string/add_chapters_summary"
app:title="@string/add_chapters" />
<ListPreference
android:defaultValue="Default"
android:entries="@array/audio_containers"