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:
parent
bb282424a8
commit
4227b1e43c
23 changed files with 448 additions and 226 deletions
|
|
@ -10,7 +10,7 @@ def properties = new Properties()
|
||||||
def versionMajor = 1
|
def versionMajor = 1
|
||||||
def versionMinor = 6
|
def versionMinor = 6
|
||||||
def versionPatch = 3
|
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 = ""
|
def versionExt = ""
|
||||||
|
|
||||||
if (versionBuild > 0){
|
if (versionBuild > 0){
|
||||||
|
|
@ -206,4 +206,6 @@ dependencies {
|
||||||
// For RTSP playback support with ExoPlayer
|
// For RTSP playback support with ExoPlayer
|
||||||
implementation("androidx.media3:media3-exoplayer-rtsp:$media3_version")
|
implementation("androidx.media3:media3-exoplayer-rtsp:$media3_version")
|
||||||
implementation("androidx.media3:media3-datasource-cronet:$media3_version")
|
implementation("androidx.media3:media3-datasource-cronet:$media3_version")
|
||||||
|
|
||||||
|
implementation "com.anggrayudi:storage:1.5.5"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,6 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
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.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
|
@ -19,7 +12,7 @@
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||||
<uses-permission android:name="android.permission.ACTION_OPEN_DOCUMENT" />
|
<uses-permission android:name="android.permission.ACTION_OPEN_DOCUMENT" />
|
||||||
<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" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -38,8 +31,7 @@
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/BaseTheme"
|
android:theme="@style/BaseTheme"
|
||||||
tools:ignore="DataExtractionRules"
|
tools:ignore="DataExtractionRules"
|
||||||
tools:targetApi="tiramisu"
|
tools:targetApi="tiramisu">
|
||||||
android:banner="@mipmap/ic_launcher">
|
|
||||||
<profileable
|
<profileable
|
||||||
android:shell="true"
|
android:shell="true"
|
||||||
tools:targetApi="29" />
|
tools:targetApi="29" />
|
||||||
|
|
@ -49,18 +41,7 @@
|
||||||
android:configChanges="smallestScreenSize|layoutDirection|locale|orientation|screenSize"
|
android:configChanges="smallestScreenSize|layoutDirection|locale|orientation|screenSize"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:windowSoftInputMode="adjustPan">
|
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>
|
||||||
<activity
|
<activity
|
||||||
android:name=".receiver.ShareActivity"
|
android:name=".receiver.ShareActivity"
|
||||||
|
|
@ -114,7 +95,7 @@
|
||||||
<activity-alias
|
<activity-alias
|
||||||
android:name=".terminalShareAlias"
|
android:name=".terminalShareAlias"
|
||||||
android:targetActivity=".ui.more.TerminalActivity"
|
android:targetActivity=".ui.more.TerminalActivity"
|
||||||
android:enabled="true"
|
android:enabled="false"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND" />
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
|
@ -126,17 +107,20 @@
|
||||||
<activity-alias
|
<activity-alias
|
||||||
android:name=".Default"
|
android:name=".Default"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="true"
|
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:configChanges="smallestScreenSize|layoutDirection|locale|orientation|screenSize"
|
android:configChanges="smallestScreenSize|layoutDirection|locale|orientation|screenSize"
|
||||||
android:targetActivity=".MainActivity"
|
android:targetActivity=".MainActivity"
|
||||||
android:windowSoftInputMode="adjustPan">
|
android:windowSoftInputMode="adjustPan"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<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>
|
</intent-filter>
|
||||||
</activity-alias>
|
</activity-alias>
|
||||||
|
|
||||||
|
|
@ -152,9 +136,12 @@
|
||||||
android:windowSoftInputMode="adjustPan">
|
android:windowSoftInputMode="adjustPan">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<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>
|
</intent-filter>
|
||||||
</activity-alias>
|
</activity-alias>
|
||||||
|
|
||||||
|
|
@ -169,9 +156,12 @@
|
||||||
android:windowSoftInputMode="adjustPan">
|
android:windowSoftInputMode="adjustPan">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<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>
|
</intent-filter>
|
||||||
</activity-alias>
|
</activity-alias>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,8 +71,12 @@ class CookieViewModel(private val application: Application) : AndroidViewModel(a
|
||||||
if (!hasCookies()) throw Exception("There is no cookies in the database!")
|
if (!hasCookies()) throw Exception("There is no cookies in the database!")
|
||||||
flush()
|
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(
|
SQLiteDatabase.openDatabase(
|
||||||
"/data/data/com.deniscerri.ytdl/app_webview/Default/Cookies", null, OPEN_READONLY
|
dbPath, null, OPEN_READONLY
|
||||||
).run {
|
).run {
|
||||||
val projection = arrayOf(
|
val projection = arrayOf(
|
||||||
CookieObject.HOST,
|
CookieObject.HOST,
|
||||||
|
|
|
||||||
|
|
@ -68,8 +68,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
private var defaultVideoFormats : MutableList<Format>
|
private var defaultVideoFormats : MutableList<Format>
|
||||||
|
|
||||||
private val videoQualityPreference: String
|
private val videoQualityPreference: String
|
||||||
private val formatIDPreference: String
|
private val formatIDPreference: List<String>
|
||||||
private val audioFormatIDPreference: String
|
private val audioFormatIDPreference: List<String>
|
||||||
private val resources : Resources
|
private val resources : Resources
|
||||||
enum class Type {
|
enum class Type {
|
||||||
audio, video, command
|
audio, video, command
|
||||||
|
|
@ -92,8 +92,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
erroredDownloads = repository.erroredDownloads.asLiveData()
|
erroredDownloads = repository.erroredDownloads.asLiveData()
|
||||||
|
|
||||||
videoQualityPreference = sharedPreferences.getString("video_quality", application.getString(R.string.best_quality)).toString()
|
videoQualityPreference = sharedPreferences.getString("video_quality", application.getString(R.string.best_quality)).toString()
|
||||||
formatIDPreference = sharedPreferences.getString("format_id", "").toString()
|
formatIDPreference = sharedPreferences.getString("format_id", "").toString().split(",")
|
||||||
audioFormatIDPreference = sharedPreferences.getString("format_id_audio", "").toString()
|
audioFormatIDPreference = sharedPreferences.getString("format_id_audio", "").toString().split(",")
|
||||||
|
|
||||||
val confTmp = Configuration(application.resources.configuration)
|
val confTmp = Configuration(application.resources.configuration)
|
||||||
confTmp.locale = Locale(sharedPreferences.getString("app_language", "en")!!)
|
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 sponsorblock = sharedPreferences.getStringSet("sponsorblock_filters", emptySet())
|
||||||
|
|
||||||
val audioPreferences = AudioPreferences(embedThumb, false, ArrayList(sponsorblock!!))
|
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,
|
return DownloadItem(0,
|
||||||
resultItem.url,
|
resultItem.url,
|
||||||
|
|
@ -317,7 +319,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
return cloneFormat (
|
return cloneFormat (
|
||||||
try {
|
try {
|
||||||
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){
|
}catch (e: Exception){
|
||||||
formats.last { it.format_note.contains("audio", ignoreCase = true) }
|
formats.last { it.format_note.contains("audio", ignoreCase = true) }
|
||||||
}
|
}
|
||||||
|
|
@ -332,7 +334,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
try {
|
try {
|
||||||
val theFormats = formats.ifEmpty { defaultVideoFormats }
|
val theFormats = formats.ifEmpty { defaultVideoFormats }
|
||||||
try {
|
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){
|
}catch (e: Exception){
|
||||||
when (videoQualityPreference) {
|
when (videoQualityPreference) {
|
||||||
"worst" -> {
|
"worst" -> {
|
||||||
|
|
@ -397,7 +399,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
|
|
||||||
fun turnResultItemsToDownloadItems(items: List<ResultItem?>) : List<DownloadItem> {
|
fun turnResultItemsToDownloadItems(items: List<ResultItem?>) : List<DownloadItem> {
|
||||||
val list : MutableList<DownloadItem> = mutableListOf()
|
val list : MutableList<DownloadItem> = mutableListOf()
|
||||||
val preferredType = sharedPreferences.getString("preferred_download_type", "video");
|
val preferredType = sharedPreferences.getString("preferred_download_type", "video")
|
||||||
items.forEach {
|
items.forEach {
|
||||||
list.add(createDownloadItemFromResult(it!!, Type.valueOf(preferredType!!)))
|
list.add(createDownloadItemFromResult(it!!, Type.valueOf(preferredType!!)))
|
||||||
}
|
}
|
||||||
|
|
@ -454,12 +456,9 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
val activeAndQueuedDownloads = repository.getActiveAndQueuedDownloads()
|
val activeAndQueuedDownloads = repository.getActiveAndQueuedDownloads()
|
||||||
val allowMeteredNetworks = sharedPreferences.getBoolean("metered_networks", true)
|
val allowMeteredNetworks = sharedPreferences.getBoolean("metered_networks", true)
|
||||||
val queuedItems = mutableListOf<DownloadItem>()
|
val queuedItems = mutableListOf<DownloadItem>()
|
||||||
var lastDownloadId = repository.getLastDownloadId()
|
|
||||||
|
|
||||||
var exists = false
|
var exists = false
|
||||||
|
|
||||||
items.forEach {
|
items.forEach {
|
||||||
lastDownloadId++
|
|
||||||
it.status = DownloadRepository.Status.Queued.toString()
|
it.status = DownloadRepository.Status.Queued.toString()
|
||||||
if (activeAndQueuedDownloads.firstOrNull{d ->
|
if (activeAndQueuedDownloads.firstOrNull{d ->
|
||||||
d.id = 0
|
d.id = 0
|
||||||
|
|
@ -469,7 +468,6 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
exists = true
|
exists = true
|
||||||
}else{
|
}else{
|
||||||
if (it.id == 0L){
|
if (it.id == 0L){
|
||||||
it.id = lastDownloadId
|
|
||||||
val insert = async {repository.insert(it)}
|
val insert = async {repository.insert(it)}
|
||||||
val id = insert.await()
|
val id = insert.await()
|
||||||
it.id = id
|
it.id = id
|
||||||
|
|
@ -492,7 +490,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
var delay = if (it.downloadStartTime != 0L){
|
var delay = if (it.downloadStartTime != 0L){
|
||||||
it.downloadStartTime - currentTime
|
it.downloadStartTime - currentTime
|
||||||
} else 0
|
} else 0
|
||||||
if (delay < 0L) delay = 0L
|
if (delay < 0L) delay = 1L
|
||||||
|
|
||||||
val workConstraints = Constraints.Builder()
|
val workConstraints = Constraints.Builder()
|
||||||
if (!allowMeteredNetworks) workConstraints.setRequiredNetworkType(NetworkType.UNMETERED)
|
if (!allowMeteredNetworks) workConstraints.setRequiredNetworkType(NetworkType.UNMETERED)
|
||||||
|
|
@ -501,7 +499,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
.setInputData(Data.Builder().putLong("id", it.id).build())
|
.setInputData(Data.Builder().putLong("id", it.id).build())
|
||||||
.addTag("download")
|
.addTag("download")
|
||||||
.setConstraints(workConstraints.build())
|
.setConstraints(workConstraints.build())
|
||||||
.setInitialDelay(delay, TimeUnit.MILLISECONDS)
|
.setInitialDelay(delay, TimeUnit.SECONDS)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
WorkManager.getInstance(context).beginUniqueWork(
|
WorkManager.getInstance(context).beginUniqueWork(
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
|
||||||
}
|
}
|
||||||
private fun getQueryType(inputQuery: String) : String {
|
private fun getQueryType(inputQuery: String) : String {
|
||||||
var type = "Search"
|
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)
|
val m = p.matcher(inputQuery)
|
||||||
if (m.find()) {
|
if (m.find()) {
|
||||||
type = "YT_Video"
|
type = "YT_Video"
|
||||||
|
|
|
||||||
|
|
@ -140,6 +140,9 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
if (chosenFormats.isEmpty()) throw Exception()
|
if (chosenFormats.isEmpty()) throw Exception()
|
||||||
|
|
||||||
|
formats = listOf(res)
|
||||||
|
|
||||||
//playlist format filtering
|
//playlist format filtering
|
||||||
}else{
|
}else{
|
||||||
var progress = "0/${items.size}"
|
var progress = "0/${items.size}"
|
||||||
|
|
@ -154,6 +157,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
||||||
formatCollection.add(it)
|
formatCollection.add(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
formats = formatCollection
|
||||||
val flatFormatCollection = formatCollection.flatten()
|
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 }
|
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()}
|
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
|
//simple video format selection
|
||||||
if (items.size == 1){
|
if (items.size == 1){
|
||||||
listener.onFormatClick(List(items.size){chosenFormats}, listOf(FormatTuple(selectedVideo, selectedAudios)))
|
listener.onFormatClick(formats, listOf(FormatTuple(selectedVideo, selectedAudios)))
|
||||||
}else{
|
}else{
|
||||||
//playlist format selection
|
//playlist format selection
|
||||||
val selectedFormats = mutableListOf<Format>()
|
val selectedFormats = mutableListOf<Format>()
|
||||||
|
|
@ -208,7 +212,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
||||||
selectedFormats.add(selectedVideo)
|
selectedFormats.add(selectedVideo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
listener.onFormatClick(formatCollection, selectedFormats.map { FormatTuple(it, selectedAudios) })
|
listener.onFormatClick(formats, selectedFormats.map { FormatTuple(it, selectedAudios) })
|
||||||
}
|
}
|
||||||
|
|
||||||
dismiss()
|
dismiss()
|
||||||
|
|
@ -260,7 +264,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
||||||
val clickedCard = (clickedformat as MaterialCardView)
|
val clickedCard = (clickedformat as MaterialCardView)
|
||||||
if (format.vcodec.isNotBlank() && format.vcodec != "none") {
|
if (format.vcodec.isNotBlank() && format.vcodec != "none") {
|
||||||
if (clickedCard.isChecked) {
|
if (clickedCard.isChecked) {
|
||||||
listener.onFormatClick(List(items.size){finalFormats}, listOf(FormatTuple(format, null)))
|
listener.onFormatClick(formats, listOf(FormatTuple(format, null)))
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
videoFormatList.forEach { (it as MaterialCardView).isChecked = false }
|
videoFormatList.forEach { (it as MaterialCardView).isChecked = false }
|
||||||
|
|
@ -279,7 +283,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
if (items.size == 1){
|
if (items.size == 1){
|
||||||
listener.onFormatClick(List(items.size){finalFormats}, listOf(FormatTuple(format, null)))
|
listener.onFormatClick(formats, listOf(FormatTuple(format, null)))
|
||||||
}else{
|
}else{
|
||||||
val selectedFormats = mutableListOf<Format>()
|
val selectedFormats = mutableListOf<Format>()
|
||||||
formatCollection.forEach {
|
formatCollection.forEach {
|
||||||
|
|
@ -290,7 +294,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
||||||
selectedFormats.add(format)
|
selectedFormats.add(format)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
listener.onFormatClick(formatCollection, selectedFormats.map { FormatTuple(it, null) })
|
listener.onFormatClick(formats, selectedFormats.map { FormatTuple(it, null) })
|
||||||
}
|
}
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -447,10 +447,15 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
|
||||||
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), DownloadItem::class.java)
|
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), DownloadItem::class.java)
|
||||||
item.id = 0L
|
item.id = 0L
|
||||||
cancelled.add(item)
|
cancelled.add(item)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelled.asReversed().forEach { f ->
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO){
|
||||||
downloadViewModel.insert(item)
|
downloadViewModel.insert(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(cancelled.isNotEmpty()){
|
if(cancelled.isNotEmpty()){
|
||||||
finalMessage.append("${getString(R.string.cancelled)}: ${cancelled.count()}\n")
|
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)
|
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), DownloadItem::class.java)
|
||||||
item.id = 0L
|
item.id = 0L
|
||||||
errored.add(item)
|
errored.add(item)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
errored.asReversed().forEach { f ->
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO){
|
||||||
downloadViewModel.insert(item)
|
downloadViewModel.insert(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(errored.isNotEmpty()){
|
if(errored.isNotEmpty()){
|
||||||
finalMessage.append("${getString(R.string.errored)}: ${errored.count()}\n")
|
finalMessage.append("${getString(R.string.errored)}: ${errored.count()}\n")
|
||||||
}
|
}
|
||||||
|
|
@ -491,13 +501,20 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
|
||||||
//command template restore
|
//command template restore
|
||||||
if(json.has("templates")){
|
if(json.has("templates")){
|
||||||
val items = json.getAsJsonArray("templates")
|
val items = json.getAsJsonArray("templates")
|
||||||
|
val templates = mutableListOf<CommandTemplate>()
|
||||||
items.forEach {
|
items.forEach {
|
||||||
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), CommandTemplate::class.java)
|
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), CommandTemplate::class.java)
|
||||||
item.id = 0L
|
item.id = 0L
|
||||||
|
templates.add(item)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
templates.asReversed().forEach { t ->
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO){
|
||||||
commandTemplateViewModel.insert(item)
|
commandTemplateViewModel.insert(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(items.count() > 0){
|
if(items.count() > 0){
|
||||||
finalMessage.append("${getString(R.string.command_templates)}: ${items.count()}\n")
|
finalMessage.append("${getString(R.string.command_templates)}: ${items.count()}\n")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,21 @@ package com.deniscerri.ytdlnis.util
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.media.MediaScannerConnection
|
import android.media.MediaScannerConnection
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.provider.DocumentsContract
|
|
||||||
import android.util.Log
|
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.R
|
||||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
import kotlinx.coroutines.Dispatchers
|
||||||
import okhttp3.internal.closeQuietly
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
|
|
@ -70,81 +76,105 @@ object FileUtil {
|
||||||
|
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun moveFile(originDir: File, context: Context, destDir: String, keepCache: Boolean, progress: (p: Int) -> Unit) : List<String> {
|
suspend fun moveFile(originDir: File, context: Context, destDir: String, keepCache: Boolean, progress: (p: Int) -> Unit) : List<String> {
|
||||||
val fileList = mutableListOf<File>()
|
return withContext(Dispatchers.Main){
|
||||||
val dir = File(formatPath(destDir))
|
val fileList = mutableListOf<String>()
|
||||||
if (!dir.exists()) dir.mkdirs()
|
val dir = File(formatPath(destDir))
|
||||||
var currentDirectory = dir
|
if (!dir.exists()) dir.mkdirs()
|
||||||
originDir.walk().forEach {
|
originDir.walk().forEach {
|
||||||
var destFile = File(dir.absolutePath + "/${it.absolutePath.removePrefix(originDir.absolutePath)}")
|
if (it.isDirectory && it.absolutePath == originDir.absolutePath) return@forEach
|
||||||
if (it.isDirectory) {
|
var destFile: DocumentFile
|
||||||
destFile.mkdirs()
|
try {
|
||||||
currentDirectory = destFile
|
if (it.name.matches("(^config.*.\\.txt\$)|(rList)|(part-Frag)".toRegex())){
|
||||||
return@forEach
|
it.delete()
|
||||||
}
|
return@forEach
|
||||||
try {
|
}
|
||||||
if (it.name.matches("(^config.*.\\.txt\$)|(rList)|(part-Frag)".toRegex())){
|
|
||||||
it.delete()
|
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
|
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 {
|
try {
|
||||||
val paths = files.map { it.absolutePath }.toTypedArray()
|
val paths = files.sortedByDescending { File(it).length() }
|
||||||
MediaScannerConnection.scanFile(context, paths, null, null)
|
MediaScannerConnection.scanFile(context, paths.toTypedArray(), null, null)
|
||||||
return files.sortedByDescending { it.length() }.map { it.absolutePath }
|
return paths
|
||||||
}catch (e: Exception){
|
}catch (e: Exception){
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ class InfoUtil(private val context: Context) {
|
||||||
|
|
||||||
@Throws(JSONException::class)
|
@Throws(JSONException::class)
|
||||||
fun searchFromPiped(query: String): ArrayList<ResultItem?> {
|
fun searchFromPiped(query: String): ArrayList<ResultItem?> {
|
||||||
val data = genericRequest("$pipedURL/search?q=$query&filter=videos")
|
val data = genericRequest("$pipedURL/search?q=$query&filter=videos®ion=${countryCODE}")
|
||||||
val dataArray = data.getJSONArray("items")
|
val dataArray = data.getJSONArray("items")
|
||||||
if (dataArray.length() == 0) return getFromYTDL(query)
|
if (dataArray.length() == 0) return getFromYTDL(query)
|
||||||
for (i in 0 until dataArray.length()) {
|
for (i in 0 until dataArray.length()) {
|
||||||
|
|
@ -86,7 +86,7 @@ class InfoUtil(private val context: Context) {
|
||||||
|
|
||||||
@Throws(JSONException::class)
|
@Throws(JSONException::class)
|
||||||
fun searchFromPipedMusic(query: String): ArrayList<ResultItem?> {
|
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®ion=${countryCODE}")
|
||||||
val dataArray = data.getJSONArray("items")
|
val dataArray = data.getJSONArray("items")
|
||||||
if (dataArray.length() == 0) return getFromYTDL(query)
|
if (dataArray.length() == 0) return getFromYTDL(query)
|
||||||
for (i in 0 until dataArray.length()) {
|
for (i in 0 until dataArray.length()) {
|
||||||
|
|
@ -350,9 +350,44 @@ class InfoUtil(private val context: Context) {
|
||||||
@Throws(JSONException::class)
|
@Throws(JSONException::class)
|
||||||
fun getTrending(): ArrayList<ResultItem?> {
|
fun getTrending(): ArrayList<ResultItem?> {
|
||||||
items = ArrayList()
|
items = ArrayList()
|
||||||
|
if (sharedPreferences.getString("api_key", "")!!.isNotBlank()){
|
||||||
|
return getTrendingFromYoutubeAPI()
|
||||||
|
}
|
||||||
return getTrendingFromPiped()
|
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®ionCode=$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®ionCode=$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?> {
|
private fun getTrendingFromPiped(): ArrayList<ResultItem?> {
|
||||||
val url = "$pipedURL/trending?region=$countryCODE"
|
val url = "$pipedURL/trending?region=$countryCODE"
|
||||||
val res = genericArrayRequest(url)
|
val res = genericArrayRequest(url)
|
||||||
|
|
@ -881,7 +916,10 @@ class InfoUtil(private val context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun buildYoutubeDLRequest(downloadItem: DownloadItem) : YoutubeDLRequest{
|
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 url = downloadItem.url
|
||||||
val request = YoutubeDLRequest(url)
|
val request = YoutubeDLRequest(url)
|
||||||
|
|
@ -925,7 +963,12 @@ class InfoUtil(private val context: Context) {
|
||||||
|
|
||||||
if (sponsorBlockFilters.isNotEmpty()) {
|
if (sponsorBlockFilters.isNotEmpty()) {
|
||||||
val filters = java.lang.String.join(",", sponsorBlockFilters.filter { it.isNotBlank() })
|
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()){
|
if(downloadItem.title.isNotBlank()){
|
||||||
|
|
@ -936,7 +979,7 @@ class InfoUtil(private val context: Context) {
|
||||||
}
|
}
|
||||||
request.addCommands(listOf("--replace-in-metadata","uploader"," - Topic$",""))
|
request.addCommands(listOf("--replace-in-metadata","uploader"," - Topic$",""))
|
||||||
if (downloadItem.customFileNameTemplate.isBlank()) downloadItem.customFileNameTemplate = "%(uploader|${downloadItem.author})s - %(title)s"
|
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()){
|
if (downloadItem.downloadSections.isNotBlank()){
|
||||||
downloadItem.downloadSections.split(";").forEach {
|
downloadItem.downloadSections.split(";").forEach {
|
||||||
|
|
@ -946,7 +989,7 @@ class InfoUtil(private val context: Context) {
|
||||||
else
|
else
|
||||||
request.addOption("--download-sections", it)
|
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")
|
request.addOption("--force-keyframes-at-cuts")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1019,7 +1062,9 @@ class InfoUtil(private val context: Context) {
|
||||||
config.writeText(configData)
|
config.writeText(configData)
|
||||||
request.addOption("--ppa", "ThumbnailsConvertor:-qmin 1 -q:v 1")
|
request.addOption("--ppa", "ThumbnailsConvertor:-qmin 1 -q:v 1")
|
||||||
request.addOption("--config", config.absolutePath)
|
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")
|
request.addOption("--parse-metadata", "%(release_year,upload_date)s:%(meta_date)s")
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ object ThemeUtil {
|
||||||
"green" -> activity.setTheme(R.style.Theme_Green)
|
"green" -> activity.setTheme(R.style.Theme_Green)
|
||||||
"purple" -> activity.setTheme(R.style.Theme_Purple)
|
"purple" -> activity.setTheme(R.style.Theme_Purple)
|
||||||
"yellow" -> activity.setTheme(R.style.Theme_Yellow)
|
"yellow" -> activity.setTheme(R.style.Theme_Yellow)
|
||||||
|
"orange" -> activity.setTheme(R.style.Theme_Orange)
|
||||||
"monochrome" -> activity.setTheme(R.style.Theme_Monochrome)
|
"monochrome" -> activity.setTheme(R.style.Theme_Monochrome)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,37 +72,41 @@ object ThemeUtil {
|
||||||
when (sharedPreferences.getString("ytdlnis_theme", "System")!!) {
|
when (sharedPreferences.getString("ytdlnis_theme", "System")!!) {
|
||||||
"System" -> {
|
"System" -> {
|
||||||
//set dynamic icon
|
//set dynamic icon
|
||||||
val aliasComponentName = ComponentName(activity, "com.deniscerri.ytdlnis.Default")
|
activity.packageManager.setComponentEnabledSetting(
|
||||||
activity.packageManager.setComponentEnabledSetting(aliasComponentName,
|
ComponentName(activity.packageName, "com.deniscerri.ytdlnis.Default"),
|
||||||
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
|
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
|
||||||
PackageManager.DONT_KILL_APP)
|
PackageManager.DONT_KILL_APP
|
||||||
|
)
|
||||||
|
|
||||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||||
}
|
}
|
||||||
"Light" -> {
|
"Light" -> {
|
||||||
//set light icon
|
//set light icon
|
||||||
val aliasComponentName = ComponentName(activity, "com.deniscerri.ytdlnis.LightIcon")
|
activity.packageManager.setComponentEnabledSetting(
|
||||||
activity.packageManager.setComponentEnabledSetting(aliasComponentName,
|
ComponentName(activity.packageName, "com.deniscerri.ytdlnis.LightIcon"),
|
||||||
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
|
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
|
||||||
PackageManager.DONT_KILL_APP)
|
PackageManager.DONT_KILL_APP
|
||||||
|
)
|
||||||
|
|
||||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
|
||||||
}
|
}
|
||||||
"Dark" -> {
|
"Dark" -> {
|
||||||
//set dark icon
|
//set dark icon
|
||||||
val aliasComponentName = ComponentName(activity, "com.deniscerri.ytdlnis.DarkIcon")
|
activity.packageManager.setComponentEnabledSetting(
|
||||||
activity.packageManager.setComponentEnabledSetting(aliasComponentName,
|
ComponentName(activity.packageName, "com.deniscerri.ytdlnis.DarkIcon"),
|
||||||
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
|
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
|
||||||
PackageManager.DONT_KILL_APP)
|
PackageManager.DONT_KILL_APP
|
||||||
|
)
|
||||||
|
|
||||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
//set dynamic icon
|
//set dynamic icon
|
||||||
val aliasComponentName = ComponentName(activity, "com.deniscerri.ytdlnis.Default")
|
activity.packageManager.setComponentEnabledSetting(
|
||||||
activity.packageManager.setComponentEnabledSetting(aliasComponentName,
|
ComponentName(activity.packageName, "com.deniscerri.ytdlnis.Default"),
|
||||||
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
|
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
|
||||||
PackageManager.DONT_KILL_APP)
|
PackageManager.DONT_KILL_APP
|
||||||
|
)
|
||||||
|
|
||||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
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 sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
val accent = sharedPreferences.getString("theme_accent", "blue")
|
val accent = sharedPreferences.getString("theme_accent", "blue")
|
||||||
return if (accent == "Default" || accent == "blue"){
|
return if (accent == "Default" || accent == "blue"){
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -133,44 +134,47 @@ class DownloadWorker(
|
||||||
}.onSuccess {
|
}.onSuccess {
|
||||||
val wasQuickDownloaded = updateDownloadItem(downloadItem, infoUtil, dao, resultDao)
|
val wasQuickDownloaded = updateDownloadItem(downloadItem, infoUtil, dao, resultDao)
|
||||||
|
|
||||||
var finalPaths : List<String>?
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
//move file from internal to set download directory
|
var finalPaths : List<String>?
|
||||||
setProgressAsync(workDataOf("progress" to 100, "output" to "Moving file to ${FileUtil.formatPath(downloadLocation)}", "id" to downloadItem.id, "log" to logDownloads))
|
//move file from internal to set download directory
|
||||||
try {
|
setProgressAsync(workDataOf("progress" to 100, "output" to "Moving file to ${FileUtil.formatPath(downloadLocation)}", "id" to downloadItem.id, "log" to logDownloads))
|
||||||
finalPaths = FileUtil.moveFile(tempFileDir.absoluteFile,context, downloadLocation, keepCache){ p ->
|
try {
|
||||||
setProgressAsync(workDataOf("progress" to p, "output" to "Moving file to ${FileUtil.formatPath(downloadLocation)}", "id" to downloadItem.id, "log" to logDownloads))
|
finalPaths = withContext(Dispatchers.IO){
|
||||||
}
|
FileUtil.moveFile(tempFileDir.absoluteFile,context, downloadLocation, keepCache){ p ->
|
||||||
if (finalPaths.isNotEmpty()){
|
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 $downloadLocation", "id" to downloadItem.id, "log" to logDownloads))
|
}
|
||||||
}else{
|
}
|
||||||
|
|
||||||
|
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))
|
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
|
//put download in history
|
||||||
val incognito = sharedPreferences.getBoolean("incognito", false)
|
val incognito = sharedPreferences.getBoolean("incognito", false)
|
||||||
if (!incognito) {
|
if (!incognito) {
|
||||||
val unixtime = System.currentTimeMillis() / 1000
|
val unixtime = System.currentTimeMillis() / 1000
|
||||||
val file = File(finalPaths?.first()!!)
|
val file = File(finalPaths?.first()!!)
|
||||||
downloadItem.format.filesize = file.length()
|
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)
|
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 {
|
|
||||||
historyDao.insert(historyItem)
|
historyDao.insert(historyItem)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
notificationUtil.cancelDownloadNotification(downloadItem.id.toInt())
|
|
||||||
|
|
||||||
notificationUtil.createDownloadFinished(
|
notificationUtil.cancelDownloadNotification(downloadItem.id.toInt())
|
||||||
downloadItem.title, if (finalPaths?.first().equals(context.getString(R.string.unfound_file))) null else finalPaths,
|
notificationUtil.createDownloadFinished(
|
||||||
NotificationUtil.DOWNLOAD_FINISHED_CHANNEL_ID
|
downloadItem.title, if (finalPaths?.first().equals(context.getString(R.string.unfound_file))) null else finalPaths,
|
||||||
)
|
NotificationUtil.DOWNLOAD_FINISHED_CHANNEL_ID
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (wasQuickDownloaded){
|
if (wasQuickDownloaded){
|
||||||
runCatching {
|
runCatching {
|
||||||
|
|
|
||||||
|
|
@ -116,16 +116,18 @@ class TerminalDownloadWorker(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.onSuccess {
|
}.onSuccess {
|
||||||
//move file from internal to set download directory
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
try {
|
//move file from internal to set download directory
|
||||||
FileUtil.moveFile(tempFileDir.absoluteFile,context, downloadLocation!!, false){ p ->
|
try {
|
||||||
setProgressAsync(workDataOf("progress" to p))
|
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){
|
if (it.out.length > 200){
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingBottom="200dp"
|
android:layout_marginBottom="200dp"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,23 +28,18 @@
|
||||||
android:label="MainSettingsFragment" >
|
android:label="MainSettingsFragment" >
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_mainSettingsFragment_to_appearanceSettingsFragment"
|
android:id="@+id/action_mainSettingsFragment_to_appearanceSettingsFragment"
|
||||||
app:destination="@id/appearanceSettingsFragment"
|
app:destination="@id/appearanceSettingsFragment" />
|
||||||
app:enterAnim="@anim/fade_in" />
|
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_mainSettingsFragment_to_folderSettingsFragment"
|
android:id="@+id/action_mainSettingsFragment_to_folderSettingsFragment"
|
||||||
app:destination="@id/folderSettingsFragment"
|
app:destination="@id/folderSettingsFragment" />
|
||||||
app:enterAnim="@anim/fade_in" />
|
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_mainSettingsFragment_to_downloadSettingsFragment"
|
android:id="@+id/action_mainSettingsFragment_to_downloadSettingsFragment"
|
||||||
app:destination="@id/downloadSettingsFragment"
|
app:destination="@id/downloadSettingsFragment" />
|
||||||
app:enterAnim="@anim/fade_in" />
|
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_mainSettingsFragment_to_processingSettingsFragment"
|
android:id="@+id/action_mainSettingsFragment_to_processingSettingsFragment"
|
||||||
app:destination="@id/processingSettingsFragment"
|
app:destination="@id/processingSettingsFragment" />
|
||||||
app:enterAnim="@anim/fade_in" />
|
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_mainSettingsFragment_to_updateSettingsFragment"
|
android:id="@+id/action_mainSettingsFragment_to_updateSettingsFragment"
|
||||||
app:destination="@id/updateSettingsFragment"
|
app:destination="@id/updateSettingsFragment" />
|
||||||
app:enterAnim="@anim/fade_in" />
|
|
||||||
</fragment>
|
</fragment>
|
||||||
</navigation>
|
</navigation>
|
||||||
|
|
@ -203,6 +203,35 @@
|
||||||
|
|
||||||
</style>
|
</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">
|
<style name="Pure">
|
||||||
|
|
||||||
<item name="android:colorBackground">@android:color/black</item>
|
<item name="android:colorBackground">@android:color/black</item>
|
||||||
|
|
|
||||||
|
|
@ -224,6 +224,7 @@
|
||||||
<string name="red">E Kuqe</string>
|
<string name="red">E Kuqe</string>
|
||||||
<string name="purple">Vjollcë</string>
|
<string name="purple">Vjollcë</string>
|
||||||
<string name="yellow">E Verdhë</string>
|
<string name="yellow">E Verdhë</string>
|
||||||
|
<string name="orange">Portokalli</string>
|
||||||
<string name="monochrome">Monokrom</string>
|
<string name="monochrome">Monokrom</string>
|
||||||
<string name="green">Jeshile</string>
|
<string name="green">Jeshile</string>
|
||||||
<string name="downloaded">"U shkarkua: "</string>
|
<string name="downloaded">"U shkarkua: "</string>
|
||||||
|
|
@ -262,7 +263,7 @@
|
||||||
<string name="misc">Të Tjera</string>
|
<string name="misc">Të Tjera</string>
|
||||||
<string name="audio_only_item">Ky artikull përmban vetëm formate audio</string>
|
<string name="audio_only_item">Ky artikull përmban vetëm formate audio</string>
|
||||||
<string name="locale">Lokal</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">Gjestet e rrëshqitjes</string>
|
||||||
<string name="swipe_gestures_summary">Rrëshqitni artikujt për veprime të caktuara</string>
|
<string name="swipe_gestures_summary">Rrëshqitni artikujt për veprime të caktuara</string>
|
||||||
<string name="crop_thumb">Preje Thumbnailin</string>
|
<string name="crop_thumb">Preje Thumbnailin</string>
|
||||||
|
|
|
||||||
|
|
@ -226,6 +226,7 @@
|
||||||
<item>@string/green</item>
|
<item>@string/green</item>
|
||||||
<item>@string/purple</item>
|
<item>@string/purple</item>
|
||||||
<item>@string/yellow</item>
|
<item>@string/yellow</item>
|
||||||
|
<item>@string/orange</item>
|
||||||
<item>@string/monochrome</item>
|
<item>@string/monochrome</item>
|
||||||
</array>
|
</array>
|
||||||
|
|
||||||
|
|
@ -236,6 +237,7 @@
|
||||||
<item>green</item>
|
<item>green</item>
|
||||||
<item>purple</item>
|
<item>purple</item>
|
||||||
<item>yellow</item>
|
<item>yellow</item>
|
||||||
|
<item>orange</item>
|
||||||
<item>monochrome</item>
|
<item>monochrome</item>
|
||||||
</array>
|
</array>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@
|
||||||
<color name="red_md_theme_dark_inverseSurface">#EDE0DE</color>
|
<color name="red_md_theme_dark_inverseSurface">#EDE0DE</color>
|
||||||
<color name="red_md_theme_dark_inversePrimary">#9F4034</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_onPrimary">#FFFFFF</color>
|
||||||
<color name="yellow_md_theme_light_primaryContainer">#FFE08D</color>
|
<color name="yellow_md_theme_light_primaryContainer">#FFE08D</color>
|
||||||
<color name="yellow_md_theme_light_onPrimaryContainer">#241A00</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_inverseSurface">#EAE0E3</color>
|
||||||
<color name="purple_md_theme_dark_inversePrimary">#9B3489</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
|
// Colors without comments seem to be not used
|
||||||
<color name="monochrome_theme_light_primary">#616161</color>
|
<color name="monochrome_theme_light_primary">#616161</color>
|
||||||
// LibreTube Logo, Hyperlinks and on-off switch (on status: filling) (also influence on the
|
// LibreTube Logo, Hyperlinks and on-off switch (on status: filling) (also influence on the
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@
|
||||||
<string name="url">URL</string>
|
<string name="url">URL</string>
|
||||||
<string name="link_copied_to_clipboard">Link copied to clipboard</string>
|
<string name="link_copied_to_clipboard">Link copied to clipboard</string>
|
||||||
<string name="api_key">Use API Key</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="update_app_summary">Announce new versions of the app when it is opened</string>
|
||||||
<string name="downloads">Downloads</string>
|
<string name="downloads">Downloads</string>
|
||||||
<string name="remove_deleted">Remove Deleted</string>
|
<string name="remove_deleted">Remove Deleted</string>
|
||||||
|
|
@ -232,6 +232,7 @@
|
||||||
<string name="green">Green</string>
|
<string name="green">Green</string>
|
||||||
<string name="purple">Purple</string>
|
<string name="purple">Purple</string>
|
||||||
<string name="yellow">Yellow</string>
|
<string name="yellow">Yellow</string>
|
||||||
|
<string name="orange">Orange</string>
|
||||||
<string name="monochrome">Monochrome</string>
|
<string name="monochrome">Monochrome</string>
|
||||||
<string name="downloaded">"Downloaded: "</string>
|
<string name="downloaded">"Downloaded: "</string>
|
||||||
<string name="finished_download_notification_channel_name">Finished Downloads</string>
|
<string name="finished_download_notification_channel_name">Finished Downloads</string>
|
||||||
|
|
|
||||||
|
|
@ -205,6 +205,35 @@
|
||||||
|
|
||||||
</style>
|
</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">
|
<style name="Pure">
|
||||||
|
|
||||||
<item name="android:colorBackground">@android:color/white</item>
|
<item name="android:colorBackground">@android:color/white</item>
|
||||||
|
|
|
||||||
|
|
@ -35,14 +35,6 @@
|
||||||
app:summary="@string/download_over_metered_networks_summary"
|
app:summary="@string/download_over_metered_networks_summary"
|
||||||
app:title="@string/download_over_metered_networks" />
|
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
|
<EditTextPreference
|
||||||
android:icon="@drawable/baseline_network_locked_24"
|
android:icon="@drawable/baseline_network_locked_24"
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,12 @@
|
||||||
app:summary="@string/video_recommendations_summary"
|
app:summary="@string/video_recommendations_summary"
|
||||||
app:title="@string/video_recommendations" />
|
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
|
<ListPreference
|
||||||
android:defaultValue=""
|
android:defaultValue=""
|
||||||
|
|
@ -106,7 +112,7 @@
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
app:defaultValue="false"
|
app:defaultValue="true"
|
||||||
android:icon="@drawable/baseline_share_24"
|
android:icon="@drawable/baseline_share_24"
|
||||||
android:key="hide_terminal"
|
android:key="hide_terminal"
|
||||||
app:title="@string/hide_terminal" />
|
app:title="@string/hide_terminal" />
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,16 @@
|
||||||
app:defaultValue="all"
|
app:defaultValue="all"
|
||||||
app:title="@string/subtitle_languages" />
|
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
|
<SwitchPreferenceCompat
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
app:defaultValue="true"
|
app:defaultValue="true"
|
||||||
|
|
@ -60,6 +70,7 @@
|
||||||
app:summary="@string/crop_thumb_summary"
|
app:summary="@string/crop_thumb_summary"
|
||||||
app:title="@string/crop_thumb" />
|
app:title="@string/crop_thumb" />
|
||||||
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
app:defaultValue="false"
|
app:defaultValue="false"
|
||||||
|
|
@ -68,22 +79,6 @@
|
||||||
app:summary="@string/force_keyframes_summary"
|
app:summary="@string/force_keyframes_summary"
|
||||||
app:title="@string/force_keyframes" />
|
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
|
<SwitchPreferenceCompat
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
|
@ -93,6 +88,15 @@
|
||||||
app:summary="@string/save_thumb_summary"
|
app:summary="@string/save_thumb_summary"
|
||||||
app:title="@string/save_thumb" />
|
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
|
<ListPreference
|
||||||
android:defaultValue="Default"
|
android:defaultValue="Default"
|
||||||
android:entries="@array/audio_containers"
|
android:entries="@array/audio_containers"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue