feat: support exporting magnets

This commit is contained in:
TwistedUmbrellaX 2024-03-21 18:28:19 -04:00
parent ab7bc15573
commit b3f83816c5
3 changed files with 97 additions and 27 deletions

View file

@ -54,7 +54,6 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.core.math.MathUtils.clamp import androidx.core.math.MathUtils.clamp
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.WindowCompat
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.media3.cast.CastPlayer import androidx.media3.cast.CastPlayer

View file

@ -3,6 +3,8 @@ package ani.dantotsu.media.anime
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.app.AlertDialog import android.app.AlertDialog
import android.content.ActivityNotFoundException
import android.content.ComponentName
import android.content.DialogInterface import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.graphics.Color import android.graphics.Color
@ -12,6 +14,8 @@ import android.util.TypedValue
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
@ -28,10 +32,12 @@ import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsViewModel import ani.dantotsu.media.MediaDetailsViewModel
import ani.dantotsu.others.Download.download import ani.dantotsu.others.Download.download
import ani.dantotsu.parsers.Subtitle import ani.dantotsu.parsers.Subtitle
import ani.dantotsu.parsers.Video
import ani.dantotsu.parsers.VideoExtractor import ani.dantotsu.parsers.VideoExtractor
import ani.dantotsu.parsers.VideoType import ani.dantotsu.parsers.VideoType
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.util.Logger
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -212,11 +218,62 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
super.onViewCreated(view, savedInstanceState) 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<String>()
video.file.headers.forEach {
headersArray.plus(arrayOf(it.key, it.value))
}
putExtra("headers", headersArray)
}
}
@SuppressLint("UnsafeOptInUsageError") @SuppressLint("UnsafeOptInUsageError")
fun startExoplayer(media: Media) { fun startExoplayer(media: Media) {
prevEpisode = null prevEpisode = null
dismiss() 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)) { if (launch!! || model.watchSources!!.isDownloadedSource(media.selected!!.sourceIndex)) {
stopAddingToList() stopAddingToList()
val intent = Intent(activity, ExoplayerView::class.java) val intent = Intent(activity, ExoplayerView::class.java)

View file

@ -1,8 +1,11 @@
package ani.dantotsu.parsers package ani.dantotsu.parsers
import android.content.ActivityNotFoundException
import android.content.ComponentName
import android.content.ContentResolver import android.content.ContentResolver
import android.content.ContentValues import android.content.ContentValues
import android.content.Context import android.content.Context
import android.content.Intent
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.net.Uri import android.net.Uri
@ -12,6 +15,7 @@ import android.provider.MediaStore
import ani.dantotsu.FileUrl import ani.dantotsu.FileUrl
import ani.dantotsu.currContext import ani.dantotsu.currContext
import ani.dantotsu.media.anime.AnimeNameAdapter import ani.dantotsu.media.anime.AnimeNameAdapter
import ani.dantotsu.media.anime.ExoplayerView
import ani.dantotsu.media.manga.ImageData import ani.dantotsu.media.manga.ImageData
import ani.dantotsu.media.manga.MangaCache import ani.dantotsu.media.manga.MangaCache
import ani.dantotsu.snackString import ani.dantotsu.snackString
@ -51,6 +55,7 @@ import uy.kohesive.injekt.api.get
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.UnsupportedEncodingException import java.io.UnsupportedEncodingException
import java.net.MalformedURLException
import java.net.URL import java.net.URL
import java.net.URLDecoder import java.net.URLDecoder
import java.util.regex.Pattern import java.util.regex.Pattern
@ -691,35 +696,44 @@ class VideoServerPassthrough(val videoServer: VideoServer) : VideoExtractor() {
// Check for null video URL // Check for null video URL
val videoUrl = aniVideo.videoUrl ?: throw Exception("Video URL is null") val videoUrl = aniVideo.videoUrl ?: throw Exception("Video URL is null")
val urlObj = URL(videoUrl) var format: VideoType?
val path = urlObj.path
val query = urlObj.query
var format = getVideoType(path) try {
val urlObj = URL(videoUrl)
val path = urlObj.path
val query = urlObj.query
if (format == null && query != null) { format = getVideoType(path)
val queryPairs: List<Pair<String, String>> = query.split("&").map {
val idx = it.indexOf("=") if (format == null && query != null) {
val key = URLDecoder.decode(it.substring(0, idx), "UTF-8") val queryPairs: List<Pair<String, String>> = query.split("&").map {
val value = URLDecoder.decode(it.substring(idx + 1), "UTF-8") val idx = it.indexOf("=")
Pair(key, value) 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<NetworkHelper>()
// format = headRequest(videoUrl, networkHelper)
//}
} }
// Assume the file is named under the "file" query parameter // If the format is still undetermined, log an error
val fileName = queryPairs.find { it.first == "file" }?.second ?: "" if (format == null) {
Logger.log("Unknown video format: $videoUrl")
format = getVideoType(fileName) format = VideoType.CONTAINER
// this solves a problem no one has, so I'm commenting it out for now }
//if (format == null) { } catch (malformed: MalformedURLException) {
// val networkHelper = Injekt.get<NetworkHelper>() if (videoUrl.startsWith("magnet:"))
// format = headRequest(videoUrl, networkHelper) format = VideoType.CONTAINER
//} else
} throw malformed
// If the format is still undetermined, log an error
if (format == null) {
Logger.log("Unknown video format: $videoUrl")
format = VideoType.CONTAINER
} }
val headersMap: Map<String, String> = val headersMap: Map<String, String> =
aniVideo.headers?.toMultimap()?.mapValues { it.value.joinToString() } ?: mapOf() aniVideo.headers?.toMultimap()?.mapValues { it.value.joinToString() } ?: mapOf()
@ -727,7 +741,7 @@ class VideoServerPassthrough(val videoServer: VideoServer) : VideoExtractor() {
return Video( return Video(
number, number,
format, format!!,
FileUrl(videoUrl, headersMap), FileUrl(videoUrl, headersMap),
if (aniVideo.totalContentLength == 0L) null else aniVideo.bytesDownloaded.toDouble() if (aniVideo.totalContentLength == 0L) null else aniVideo.bytesDownloaded.toDouble()
) )