fixes, fix app preferring auto dub instead of original audio
This commit is contained in:
parent
1a12637397
commit
0485fb71e1
21 changed files with 319 additions and 138 deletions
|
|
@ -149,9 +149,9 @@ dependencies {
|
|||
// implementation "com.github.yausername.youtubedl-android:aria2c:$youtubedlAndroidVer"
|
||||
|
||||
|
||||
implementation "io.github.junkfood02.youtubedl-android:library:0.17.4"
|
||||
implementation "io.github.junkfood02.youtubedl-android:ffmpeg:0.17.2"
|
||||
implementation "io.github.junkfood02.youtubedl-android:aria2c:0.17.2"
|
||||
implementation "io.github.junkfood02.youtubedl-android:library:0.18.0"
|
||||
implementation "io.github.junkfood02.youtubedl-android:ffmpeg:0.18.0"
|
||||
implementation "io.github.junkfood02.youtubedl-android:aria2c:0.18.0"
|
||||
|
||||
implementation "androidx.appcompat:appcompat:$appCompatVer"
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.2.1"
|
||||
|
|
|
|||
14
app/src/main/assets/build_fake_node/README.md
Normal file
14
app/src/main/assets/build_fake_node/README.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# Set NDK path
|
||||
NDK=/path/to/android-ndk
|
||||
|
||||
# Target: arm64-v8a, min API 24
|
||||
$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang -O2 -static -o android-js-arm64 fake-node.c
|
||||
|
||||
# armeabi-v7a
|
||||
$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi14-clang -O2 -static -o android-js-arm fake-node.c
|
||||
|
||||
# x86
|
||||
$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android24-clang -O2 -static -o android-js-x86 fake-node.c
|
||||
|
||||
# x86_64
|
||||
$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android24-clang -O2 -static -o android-js-x86_64 fake-node.c
|
||||
70
app/src/main/assets/build_fake_node/fake-node.c
Normal file
70
app/src/main/assets/build_fake_node/fake-node.c
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#define PORT 65953
|
||||
#define BUFSIZE 65536
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
char buffer[BUFSIZE];
|
||||
int sock;
|
||||
struct sockaddr_in server_addr;
|
||||
|
||||
// Collect JS code
|
||||
char *code = NULL;
|
||||
size_t code_len = 0;
|
||||
|
||||
if (argc > 1) {
|
||||
// Join argv[1..] as code (like "node -e 'code'")
|
||||
size_t total = 0;
|
||||
for (int i = 1; i < argc; i++) total += strlen(argv[i]) + 1;
|
||||
code = malloc(total);
|
||||
code[0] = '\0';
|
||||
for (int i = 1; i < argc; i++) {
|
||||
strcat(code, argv[i]);
|
||||
if (i < argc - 1) strcat(code, " ");
|
||||
}
|
||||
} else {
|
||||
// Read from stdin
|
||||
ssize_t n = read(STDIN_FILENO, buffer, BUFSIZE - 1);
|
||||
if (n <= 0) return 1;
|
||||
buffer[n] = '\0';
|
||||
code = strdup(buffer);
|
||||
}
|
||||
|
||||
// Open socket
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock < 0) {
|
||||
perror("socket");
|
||||
return 1;
|
||||
}
|
||||
|
||||
server_addr.sin_family = AF_INET;
|
||||
server_addr.sin_port = htons(PORT);
|
||||
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
|
||||
if (connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
|
||||
perror("connect");
|
||||
close(sock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Send code
|
||||
write(sock, code, strlen(code));
|
||||
write(sock, "\0", 1);
|
||||
|
||||
// Receive result
|
||||
ssize_t n = read(sock, buffer, BUFSIZE - 1);
|
||||
if (n > 0) {
|
||||
buffer[n] = '\0';
|
||||
printf("%s\n", buffer);
|
||||
}
|
||||
|
||||
close(sock);
|
||||
free(code);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import androidx.core.content.edit
|
|||
import androidx.preference.PreferenceManager
|
||||
import com.deniscerri.ytdl.util.NotificationUtil
|
||||
import com.deniscerri.ytdl.util.ThemeUtil
|
||||
import com.deniscerri.ytdl.util.extractors.ytdlp.webview.YTDLPWebview
|
||||
import com.yausername.aria2c.Aria2c
|
||||
import com.yausername.ffmpeg.FFmpeg
|
||||
import com.yausername.youtubedl_android.YoutubeDL
|
||||
|
|
@ -15,7 +16,6 @@ import kotlinx.coroutines.CoroutineScope
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.*
|
||||
|
||||
|
||||
class App : Application() {
|
||||
|
|
@ -30,8 +30,10 @@ class App : Application() {
|
|||
applicationScope.launch((Dispatchers.IO)) {
|
||||
try {
|
||||
createNotificationChannels()
|
||||
|
||||
initLibraries()
|
||||
//init js interp server
|
||||
//val jsServer = YTDLPWebview(this@App, 65953)
|
||||
|
||||
val appVer = sharedPreferences.getString("version", "")!!
|
||||
if(appVer.isEmpty() || appVer != BuildConfig.VERSION_NAME){
|
||||
sharedPreferences.edit(commit = true){
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import com.deniscerri.ytdl.util.Extensions.isYoutubeWatchVideosURL
|
|||
import com.deniscerri.ytdl.util.Extensions.needsDataUpdating
|
||||
import com.deniscerri.ytdl.util.extractors.GoogleApiUtil
|
||||
import com.deniscerri.ytdl.util.extractors.newpipe.NewPipeUtil
|
||||
import com.deniscerri.ytdl.util.extractors.YTDLPUtil
|
||||
import com.deniscerri.ytdl.util.extractors.ytdlp.YTDLPUtil
|
||||
import com.deniscerri.ytdl.util.extractors.YoutubeApiUtil
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ import com.deniscerri.ytdl.util.Extensions.toListString
|
|||
import com.deniscerri.ytdl.util.FileUtil
|
||||
import com.deniscerri.ytdl.util.FormatUtil
|
||||
import com.deniscerri.ytdl.util.NotificationUtil
|
||||
import com.deniscerri.ytdl.util.extractors.YTDLPUtil
|
||||
import com.deniscerri.ytdl.util.extractors.ytdlp.YTDLPUtil
|
||||
import com.deniscerri.ytdl.work.AlarmScheduler
|
||||
import com.deniscerri.ytdl.work.UpdateMultipleDownloadsDataWorker
|
||||
import com.deniscerri.ytdl.work.UpdateMultipleDownloadsFormatsWorker
|
||||
|
|
|
|||
|
|
@ -1,72 +1,11 @@
|
|||
package com.deniscerri.ytdl.database.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.os.Build
|
||||
import android.os.Parcelable
|
||||
import android.util.DisplayMetrics
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.asLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.filter
|
||||
import androidx.paging.map
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.work.Data
|
||||
import androidx.work.ExistingWorkPolicy
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import com.deniscerri.ytdl.App
|
||||
import com.deniscerri.ytdl.R
|
||||
import com.deniscerri.ytdl.database.DBManager
|
||||
import com.deniscerri.ytdl.database.dao.CommandTemplateDao
|
||||
import com.deniscerri.ytdl.database.dao.DownloadDao
|
||||
import com.deniscerri.ytdl.database.models.AudioPreferences
|
||||
import com.deniscerri.ytdl.database.models.CommandTemplate
|
||||
import com.deniscerri.ytdl.database.models.DownloadItem
|
||||
import com.deniscerri.ytdl.database.models.DownloadItemConfigureMultiple
|
||||
import com.deniscerri.ytdl.database.models.DownloadItemSimple
|
||||
import com.deniscerri.ytdl.database.models.Format
|
||||
import com.deniscerri.ytdl.database.models.HistoryItem
|
||||
import com.deniscerri.ytdl.database.models.ResultItem
|
||||
import com.deniscerri.ytdl.database.models.VideoPreferences
|
||||
import com.deniscerri.ytdl.database.repository.DownloadRepository
|
||||
import com.deniscerri.ytdl.database.repository.HistoryRepository
|
||||
import com.deniscerri.ytdl.database.repository.ResultRepository
|
||||
import com.deniscerri.ytdl.ui.downloadcard.MultipleItemFormatTuple
|
||||
import com.deniscerri.ytdl.util.Extensions.needsDataUpdating
|
||||
import com.deniscerri.ytdl.util.Extensions.toListString
|
||||
import com.deniscerri.ytdl.util.FileUtil
|
||||
import com.deniscerri.ytdl.util.FormatUtil
|
||||
import com.deniscerri.ytdl.util.NotificationUtil
|
||||
import com.deniscerri.ytdl.util.extractors.YTDLPUtil
|
||||
import com.deniscerri.ytdl.work.AlarmScheduler
|
||||
import com.deniscerri.ytdl.work.UpdateMultipleDownloadsDataWorker
|
||||
import com.deniscerri.ytdl.work.UpdateMultipleDownloadsFormatsWorker
|
||||
import com.google.gson.Gson
|
||||
import com.yausername.youtubedl_android.YoutubeDL
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.io.File
|
||||
import java.util.Locale
|
||||
import com.deniscerri.ytdl.util.extractors.ytdlp.YTDLPUtil
|
||||
|
||||
|
||||
class YTDLPViewModel(private val application: Application) : AndroidViewModel(application) {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import com.deniscerri.ytdl.database.viewmodel.CommandTemplateViewModel
|
|||
import com.deniscerri.ytdl.database.viewmodel.YTDLPViewModel
|
||||
import com.deniscerri.ytdl.util.Extensions.enableTextHighlight
|
||||
import com.deniscerri.ytdl.util.UiUtil
|
||||
import com.deniscerri.ytdl.util.extractors.YTDLPUtil
|
||||
import com.deniscerri.ytdl.util.extractors.ytdlp.YTDLPUtil
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import com.google.android.material.elevation.SurfaceColors
|
||||
|
|
|
|||
|
|
@ -59,7 +59,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|||
import com.google.android.material.elevation.SurfaceColors
|
||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.yausername.youtubedl_android.YoutubeDL
|
||||
import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package com.deniscerri.ytdl.ui.downloads
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.DialogInterface
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
|
|
@ -20,18 +18,14 @@ import androidx.viewpager2.widget.ViewPager2
|
|||
import androidx.work.WorkManager
|
||||
import com.deniscerri.ytdl.MainActivity
|
||||
import com.deniscerri.ytdl.R
|
||||
import com.deniscerri.ytdl.database.repository.DownloadRepository
|
||||
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
||||
import com.deniscerri.ytdl.util.Extensions.createBadge
|
||||
import com.deniscerri.ytdl.util.NavbarUtil
|
||||
import com.deniscerri.ytdl.util.NotificationUtil
|
||||
import com.deniscerri.ytdl.util.UiUtil
|
||||
import com.google.android.material.appbar.MaterialToolbar
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import com.google.gson.Gson
|
||||
import com.yausername.youtubedl_android.YoutubeDL
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import android.content.SharedPreferences
|
|||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
|
|
@ -30,7 +29,6 @@ import androidx.preference.PreferenceManager
|
|||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.work.WorkManager
|
||||
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
|
||||
import com.deniscerri.ytdl.R
|
||||
import com.deniscerri.ytdl.database.models.DownloadItem
|
||||
|
|
@ -49,7 +47,6 @@ import com.google.android.material.card.MaterialCardView
|
|||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.yausername.youtubedl_android.YoutubeDL
|
||||
import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.deniscerri.ytdl.ui.more
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
|
|
@ -16,22 +15,14 @@ import androidx.lifecycle.ViewModelProvider
|
|||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.work.WorkManager
|
||||
import com.deniscerri.ytdl.MainActivity
|
||||
import com.deniscerri.ytdl.R
|
||||
import com.deniscerri.ytdl.database.repository.DownloadRepository
|
||||
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
||||
import com.deniscerri.ytdl.ui.more.settings.SettingsActivity
|
||||
import com.deniscerri.ytdl.ui.more.terminal.TerminalActivity
|
||||
import com.deniscerri.ytdl.util.NavbarUtil
|
||||
import com.deniscerri.ytdl.util.NotificationUtil
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.yausername.youtubedl_android.YoutubeDL
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
class MoreFragment : Fragment() {
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@ class FolderSettingsFragment : BaseSettingsFragment() {
|
|||
}else {
|
||||
FileUtil.convertFileSize(cacheSize)
|
||||
}
|
||||
clearCache!!.summary = "(${filesize}) ${resources.getString(R.string.clear_temporary_files_summary)}"
|
||||
clearCache!!.summary = "${resources.getString(R.string.clear_temporary_files_summary)} (${filesize}) "
|
||||
clearCache!!.onPreferenceClickListener =
|
||||
Preference.OnPreferenceClickListener {
|
||||
lifecycleScope.launch {
|
||||
|
|
@ -224,7 +224,7 @@ class FolderSettingsFragment : BaseSettingsFragment() {
|
|||
}else {
|
||||
FileUtil.convertFileSize(cacheSize)
|
||||
}
|
||||
clearCache!!.summary = "(${filesize}) ${resources.getString(R.string.clear_temporary_files_summary)}"
|
||||
clearCache!!.summary = "${resources.getString(R.string.clear_temporary_files_summary)} (${filesize})"
|
||||
}else{
|
||||
Snackbar.make(requireView(), getString(R.string.downloads_running_try_later), Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
|
|
@ -252,7 +252,7 @@ class FolderSettingsFragment : BaseSettingsFragment() {
|
|||
|
||||
if (list.first().state == WorkInfo.State.SUCCEEDED){
|
||||
cacheSize = File(FileUtil.getCachePath(requireContext())).walkBottomUp().fold(0L) { acc, file -> acc + file.length() }
|
||||
clearCache!!.summary = "(${FileUtil.convertFileSize(cacheSize)}) ${resources.getString(R.string.clear_temporary_files_summary)}"
|
||||
clearCache!!.summary = "${resources.getString(R.string.clear_temporary_files_summary)} (${FileUtil.convertFileSize(cacheSize)})"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
package com.deniscerri.ytdl.util
|
||||
|
||||
import android.R.attr.mimeType
|
||||
import android.app.Activity
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.media.MediaScannerConnection
|
||||
|
|
@ -11,19 +8,12 @@ import android.os.Build
|
|||
import android.os.Environment
|
||||
import android.provider.DocumentsContract
|
||||
import android.provider.MediaStore
|
||||
import android.util.DisplayMetrics
|
||||
import android.util.Log
|
||||
import android.view.ViewGroup
|
||||
import android.view.Window
|
||||
import android.webkit.MimeTypeMap
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.anggrayudi.storage.callback.FileCallback
|
||||
import com.anggrayudi.storage.callback.FolderCallback
|
||||
|
|
@ -31,27 +21,20 @@ import com.anggrayudi.storage.file.copyFolderTo
|
|||
import com.anggrayudi.storage.file.getAbsolutePath
|
||||
import com.anggrayudi.storage.file.moveFileTo
|
||||
import com.deniscerri.ytdl.App
|
||||
import com.deniscerri.ytdl.BuildConfig
|
||||
import com.deniscerri.ytdl.MainActivity
|
||||
import com.deniscerri.ytdl.R
|
||||
import com.deniscerri.ytdl.util.Extensions.getMediaDuration
|
||||
import com.deniscerri.ytdl.util.Extensions.toStringDuration
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.yausername.youtubedl_android.YoutubeDLRequest
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.internal.closeQuietly
|
||||
import okhttp3.internal.format
|
||||
import okhttp3.internal.lowercase
|
||||
import java.io.File
|
||||
import java.net.URLDecoder
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.StandardCopyOption
|
||||
import java.text.DecimalFormat
|
||||
import java.text.DecimalFormatSymbols
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
import kotlin.io.path.absolutePathString
|
||||
import kotlin.math.log10
|
||||
import kotlin.math.pow
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ class FormatUtil(private var context: Context) {
|
|||
|
||||
return orderPreferences
|
||||
}else {
|
||||
val formatImportance = mutableListOf("id","codec", "container", "language")
|
||||
val formatImportance = mutableListOf("id", "language", "codec", "container")
|
||||
if (preferredFormatSize == "smallest") {
|
||||
formatImportance.add("smallsize")
|
||||
}else if (preferredFormatSize == "largest") {
|
||||
|
|
@ -122,13 +122,27 @@ class FormatUtil(private var context: Context) {
|
|||
|
||||
"language" -> {
|
||||
if (audioLanguagePreference.isBlank()) {
|
||||
0
|
||||
(b.format_note.contains("default", true)).compareTo(
|
||||
a.format_note.contains(
|
||||
"default", true
|
||||
)
|
||||
)
|
||||
//0
|
||||
} else {
|
||||
(b.lang?.contains(audioLanguagePreference) == true).compareTo(
|
||||
val res = (b.lang?.contains(audioLanguagePreference) == true).compareTo(
|
||||
a.lang?.contains(
|
||||
audioLanguagePreference
|
||||
) == true
|
||||
)
|
||||
if(res == 0) {
|
||||
(b.format_note.contains("default", true)).compareTo(
|
||||
a.format_note.contains(
|
||||
"default", true
|
||||
)
|
||||
)
|
||||
}else {
|
||||
res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ import com.deniscerri.ytdl.util.Extensions.createBadge
|
|||
import com.deniscerri.ytdl.util.Extensions.enableTextHighlight
|
||||
import com.deniscerri.ytdl.util.Extensions.getMediaDuration
|
||||
import com.deniscerri.ytdl.util.Extensions.toStringDuration
|
||||
import com.deniscerri.ytdl.util.extractors.YTDLPUtil
|
||||
import com.deniscerri.ytdl.util.extractors.ytdlp.YTDLPUtil
|
||||
import com.google.android.material.badge.BadgeDrawable
|
||||
import com.google.android.material.badge.BadgeUtils
|
||||
import com.google.android.material.badge.ExperimentalBadgeUtils
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import com.google.gson.reflect.TypeToken
|
|||
import com.yausername.youtubedl_android.YoutubeDL
|
||||
import com.yausername.youtubedl_android.YoutubeDLRequest
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.processNextEventInCurrentThread
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.InputStreamReader
|
||||
import java.net.HttpURLConnection
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package com.deniscerri.ytdl.util.extractors
|
||||
package com.deniscerri.ytdl.util.extractors.ytdlp
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
|
|
@ -32,8 +32,8 @@ import com.deniscerri.ytdl.util.FileUtil
|
|||
import com.deniscerri.ytdl.util.FormatUtil
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.yausername.youtubedl_android.YoutubeDL
|
||||
import com.yausername.youtubedl_android.YoutubeDLRequest
|
||||
import com.yausername.youtubedl_android.YoutubeDL
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.withContext
|
||||
|
|
@ -111,7 +111,8 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
|
|||
if (query.contains("http")){
|
||||
if (query.isYoutubeWatchVideosURL()) {
|
||||
request = YoutubeDLRequest(emptyList())
|
||||
val config = File(context.cacheDir.absolutePath + "/config" + System.currentTimeMillis() + "##url.txt")
|
||||
val config =
|
||||
File(context.cacheDir.absolutePath + "/config" + System.currentTimeMillis() + "##url.txt")
|
||||
config.writeText(query)
|
||||
request.addOption("--config", config.absolutePath)
|
||||
}else{
|
||||
|
|
@ -182,7 +183,7 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
|
|||
|
||||
if (playlistTitle.isNotBlank()){
|
||||
playlistURL = jsonObject.getStringByAny("playlist_webpage_url").ifEmpty { query }
|
||||
kotlin.runCatching { playlistIndex = jsonObject.getInt("playlist_index") }
|
||||
runCatching { playlistIndex = jsonObject.getInt("playlist_index") }
|
||||
}
|
||||
val url = if (jsonObject.has("url") && results.size > 1){
|
||||
jsonObject.getString("url")
|
||||
|
|
@ -266,7 +267,8 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
|
|||
}
|
||||
}
|
||||
|
||||
val res = ResultItem(0,
|
||||
val res = ResultItem(
|
||||
0,
|
||||
url = url,
|
||||
title = title,
|
||||
author = author,
|
||||
|
|
@ -417,7 +419,7 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
|
|||
e.message?.split(System.lineSeparator())?.onEach { line ->
|
||||
println(line)
|
||||
if (line.contains("unavailable")) {
|
||||
kotlin.runCatching {
|
||||
runCatching {
|
||||
val id = Regex("""\[.*?\] (\w+):""").find(line)!!.groupValues[1]
|
||||
val url = urls.first { it.contains(id) }
|
||||
progress(
|
||||
|
|
@ -464,7 +466,7 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
|
|||
arrayOf(res.out)
|
||||
}
|
||||
val json = results[0]
|
||||
val jsonArray = kotlin.runCatching { JSONArray(json) }.getOrElse { JSONArray() }
|
||||
val jsonArray = runCatching { JSONArray(json) }.getOrElse { JSONArray() }
|
||||
|
||||
return parseYTDLFormats(jsonArray)
|
||||
}
|
||||
|
|
@ -475,19 +477,19 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
|
|||
if (formatsInJSON != null) {
|
||||
for (f in formatsInJSON.length() - 1 downTo 0){
|
||||
val format = formatsInJSON.getJSONObject(f)
|
||||
kotlin.runCatching {
|
||||
runCatching {
|
||||
if (format.get("filesize").toString() == "None") {
|
||||
format.remove("filesize")
|
||||
}
|
||||
}
|
||||
|
||||
kotlin.runCatching {
|
||||
runCatching {
|
||||
if (format.get("filesize_approx").toString() == "None") {
|
||||
format.remove("filesize_approx")
|
||||
}
|
||||
}
|
||||
|
||||
kotlin.runCatching {
|
||||
runCatching {
|
||||
if(format.get("format_note").toString() == "null"){
|
||||
format.remove("format_note")
|
||||
}
|
||||
|
|
@ -686,8 +688,8 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
|
|||
|
||||
val useLanguageForMetadata = sharedPreferences.getBoolean("use_app_language_for_metadata", true)
|
||||
if (useLanguageForMetadata) {
|
||||
val lang = Locale.getDefault().language
|
||||
val langTag = Locale.getDefault().toLanguageTag()
|
||||
val lang = java.util.Locale.getDefault().language
|
||||
val langTag = java.util.Locale.getDefault().toLanguageTag()
|
||||
if (context.getStringArray(R.array.subtitle_langs).contains(lang)) {
|
||||
extractorArgs.add("lang=$lang")
|
||||
}else if (context.getStringArray(R.array.subtitle_langs).contains(langTag)) {
|
||||
|
|
@ -709,7 +711,7 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
|
|||
private fun YoutubeDLRequest.addConfig(commandString: String) {
|
||||
this.addOption(
|
||||
"--config-locations",
|
||||
File(context.cacheDir.absolutePath + "/${System.currentTimeMillis()}${UUID.randomUUID()}.txt").apply {
|
||||
File(context.cacheDir.absolutePath + "/${System.currentTimeMillis()}${java.util.UUID.randomUUID()}.txt").apply {
|
||||
writeText(commandString)
|
||||
}.absolutePath
|
||||
)
|
||||
|
|
@ -956,7 +958,8 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
|
|||
var abrSort = ""
|
||||
|
||||
var audioQualityId : String = downloadItem.format.format_id
|
||||
if (audioQualityId.isBlank() || listOf("0", context.getString(R.string.best_quality), "ba", "best", "", context.getString(R.string.worst_quality), "wa", "worst").contains(audioQualityId)){
|
||||
if (audioQualityId.isBlank() || listOf("0", context.getString(R.string.best_quality), "ba", "best", "", context.getString(
|
||||
R.string.worst_quality), "wa", "worst").contains(audioQualityId)){
|
||||
audioQualityId = "ba/b"
|
||||
}else if(audioQualityId.contains("kbps_ytdlnisgeneric")){
|
||||
abrSort = audioQualityId.split("kbps")[0]
|
||||
|
|
@ -1134,7 +1137,8 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
|
|||
|
||||
var cont = ""
|
||||
val outputContainer = downloadItem.container
|
||||
if(outputContainer.isNotEmpty() && outputContainer != "Default" && outputContainer != context.getString(R.string.defaultValue) && supportedContainers.contains(outputContainer)){
|
||||
if(outputContainer.isNotEmpty() && outputContainer != "Default" && outputContainer != context.getString(
|
||||
R.string.defaultValue) && supportedContainers.contains(outputContainer)){
|
||||
cont = outputContainer
|
||||
|
||||
val cantRecode = listOf("avi")
|
||||
|
|
@ -1225,7 +1229,8 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
|
|||
}
|
||||
}
|
||||
}else{
|
||||
if (videoF == context.resources.getString(R.string.best_quality) || videoF == "best" || videoF == context.resources.getString(R.string.worst_quality) || videoF == "worst") {
|
||||
if (videoF == context.resources.getString(R.string.best_quality) || videoF == "best" || videoF == context.resources.getString(
|
||||
R.string.worst_quality) || videoF == "worst") {
|
||||
videoF = "bv"
|
||||
}else if (defaultFormats.contains(videoF)) {
|
||||
hasGenericResulutionFormat = videoF.split("_")[0].dropLast(1)
|
||||
|
|
@ -1423,7 +1428,7 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
|
|||
val cacheDirArg = """(--cache-dir (".*"))""".toRegex().find(downloadItem.extraCommands)
|
||||
if (cacheDirArg != null) {
|
||||
ytDlRequest.addOption("--cache-dir", cacheDirArg.groupValues.last().replace("\"", ""))
|
||||
kotlin.runCatching {
|
||||
runCatching {
|
||||
downloadItem.extraCommands = downloadItem.extraCommands.replace(cacheDirArg.value, "")
|
||||
}
|
||||
}
|
||||
|
|
@ -1437,7 +1442,8 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
|
|||
|
||||
val cache = File(FileUtil.getCachePath(context))
|
||||
cache.mkdirs()
|
||||
val conf = File(cache.absolutePath + "/${System.currentTimeMillis()}${UUID.randomUUID()}.txt")
|
||||
val conf =
|
||||
File(cache.absolutePath + "/${System.currentTimeMillis()}${UUID.randomUUID()}.txt")
|
||||
conf.createNewFile()
|
||||
conf.writeText(request.toString())
|
||||
val tmp = mutableListOf<String>()
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
package com.deniscerri.ytdl.util.extractors.ytdlp.webview
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.webkit.JavascriptInterface
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import org.json.JSONObject
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.net.InetAddress
|
||||
import java.net.ServerSocket
|
||||
import java.net.Socket
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
class YTDLPWebview(private val context: Context, private val port: Int, private val evalTimeoutMs: Long = 10_000L) {
|
||||
private val mainHandler = Handler(Looper.getMainLooper())
|
||||
private val executor = Executors.newCachedThreadPool()
|
||||
private lateinit var webView: WebView
|
||||
|
||||
// map callId -> callback
|
||||
private val callbacks = ConcurrentHashMap<String, (JSONObject) -> Unit>()
|
||||
|
||||
init {
|
||||
initWebView()
|
||||
// start server off-main thread
|
||||
executor.submit { startServer() }
|
||||
}
|
||||
|
||||
private fun initWebView() {
|
||||
mainHandler.post {
|
||||
webView = WebView(context)
|
||||
val s = webView.settings
|
||||
s.javaScriptEnabled = true
|
||||
s.allowFileAccess = false
|
||||
s.allowContentAccess = false
|
||||
// disable access to file:// and other risky things
|
||||
webView.settings.domStorageEnabled = false
|
||||
webView.webViewClient = WebViewClient()
|
||||
webView.addJavascriptInterface(JsBridge(), "AndroidBridge")
|
||||
// Load blank page (we will eval into this context)
|
||||
webView.loadData("<html><body></body></html>", "text/html", "utf-8")
|
||||
}
|
||||
}
|
||||
|
||||
inner class JsBridge {
|
||||
@JavascriptInterface
|
||||
fun postResponse(id: String, json: String) {
|
||||
val obj = try { JSONObject(json) } catch (_: Exception) {
|
||||
JSONObject().put("result", JSONObject.NULL).put("error", "invalid_json_from_js")
|
||||
}
|
||||
callbacks.remove(id)?.invoke(obj)
|
||||
}
|
||||
}
|
||||
|
||||
private fun startServer() {
|
||||
// Bind only to localhost
|
||||
val server = ServerSocket(port, 50, InetAddress.getByName("127.0.0.1"))
|
||||
while (!server.isClosed) {
|
||||
val client = server.accept()
|
||||
executor.submit { handleClient(client) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleClient(client: Socket) {
|
||||
client.soTimeout = (evalTimeoutMs + 2000).toInt()
|
||||
val reader = BufferedReader(InputStreamReader(client.getInputStream()))
|
||||
// read until NUL terminator (wrapper should send code+'\0')
|
||||
val sb = StringBuilder()
|
||||
var ch = reader.read()
|
||||
while (ch >= 0 && ch != 0) {
|
||||
sb.append(ch.toChar())
|
||||
ch = reader.read()
|
||||
}
|
||||
val code = sb.toString()
|
||||
|
||||
val callId = System.nanoTime().toString()
|
||||
// register callback with timeout
|
||||
val done = Object()
|
||||
var responded = false
|
||||
|
||||
callbacks[callId] = { jsonObj ->
|
||||
try {
|
||||
val bytes = (jsonObj.toString() + "\u0000").toByteArray()
|
||||
client.getOutputStream().write(bytes)
|
||||
client.getOutputStream().flush()
|
||||
} catch (_: Exception) { }
|
||||
finally {
|
||||
try { client.close() } catch (_: Exception) {}
|
||||
synchronized(done) { responded = true; done.notifyAll() }
|
||||
}
|
||||
}
|
||||
|
||||
// schedule timeout
|
||||
executor.submit {
|
||||
try {
|
||||
Thread.sleep(evalTimeoutMs)
|
||||
} catch (_: InterruptedException) {}
|
||||
if (callbacks.remove(callId) != null) {
|
||||
val timeoutJson = JSONObject().put("result", JSONObject.NULL).put("logs", arrayOf<String>()).put("error", "timeout")
|
||||
try {
|
||||
client.getOutputStream().write((timeoutJson.toString() + "\u0000").toByteArray())
|
||||
client.close()
|
||||
} catch (_: Exception) {}
|
||||
}
|
||||
}
|
||||
|
||||
// eval
|
||||
evalJsWithConsoleCapture(code, callId)
|
||||
// wait briefly for response (not strictly necessary)
|
||||
synchronized(done) {
|
||||
if (!responded) {
|
||||
try { done.wait(evalTimeoutMs + 1000) } catch (_: InterruptedException) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun evalJsWithConsoleCapture(code: String, id: String) {
|
||||
// wrap code so we capture console logs and async results
|
||||
val escaped = code.replace("\\", "\\\\").replace("\n", "\\n").replace("\"", "\\\"")
|
||||
val wrapped = """
|
||||
(function(){
|
||||
(function(){
|
||||
var logs = [];
|
||||
var origLog = console.log;
|
||||
console.log = function(){
|
||||
try {
|
||||
var a = Array.prototype.slice.call(arguments).map(function(x){
|
||||
try { return typeof x === 'object' ? JSON.stringify(x) : String(x); } catch (e) { return String(x); }
|
||||
});
|
||||
logs.push(a.join(' '));
|
||||
} catch(e){}
|
||||
try{ origLog.apply(console, arguments); }catch(e){}
|
||||
};
|
||||
function send(obj){
|
||||
try{
|
||||
AndroidBridge.postResponse("$id", JSON.stringify(obj));
|
||||
}catch(e){}
|
||||
}
|
||||
try {
|
||||
var res = (function(){ return eval("$escaped"); })();
|
||||
// if Promise-like, wait for it
|
||||
if (res && typeof res.then === 'function') {
|
||||
res.then(function(v){ send({result: v==undefined? null: v, logs: logs, error: null}); })
|
||||
.catch(function(err){ send({result: null, logs: logs, error: String(err)}); });
|
||||
} else {
|
||||
send({result: res==undefined? null: res, logs: logs, error: null});
|
||||
}
|
||||
} catch(e) {
|
||||
send({result: null, logs: logs, error: String(e)});
|
||||
}
|
||||
})();
|
||||
})();
|
||||
""".trimIndent()
|
||||
|
||||
mainHandler.post {
|
||||
try {
|
||||
webView.evaluateJavascript(wrapped, null)
|
||||
} catch (e: Exception) {
|
||||
// immediate failure: return error
|
||||
callbacks.remove(id)?.invoke(JSONObject().put("result", JSONObject.NULL).put("logs", arrayOf<String>()).put("error", e.message))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun shutdown() {
|
||||
executor.shutdownNow()
|
||||
try { executor.awaitTermination(1, TimeUnit.SECONDS) } catch (_: Exception) {}
|
||||
mainHandler.post { webView.destroy() }
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@ import android.app.PendingIntent
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.os.Build
|
||||
|
|
@ -33,16 +32,14 @@ import com.deniscerri.ytdl.util.Extensions.getMediaDuration
|
|||
import com.deniscerri.ytdl.util.Extensions.toStringDuration
|
||||
import com.deniscerri.ytdl.util.FileUtil
|
||||
import com.deniscerri.ytdl.util.NotificationUtil
|
||||
import com.deniscerri.ytdl.util.extractors.YTDLPUtil
|
||||
import com.deniscerri.ytdl.util.extractors.ytdlp.YTDLPUtil
|
||||
import com.yausername.youtubedl_android.YoutubeDL
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.supervisorScope
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import java.io.File
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import com.deniscerri.ytdl.database.repository.ResultRepository
|
|||
import com.deniscerri.ytdl.util.Extensions.calculateNextTimeForObserving
|
||||
import com.deniscerri.ytdl.util.FileUtil
|
||||
import com.deniscerri.ytdl.util.NotificationUtil
|
||||
import com.deniscerri.ytdl.util.extractors.YTDLPUtil
|
||||
import com.deniscerri.ytdl.util.extractors.ytdlp.YTDLPUtil
|
||||
import com.google.gson.Gson
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
|
|
|||
Loading…
Reference in a new issue