feat: support exporting magnets
This commit is contained in:
parent
ab7bc15573
commit
b3f83816c5
3 changed files with 97 additions and 27 deletions
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue