From b3f83816c5ad41f0bddbf2262ed24bbea0be6dd1 Mon Sep 17 00:00:00 2001 From: TwistedUmbrellaX Date: Thu, 21 Mar 2024 18:28:19 -0400 Subject: [PATCH] feat: support exporting magnets --- .../ani/dantotsu/media/anime/ExoplayerView.kt | 1 - .../media/anime/SelectorDialogFragment.kt | 57 ++++++++++++++++ .../ani/dantotsu/parsers/AniyomiAdapter.kt | 66 +++++++++++-------- 3 files changed, 97 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt b/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt index ea95e077..d62e781a 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt @@ -54,7 +54,6 @@ import androidx.appcompat.app.AppCompatActivity import androidx.core.content.res.ResourcesCompat import androidx.core.math.MathUtils.clamp import androidx.core.view.isVisible -import androidx.core.view.WindowCompat import androidx.core.view.updateLayoutParams import androidx.lifecycle.lifecycleScope import androidx.media3.cast.CastPlayer diff --git a/app/src/main/java/ani/dantotsu/media/anime/SelectorDialogFragment.kt b/app/src/main/java/ani/dantotsu/media/anime/SelectorDialogFragment.kt index 22a7e097..7e271085 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/SelectorDialogFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/SelectorDialogFragment.kt @@ -3,6 +3,8 @@ package ani.dantotsu.media.anime import android.annotation.SuppressLint import android.app.Activity import android.app.AlertDialog +import android.content.ActivityNotFoundException +import android.content.ComponentName import android.content.DialogInterface import android.content.Intent import android.graphics.Color @@ -12,6 +14,8 @@ import android.util.TypedValue import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.activity.result.ActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.fragment.app.activityViewModels @@ -28,10 +32,12 @@ import ani.dantotsu.media.Media import ani.dantotsu.media.MediaDetailsViewModel import ani.dantotsu.others.Download.download import ani.dantotsu.parsers.Subtitle +import ani.dantotsu.parsers.Video import ani.dantotsu.parsers.VideoExtractor import ani.dantotsu.parsers.VideoType import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefName +import ani.dantotsu.util.Logger import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -212,11 +218,62 @@ class SelectorDialogFragment : BottomSheetDialogFragment() { super.onViewCreated(view, savedInstanceState) } + private val externalPlayerResult = registerForActivityResult( + ActivityResultContracts.StartActivityForResult()) { result: ActivityResult -> + Logger.log(result.data.toString()) + } + + private fun exportMagnetIntent(episode: Episode, video: Video) : Intent { + val amnis = "com.amnis" + return Intent(Intent.ACTION_VIEW).apply { + component = ComponentName(amnis, "$amnis.gui.player.PlayerActivity") + data = Uri.parse(video.file.url) + putExtra("title", "${media?.name} - ${episode.title}") + putExtra("position", 0) + putExtra(Intent.EXTRA_RETURN_RESULT, true) + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + putExtra("secure_uri", true) + val headersArray = arrayOf() + video.file.headers.forEach { + headersArray.plus(arrayOf(it.key, it.value)) + } + putExtra("headers", headersArray) + } + } + @SuppressLint("UnsafeOptInUsageError") fun startExoplayer(media: Media) { prevEpisode = null dismiss() + + episode?.let { ep -> + val video = ep.extractors?.find { + it.server.name == ep.selectedExtractor + }?.videos?.getOrNull(ep.selectedVideo) + video?.file?.url?.let { url -> + if (url.startsWith("magnet:")) { + try { + externalPlayerResult.launch(exportMagnetIntent(ep, video)) + } catch (e: ActivityNotFoundException) { + val amnis = "com.amnis" + try { + startActivity(Intent( + Intent.ACTION_VIEW, + Uri.parse("market://details?id=$amnis")) + ) + } catch (e: ActivityNotFoundException) { + startActivity(Intent( + Intent.ACTION_VIEW, + Uri.parse("https://play.google.com/store/apps/details?id=$amnis") + )) + } + } + return + } + } + } + if (launch!! || model.watchSources!!.isDownloadedSource(media.selected!!.sourceIndex)) { stopAddingToList() val intent = Intent(activity, ExoplayerView::class.java) diff --git a/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt b/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt index fc271de6..1fc6fba4 100644 --- a/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt +++ b/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt @@ -1,8 +1,11 @@ package ani.dantotsu.parsers +import android.content.ActivityNotFoundException +import android.content.ComponentName import android.content.ContentResolver import android.content.ContentValues import android.content.Context +import android.content.Intent import android.graphics.Bitmap import android.graphics.BitmapFactory import android.net.Uri @@ -12,6 +15,7 @@ import android.provider.MediaStore import ani.dantotsu.FileUrl import ani.dantotsu.currContext import ani.dantotsu.media.anime.AnimeNameAdapter +import ani.dantotsu.media.anime.ExoplayerView import ani.dantotsu.media.manga.ImageData import ani.dantotsu.media.manga.MangaCache import ani.dantotsu.snackString @@ -51,6 +55,7 @@ import uy.kohesive.injekt.api.get import java.io.File import java.io.FileOutputStream import java.io.UnsupportedEncodingException +import java.net.MalformedURLException import java.net.URL import java.net.URLDecoder import java.util.regex.Pattern @@ -691,35 +696,44 @@ class VideoServerPassthrough(val videoServer: VideoServer) : VideoExtractor() { // Check for null video URL val videoUrl = aniVideo.videoUrl ?: throw Exception("Video URL is null") - val urlObj = URL(videoUrl) - val path = urlObj.path - val query = urlObj.query + var format: VideoType? - var format = getVideoType(path) + try { + val urlObj = URL(videoUrl) + val path = urlObj.path + val query = urlObj.query - if (format == null && query != null) { - val queryPairs: List> = query.split("&").map { - val idx = it.indexOf("=") - val key = URLDecoder.decode(it.substring(0, idx), "UTF-8") - val value = URLDecoder.decode(it.substring(idx + 1), "UTF-8") - Pair(key, value) + format = getVideoType(path) + + if (format == null && query != null) { + val queryPairs: List> = query.split("&").map { + val idx = it.indexOf("=") + val key = URLDecoder.decode(it.substring(0, idx), "UTF-8") + val value = URLDecoder.decode(it.substring(idx + 1), "UTF-8") + Pair(key, value) + } + + // Assume the file is named under the "file" query parameter + val fileName = queryPairs.find { it.first == "file" }?.second ?: "" + + format = getVideoType(fileName) + // this solves a problem no one has, so I'm commenting it out for now + //if (format == null) { + // val networkHelper = Injekt.get() + // format = headRequest(videoUrl, networkHelper) + //} } - // Assume the file is named under the "file" query parameter - val fileName = queryPairs.find { it.first == "file" }?.second ?: "" - - format = getVideoType(fileName) - // this solves a problem no one has, so I'm commenting it out for now - //if (format == null) { - // val networkHelper = Injekt.get() - // format = headRequest(videoUrl, networkHelper) - //} - } - - // If the format is still undetermined, log an error - if (format == null) { - Logger.log("Unknown video format: $videoUrl") - format = VideoType.CONTAINER + // If the format is still undetermined, log an error + if (format == null) { + Logger.log("Unknown video format: $videoUrl") + format = VideoType.CONTAINER + } + } catch (malformed: MalformedURLException) { + if (videoUrl.startsWith("magnet:")) + format = VideoType.CONTAINER + else + throw malformed } val headersMap: Map = aniVideo.headers?.toMultimap()?.mapValues { it.value.joinToString() } ?: mapOf() @@ -727,7 +741,7 @@ class VideoServerPassthrough(val videoServer: VideoServer) : VideoExtractor() { return Video( number, - format, + format!!, FileUrl(videoUrl, headersMap), if (aniVideo.totalContentLength == 0L) null else aniVideo.bytesDownloaded.toDouble() )