1.7.9.1 fr

This commit is contained in:
zaednasr 2024-09-01 23:00:51 +02:00
parent 4a6b8f450d
commit defe14f3cc
No known key found for this signature in database
GPG key ID: 92B1DE23AD3D0E9E
11 changed files with 160 additions and 79 deletions

View file

@ -16,6 +16,8 @@ import com.deniscerri.ytdl.util.Extensions.isYoutubeChannelURL
import com.deniscerri.ytdl.util.Extensions.isYoutubeURL
import com.deniscerri.ytdl.util.FileUtil
import com.deniscerri.ytdl.util.extractors.GoogleApiUtil
import com.deniscerri.ytdl.util.extractors.IYoutubeExtractor
import com.deniscerri.ytdl.util.extractors.PipedApiUtil
import com.deniscerri.ytdl.util.extractors.newpipe.NewPipeUtil
import com.deniscerri.ytdl.util.extractors.YTDLPUtil
import com.deniscerri.ytdl.util.extractors.YoutubeApiUtil
@ -33,12 +35,20 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
}
private val youtubeApiUtil = YoutubeApiUtil(context)
//private val pipedApiUtil = PipedApiUtil(context)
private val newPipeUtil = NewPipeUtil(context)
private val ytdlpUtil = YTDLPUtil(context)
private var youtubeExtractor: IYoutubeExtractor? = null
private var pipedUtil = PipedApiUtil(context)
private var newPipeUtil = NewPipeUtil(context)
private val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
init {
youtubeExtractor = when(YoutubeExtractors.valueOf(sharedPreferences.getString("data_fetching_extractor_youtube", "PIPED")!!)) {
YoutubeExtractors.PIPED -> PipedApiUtil(context)
YoutubeExtractors.NEWPIPE -> NewPipeUtil(context)
else -> null
}
}
enum class SourceType {
YOUTUBE_VIDEO,
YOUTUBE_PLAYLIST,
@ -47,6 +57,12 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
YT_DLP
}
enum class YoutubeExtractors {
YT_DLP,
PIPED,
NEWPIPE
}
suspend fun insert(it: ResultItem){
resultDao.insert(it)
}
@ -60,7 +76,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
val items = if (sharedPreferences.getString("api_key", "")!!.isNotBlank()) {
youtubeApiUtil.getTrending()
}else{
newPipeUtil.getTrending()
youtubeExtractor?.getTrending() ?: listOf()
}
itemCount.value = items.size
@ -72,27 +88,27 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
}
fun getStreamingUrlAndChapters(url: String) : Pair<List<String>, List<ChapterItem>?> {
val newPipeTrial = newPipeUtil.getStreamingUrlAndChapters(url)
if (newPipeTrial.isFailure){
val extractorsTrial = youtubeExtractor?.getStreamingUrlAndChapters(url) ?: Result.failure(Throwable())
if (extractorsTrial.isFailure){
val res = ytdlpUtil.getStreamingUrlAndChapters(url)
return res.getOrDefault(Pair(listOf(""), null))
}
return newPipeTrial.getOrNull()!!
return extractorsTrial.getOrNull()!!
}
suspend fun search(inputQuery: String, resetResults: Boolean, addToResults: Boolean) : ArrayList<ResultItem>{
if (resetResults) deleteAll()
val res = when(sharedPreferences.getString("search_engine", "ytsearch")) {
"ytsearch" -> newPipeUtil.search(inputQuery)
"ytsearchmusic" -> newPipeUtil.searchMusic(inputQuery)
"ytsearch" -> youtubeExtractor?.search(inputQuery)
"ytsearchmusic" -> youtubeExtractor?.searchMusic(inputQuery)
else -> Result.failure(Throwable())
}
val items = if (res.isSuccess) {
val items = if (res?.isSuccess == true) {
res.getOrNull()!!
}else{
//fallback if newpipe failed
//fallback to yt-dlp
ytdlpUtil.getFromYTDL(inputQuery)
}
@ -108,10 +124,10 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
private suspend fun getYoutubeVideo(inputQuery: String, resetResults: Boolean, addToResults: Boolean) : List<ResultItem>{
val theURL = inputQuery.replace("\\?list.*".toRegex(), "")
val newPipeRes = newPipeUtil.getVideoData(theURL)
val ytExtractorRes = youtubeExtractor?.getVideoData(theURL)
val res = if (newPipeRes.isSuccess) {
newPipeRes.getOrNull()!!
val res = if (ytExtractorRes?.isSuccess == true) {
ytExtractorRes.getOrNull()!!
}else{
ytdlpUtil.getFromYTDL(inputQuery)
}
@ -136,7 +152,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
val playlistURL = "https://youtube.com/playlist?list=${id}"
if (resetResults) deleteAll()
val items = mutableListOf<ResultItem>()
val newPipeResult = newPipeUtil.getPlaylistData(playlistURL) {
val ytExtractorResult = youtubeExtractor?.getPlaylistData(playlistURL) {
if (addToResults){
runBlocking {
val ids = resultDao.insertMultiple(it)
@ -148,8 +164,8 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
items.addAll(it)
}
val response = if (newPipeResult.isSuccess){
newPipeResult.getOrElse { items }
val response = if (ytExtractorResult?.isSuccess == true){
ytExtractorResult.getOrElse { items }
}else{
val res = ytdlpUtil.getFromYTDL(playlistURL)
val ids = resultDao.insertMultiple(res)
@ -166,7 +182,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
private suspend fun getYoutubeChannel(url: String, resetResults: Boolean, addToResults: Boolean) : List<ResultItem>{
if (resetResults) deleteAll()
val items = mutableListOf<ResultItem>()
val newPipeResult = newPipeUtil.getChannelData(url) {
val ytExtractorResult = youtubeExtractor?.getChannelData(url) {
if (addToResults){
runBlocking {
val ids = resultDao.insertMultiple(it)
@ -178,8 +194,8 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
items.addAll(it)
}
val response = if (newPipeResult.isSuccess){
newPipeResult.getOrElse { items }
val response = if (ytExtractorResult?.isSuccess == true){
ytExtractorResult.getOrElse { items }
}else{
val res = ytdlpUtil.getFromYTDL(url)
val ids = resultDao.insertMultiple(res)
@ -214,12 +230,25 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
fun getFormats(url: String, source : String? = null) : List<Format> {
val formatSource = source ?: sharedPreferences.getString("formats_source", "yt-dlp")
val res = if (url.isYoutubeURL() && formatSource == "piped") {
val tmpRes = newPipeUtil.getFormats(url)
if (tmpRes.isFailure && source != null) {
Result.success(listOf())
}else{
tmpRes
val res = if (url.isYoutubeURL()) {
when(formatSource) {
"piped" -> {
val tmpRes = pipedUtil.getFormats(url)
if (tmpRes.isFailure && source != null) {
Result.success(listOf())
}else{
tmpRes
}
}
"newpipe" -> {
val tmpRes = newPipeUtil.getFormats(url)
if (tmpRes.isFailure && source != null) {
Result.success(listOf())
}else{
tmpRes
}
}
else -> Result.failure(Throwable())
}
}else{
Result.failure(Throwable())
@ -235,18 +264,40 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
suspend fun getFormatsMultiple(urls: List<String>, source: String? = null, progress: (progress: ResultViewModel.MultipleFormatProgress) -> Unit) : MutableList<MutableList<Format>> {
val formatSource = source ?: sharedPreferences.getString("formats_source", "yt-dlp")
val allYoutubeLinks = urls.all { it.isYoutubeURL() }
if (allYoutubeLinks && formatSource == "piped") {
val res = newPipeUtil.getFormatsForAll(urls) {
progress(it)
val res = when(formatSource) {
"piped" -> {
if (!allYoutubeLinks) {
Result.failure(Throwable())
}else{
val res = pipedUtil.getFormatsForAll(urls) {
progress(it)
}
res
}
}
"newpipe" -> {
val res = newPipeUtil.getFormatsForAll(urls) {
progress(it)
}
res
}
else -> {
Result.failure(Throwable())
}
}
if (res.isSuccess) {
return res.getOrElse { mutableListOf() }
}
else {
val res = ytdlpUtil.getFormatsForAll(urls) {
progress(it)
}
return res.getOrElse { mutableListOf() }
//last fallback
val ytdlpRes = ytdlpUtil.getFormatsForAll(urls) {
progress(it)
}
return ytdlpRes.getOrElse { mutableListOf() }
}
suspend fun delete(item: ResultItem){

View file

@ -106,7 +106,7 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
sideDetails.add(item.container.uppercase().ifEmpty { item.format.container.uppercase() })
val fileSize = FileUtil.convertFileSize(item.format.filesize)
if (fileSize != "?") sideDetails.add(fileSize)
if (fileSize != "?" && item.downloadSections.isBlank()) sideDetails.add(fileSize)
formatDetailsChip.text = sideDetails.filter { it.isNotBlank() }.joinToString(" · ")
//OUTPUT

View file

@ -125,7 +125,7 @@ class ActiveDownloadMinifiedAdapter(onItemClickListener: OnItemClickListener, ac
val fileSize = card.findViewById<TextView>(R.id.file_size)
val fileSizeReadable = FileUtil.convertFileSize(item.format.filesize)
if (fileSizeReadable == "?") fileSize.visibility = View.GONE
if (fileSizeReadable == "?" && item.downloadSections.isNotBlank()) fileSize.visibility = View.GONE
else fileSize.text = fileSizeReadable
val menu = card.findViewById<View>(R.id.options)

View file

@ -1571,12 +1571,10 @@ object UiUtil {
fun handleNoResults(context: Activity, message: String, continueAnyway: Boolean = false, continued: () -> Unit, closed: () -> Unit) {
val updateYTDLP = "<span style='color:#d43c3b';>${context.getString(R.string.update_ytdl)}</span>"
.parseAsHtml(HtmlCompat.FROM_HTML_MODE_COMPACT)
val errDialog = MaterialAlertDialogBuilder(context)
.setTitle(R.string.no_results)
.setMessage(message + "\n\n" + updateYTDLP)
.setMessage(message)
errDialog.setPositiveButton(R.string.copy_log) { d:DialogInterface?, _:Int ->
val clipboard: ClipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
@ -1594,23 +1592,7 @@ object UiUtil {
closed()
}
val dialog = errDialog.show()
dialog.findViewById<View>(android.R.id.message)?.setOnClickListener {
dialog.cancel()
CoroutineScope(Dispatchers.IO).launch {
val res = UpdateUtil(context).updateYoutubeDL()
if (res == YoutubeDL.UpdateStatus.DONE) {
val version = YoutubeDL.getInstance().version(context)
withContext(Dispatchers.Main){
Toast.makeText(context, context.getString(R.string.ytld_update_success) + " [${version}]", Toast.LENGTH_LONG).show()
}
}else {
withContext(Dispatchers.Main) {
Toast.makeText(context, context.getString(R.string.you_are_in_latest_version), Toast.LENGTH_SHORT).show()
}
}
}
}
errDialog.show()
}
fun showErrorDialog(context: Context, it: String){

View file

@ -0,0 +1,18 @@
package com.deniscerri.ytdl.util.extractors
import com.deniscerri.ytdl.database.models.ChapterItem
import com.deniscerri.ytdl.database.models.Format
import com.deniscerri.ytdl.database.models.ResultItem
import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
interface IYoutubeExtractor {
fun getVideoData(url: String) : Result<List<ResultItem>>
fun getFormats(url: String) : Result<List<Format>>
fun getFormatsForAll(urls: List<String>, progress: (progress: ResultViewModel.MultipleFormatProgress) -> Unit) : Result<MutableList<MutableList<Format>>>
fun search(query: String) : Result<ArrayList<ResultItem>>
fun searchMusic(query: String) : Result<ArrayList<ResultItem>>
fun getStreamingUrlAndChapters(url: String) : Result<Pair<List<String>, List<ChapterItem>?>>
suspend fun getPlaylistData(id: String, progress: (pagedResults: MutableList<ResultItem>) -> Unit) : Result<List<ResultItem>>
fun getTrending() : ArrayList<ResultItem>
fun getChannelData(url: String, progress: (pagedResults: MutableList<ResultItem>) -> Unit) : Result<List<ResultItem>>
}

View file

@ -19,7 +19,7 @@ import java.util.ArrayList
import java.util.Locale
import kotlin.coroutines.cancellation.CancellationException
class PipedApiUtil(private val context: Context) {
class PipedApiUtil(context: Context) : IYoutubeExtractor {
private var sharedPreferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
private val countryCode = sharedPreferences.getString("locale", "")!!.ifEmpty { "US" }
private val defaultPipedURL = "https://pipedapi.kavin.rocks/"
@ -38,7 +38,7 @@ class PipedApiUtil(private val context: Context) {
return listOf()
}
fun getVideoData(url : String) : Result<List<ResultItem>> {
override fun getVideoData(url : String) : Result<List<ResultItem>> {
val id = getIDFromYoutubeURL(url)
val res = NetworkUtil.genericRequest("$pipedURL/streams/$id")
if (res.length() == 0) {
@ -49,7 +49,7 @@ class PipedApiUtil(private val context: Context) {
return Result.success(listOf(vid))
}
fun getFormats(url: String) : Result<List<Format> > {
override fun getFormats(url: String) : Result<List<Format> > {
try {
val id = getIDFromYoutubeURL(url)
val res = NetworkUtil.genericRequest("$pipedURL/streams/$id")
@ -67,7 +67,7 @@ class PipedApiUtil(private val context: Context) {
}
}
fun getFormatsForAll(urls: List<String>, progress: (progress: ResultViewModel.MultipleFormatProgress) -> Unit) : Result<MutableList<MutableList<Format>>> {
override fun getFormatsForAll(urls: List<String>, progress: (progress: ResultViewModel.MultipleFormatProgress) -> Unit) : Result<MutableList<MutableList<Format>>> {
return kotlin.runCatching {
val formatCollection = mutableListOf<MutableList<Format>>()
urls.forEach { url ->
@ -88,7 +88,7 @@ class PipedApiUtil(private val context: Context) {
}
@Throws(JSONException::class)
fun search(query: String): Result<ArrayList<ResultItem>> {
override fun search(query: String): Result<ArrayList<ResultItem>> {
val items = arrayListOf<ResultItem>()
val data = NetworkUtil.genericRequest("$pipedURL/search?q=$query&filter=videos&region=${countryCode}")
val dataArray = data.getJSONArray("items")
@ -107,7 +107,7 @@ class PipedApiUtil(private val context: Context) {
}
@Throws(JSONException::class)
fun searchMusic(query: String): Result<ArrayList<ResultItem>> {
override fun searchMusic(query: String): Result<ArrayList<ResultItem>> {
val items = arrayListOf<ResultItem>()
val data = NetworkUtil.genericRequest("$pipedURL/search?q=$query=&filter=music_songs&region=${countryCode}")
val dataArray = data.getJSONArray("items")
@ -125,7 +125,7 @@ class PipedApiUtil(private val context: Context) {
return Result.success(items)
}
fun getStreamingUrlAndChapters(url: String) : Result<Pair<List<String>, List<ChapterItem>?>> {
override fun getStreamingUrlAndChapters(url: String) : Result<Pair<List<String>, List<ChapterItem>?>> {
val id = getIDFromYoutubeURL(url)
val res = NetworkUtil.genericRequest("$pipedURL/streams/$id")
if (res.length() == 0) {
@ -140,7 +140,7 @@ class PipedApiUtil(private val context: Context) {
}
}
suspend fun getPlaylistData(id: String, progress: (pagedResults: MutableList<ResultItem>) -> Unit) : Result<List<ResultItem>> {
override suspend fun getPlaylistData(id: String, progress: (pagedResults: MutableList<ResultItem>) -> Unit) : Result<List<ResultItem>> {
val totalItems = mutableListOf<ResultItem>()
val nextPageToken = ""
var playlistName = ""
@ -194,7 +194,7 @@ class PipedApiUtil(private val context: Context) {
}
fun getTrending(): ArrayList<ResultItem> {
override fun getTrending(): ArrayList<ResultItem> {
val items = arrayListOf<ResultItem>()
val url = "$pipedURL/trending?region=${countryCode}"
val res = NetworkUtil.genericArrayRequest(url)
@ -209,6 +209,13 @@ class PipedApiUtil(private val context: Context) {
return items
}
override fun getChannelData(
url: String,
progress: (pagedResults: MutableList<ResultItem>) -> Unit
): Result<List<ResultItem>> {
return Result.failure(Throwable("Not yet implemented / supported?"))
}
private fun createVideoFromPipedJSON(obj: JSONObject, url: String, ignoreFormatPreference : Boolean = false): ResultItem? {
var video: ResultItem? = null
try {

View file

@ -9,6 +9,7 @@ import com.deniscerri.ytdl.database.models.Format
import com.deniscerri.ytdl.database.models.ResultItem
import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
import com.deniscerri.ytdl.util.Extensions.toStringDuration
import com.deniscerri.ytdl.util.extractors.IYoutubeExtractor
import com.google.gson.Gson
import kotlinx.serialization.Serializer
import okhttp3.OkHttpClient
@ -38,7 +39,7 @@ import org.schabi.newpipe.extractor.utils.ExtractorHelper
import java.util.Locale
import kotlin.coroutines.cancellation.CancellationException
class NewPipeUtil(context: Context) {
class NewPipeUtil(context: Context) : IYoutubeExtractor {
private var sharedPreferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
private val countryCode = sharedPreferences.getString("locale", "")!!.ifEmpty { "US" }
private val language = sharedPreferences.getString("app_language", "")!!.ifEmpty { "en" }
@ -46,7 +47,7 @@ class NewPipeUtil(context: Context) {
NewPipe.init(NewPipeDownloaderImpl(OkHttpClient.Builder()), Localization(language, countryCode))
}
fun getVideoData(url : String) : Result<List<ResultItem>> {
override fun getVideoData(url : String) : Result<List<ResultItem>> {
try {
val streamInfo = StreamInfo.getInfo(NewPipe.getService(ServiceList.YouTube.serviceId), url)
val vid = createVideoFromStream(streamInfo, url) ?: return Result.failure(Throwable())
@ -56,7 +57,7 @@ class NewPipeUtil(context: Context) {
}
}
fun getFormats(url: String) : Result<List<Format> > {
override fun getFormats(url: String) : Result<List<Format> > {
try {
val streamInfo = StreamInfo.getInfo(NewPipe.getService(ServiceList.YouTube.serviceId), url)
val vid = createVideoFromStream(streamInfo, url)
@ -68,7 +69,7 @@ class NewPipeUtil(context: Context) {
}
}
fun getFormatsForAll(urls: List<String>, progress: (progress: ResultViewModel.MultipleFormatProgress) -> Unit) : Result<MutableList<MutableList<Format>>> {
override fun getFormatsForAll(urls: List<String>, progress: (progress: ResultViewModel.MultipleFormatProgress) -> Unit) : Result<MutableList<MutableList<Format>>> {
return kotlin.runCatching {
val formatCollection = mutableListOf<MutableList<Format>>()
urls.forEach { url ->
@ -85,7 +86,7 @@ class NewPipeUtil(context: Context) {
}
@Throws(JSONException::class)
fun search(query: String): Result<ArrayList<ResultItem>> {
override fun search(query: String): Result<ArrayList<ResultItem>> {
try {
val items = arrayListOf<ResultItem>()
val res = SearchInfo.getInfo(NewPipe.getService(ServiceList.YouTube.serviceId),
@ -111,7 +112,7 @@ class NewPipeUtil(context: Context) {
}
@Throws(JSONException::class)
fun searchMusic(query: String): Result<ArrayList<ResultItem>> {
override fun searchMusic(query: String): Result<ArrayList<ResultItem>> {
try {
val items = arrayListOf<ResultItem>()
val res = SearchInfo.getInfo(NewPipe.getService(ServiceList.YouTube.serviceId),
@ -135,7 +136,7 @@ class NewPipeUtil(context: Context) {
}
}
fun getStreamingUrlAndChapters(url: String) : Result<Pair<List<String>, List<ChapterItem>?>> {
override fun getStreamingUrlAndChapters(url: String) : Result<Pair<List<String>, List<ChapterItem>?>> {
try {
val streamInfo = StreamInfo.getInfo(NewPipe.getService(ServiceList.YouTube.serviceId), url)
val item = createVideoFromStream(streamInfo, url)
@ -148,7 +149,7 @@ class NewPipeUtil(context: Context) {
}
}
fun getChannelData(url: String, progress: (pagedResults: MutableList<ResultItem>) -> Unit) : Result<List<ResultItem>> {
override fun getChannelData(url: String, progress: (pagedResults: MutableList<ResultItem>) -> Unit) : Result<List<ResultItem>> {
try {
val req = ChannelInfo.getInfo(ServiceList.YouTube, url)
println(Gson().toJson(req))
@ -214,7 +215,7 @@ class NewPipeUtil(context: Context) {
}
}
fun getPlaylistData(playlistURL: String, progress: (pagedResults: MutableList<ResultItem>) -> Unit) : Result<List<ResultItem>> {
override suspend fun getPlaylistData(playlistURL: String, progress: (pagedResults: MutableList<ResultItem>) -> Unit) : Result<List<ResultItem>> {
try {
val totalItems = mutableListOf<ResultItem>()
var nextPage : Page? = null
@ -261,7 +262,7 @@ class NewPipeUtil(context: Context) {
}
fun getTrending(): ArrayList<ResultItem> {
override fun getTrending(): ArrayList<ResultItem> {
try {
val items = arrayListOf<ResultItem>()
val info = KioskInfo.getInfo(NewPipe.getService(ServiceList.YouTube.serviceId), "https://www.youtube.com/feed/trending")
@ -434,7 +435,4 @@ class NewPipeUtil(context: Context) {
query = el[0]
return query!!
}
}

View file

@ -235,12 +235,14 @@
</array>
<array name="formats_source">
<item>Piped</item>
<item>NewPipe</item>
<item>yt-dlp</item>
</array>
<array name="formats_source_values">
<item>piped</item>
<item>newpipe</item>
<item>yt-dlp</item>
</array>
@ -1494,4 +1496,10 @@
<item>monthly</item>
</string-array>
<string-array name="data_fetching_extractor_youtube">
<item>YT_DLP</item>
<item>NEWPIPE</item>
<item>PIPED</item>
</string-array>
</resources>

View file

@ -421,4 +421,5 @@
<string name="continue_anyway">Continue Anyway</string>
<string name="playlist_as_album">Use Playlist Name as Album Metadata</string>
<string name="playlist_as_album_summary">If Album Metadata is not present, use playlist name instead</string>
<string name="data_fetching_extractor_youtube">Data Fetching Extractor (YouTube)</string>
</resources>

View file

@ -84,6 +84,15 @@
android:summary="@string/api_key_summary"
app:title="@string/api_key" />
<ListPreference
android:defaultValue="NEWPIPE"
android:entries="@array/data_fetching_extractor_youtube"
android:entryValues="@array/data_fetching_extractor_youtube"
android:icon="@drawable/baseline_manage_search_24"
app:key="data_fetching_extractor_youtube"
app:useSimpleSummaryProvider="true"
app:title="@string/data_fetching_extractor_youtube" />
<ListPreference
android:defaultValue="yt-dlp"
android:entries="@array/formats_source"

View file

@ -9,7 +9,8 @@ Since Piped is currently broken and unusable, i implemented the NEWPIPE EXTRACTO
- searching
- youtube channel queries
- formats
The speed is as fast as piped and seems to be working. Will use that for the forseeable future, until further notice
The speed is as fast as piped and seems to be working.
Now you can select between piped, newpipe or strictly rely on yt-dlp
## Home screen filtering
@ -37,10 +38,16 @@ https://github.com/yt-dlp/yt-dlp/issues/10827
- Prevented app selecting bad formats 233 and 234 when quick downloading
- Fixed app not showing best quality and worst quality labels depending on the user language
- Fixed app crashing when selecting formats of an instagram post with the same url
- Added approx filesize in yt-dlp formats, somehow the app was not adding it lol
- Added ability to remember text wrapping in download logs and terminal
- if quick download enabled, added ability to show the download card while sharing the txt file on any download type
- hide download size when enabling cutting because the size is unknown
## Note
Some people are asking for the ability to not use cache while downloading in usb storage. This is an android limitation. Even though you might have all files access, android still doesnt consider usb storage as main storage and cant directly write to it. So i have to use caching and then transfer the file.
If anyone is knowledgeable to pull this off, dm me on telegram and we can make it happen. :)
Also for people cutting really long videos and its slow. Its a yt-dlp / ffmpeg issue of how they handle that, so it can't be helped. I guess its best to just download it fully and cut it somewhere else.
I could've released this sooner but i have been busy :/