From d394f0e1d0916718d96517aff11ad8f2ada6010f Mon Sep 17 00:00:00 2001 From: LagradOst <11805592+LagradOst@users.noreply.github.com> Date: Thu, 14 Sep 2023 18:46:34 +0200 Subject: [PATCH 1/6] torrent testing --- .../lagradost/cloudstream3/MainActivity.kt | 33 +- .../ui/player/AbstractPlayerFragment.kt | 5 + .../cloudstream3/ui/player/CS3IPlayer.kt | 303 +++++++++++++++++- .../cloudstream3/ui/player/GeneratorPlayer.kt | 62 +++- .../cloudstream3/ui/player/IGenerator.kt | 5 +- .../cloudstream3/ui/player/IPlayer.kt | 13 +- .../cloudstream3/utils/ExtractorApi.kt | 2 +- app/src/main/res/layout/fragment_player.xml | 143 +++++++-- 8 files changed, 511 insertions(+), 55 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index a07ae2c28cd..44d078e0c63 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -94,6 +94,7 @@ import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO import com.lagradost.cloudstream3.ui.home.HomeViewModel import com.lagradost.cloudstream3.ui.player.BasicLink +import com.lagradost.cloudstream3.ui.player.ExtractorLinkGenerator import com.lagradost.cloudstream3.ui.player.GeneratorPlayer import com.lagradost.cloudstream3.ui.player.LinkGenerator import com.lagradost.cloudstream3.ui.result.LinearListLayout @@ -130,8 +131,11 @@ import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.DataStoreHelper.migrateResumeWatching import com.lagradost.cloudstream3.utils.Event +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.INFER_TYPE import com.lagradost.cloudstream3.utils.IOnBackPressed import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate +import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog import com.lagradost.cloudstream3.utils.UIHelper.changeStatusBarState import com.lagradost.cloudstream3.utils.UIHelper.checkWrite @@ -1112,16 +1116,17 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { newLocalBinding.root.viewTreeObserver.addOnGlobalFocusChangeListener { _, newFocus -> // println("refocus $oldFocus -> $newFocus") try { - val r = Rect(0,0,0,0) + val r = Rect(0, 0, 0, 0) newFocus.getDrawingRect(r) val x = r.centerX() val y = r.centerY() val dx = 0 //screenWidth / 2 val dy = screenHeight / 2 - val r2 = Rect(x-dx,y-dy,x+dx,y+dy) + val r2 = Rect(x - dx, y - dy, x + dx, y + dy) newFocus.requestRectangleOnScreen(r2, false) - // TvFocus.current =TvFocus.current.copy(y=y.toFloat()) - } catch (_ : Throwable) { } + // TvFocus.current =TvFocus.current.copy(y=y.toFloat()) + } catch (_: Throwable) { + } TvFocus.updateFocusView(newFocus) /*var focus = newFocus @@ -1562,6 +1567,26 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { setKey(HAS_DONE_SETUP_KEY, true) } + /*val defaultDirectory = "${filesDir.path}/torrent_tmp" + //File(defaultDirectory).deleteRecursively() + navigate( + R.id.global_to_navigation_player, GeneratorPlayer.newInstance( + ExtractorLinkGenerator( + listOf( + ExtractorLink( + source = "", + name = "hello world", + url = "", + "", + Qualities.Unknown.value, + type = INFER_TYPE + ) + ), + emptyList() + ) + ) + )*/ + // Used to check current focus for TV // main { // while (true) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt index 8388e58f7b1..efa03bd49c4 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt @@ -370,12 +370,17 @@ abstract class AbstractPlayerFragment( // } //} + open fun onDownload(event : DownloadEvent) = Unit + /** This receives the events from the player, if you want to append functionality you do it here, * do note that this only receives events for UI changes, * and returning early WONT stop it from changing in eg the player time or pause status */ open fun mainCallback(event : PlayerEvent) { Log.i(TAG, "Handle event: $event") when(event) { + is DownloadEvent -> { + onDownload(event) + } is ResizedEvent -> { playerDimensionsLoaded(event.width, event.height) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt index 331cfb73619..b44ce003b72 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt @@ -1,6 +1,7 @@ package com.lagradost.cloudstream3.ui.player import android.annotation.SuppressLint +import android.app.Activity import android.content.Context import android.net.Uri import android.os.Handler @@ -8,6 +9,7 @@ import android.os.Looper import android.util.Log import android.util.Rational import android.widget.FrameLayout +import androidx.core.net.toUri import androidx.media3.common.C.* import androidx.media3.common.Format import androidx.media3.common.MediaItem @@ -49,6 +51,8 @@ import androidx.preference.PreferenceManager import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey +import com.lagradost.cloudstream3.CommonActivity +import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.USER_AGENT import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.mvvm.debugAssert @@ -63,6 +67,15 @@ import com.lagradost.cloudstream3.utils.ExtractorLinkPlayList import com.lagradost.cloudstream3.utils.ExtractorLinkType import com.lagradost.cloudstream3.utils.ExtractorUri import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage +import com.lagradost.fetchbutton.aria2c.Aria2Settings +import com.lagradost.fetchbutton.aria2c.Aria2Starter +import com.lagradost.fetchbutton.aria2c.DownloadListener +import com.lagradost.fetchbutton.aria2c.DownloadStatusTell +import com.lagradost.fetchbutton.aria2c.newUriRequest +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import java.io.File import java.lang.IllegalArgumentException import java.util.UUID @@ -88,7 +101,9 @@ class CS3IPlayer : IPlayer { private var exoPlayer: ExoPlayer? = null set(value) { // If the old value is not null then the player has not been properly released. - debugAssert({ field != null && value != null }, { "Previous player instance should be released!" }) + debugAssert( + { field != null && value != null }, + { "Previous player instance should be released!" }) field = value } @@ -182,6 +197,273 @@ class CS3IPlayer : IPlayer { subtitleHelper.initSubtitles(subView, subHolder, style) } + private fun getPlayableFile( + data: com.lagradost.fetchbutton.aria2c.Metadata, + minimumBytes: Long + ): Uri? { + for (item in data.items) { + for (file in item.files) { + // only allow files with a length above minimumBytes + if (file.completedLength < minimumBytes) continue + // only allow video formats + if (videoFormats.none { suf -> + file.path.contains( + suf, + ignoreCase = true + ) + }) continue + + return file.path.toUri() + } + } + return null + } + + private val videoFormats = arrayOf( + ".3g2", + ".3gp", + ".amv", + ".asf", + ".avi", + ".drc", + ".flv", + ".f4v", + ".f4p", + ".f4a", + ".f4b", + ".gif", + ".gifv", + ".m4v", + ".mkv", + ".mng", + ".mov", + ".qt", + ".mp4", + ".m4p", + ".mpg", ".mp2", ".mpeg", ".mpe", ".mpv", + ".mpg", ".mpeg", ".m2v", + ".MTS", ".M2TS", ".TS", + ".mxf", + ".nsv", + ".ogv", ".ogg", + //".rm", // Made for RealPlayer + //".rmvb", // Made for RealPlayer + ".svi", + ".viv", + ".vob", + ".webm", + ".wmv", + ".yuv" + ) + + @Throws + private suspend fun awaitAria2c( + activity: Activity, + link: ExtractorLink, + requestId: Long, + ) { + var hasFileChecked = false + while (true) { + val gid = DownloadListener.sessionIdToGid[requestId] + + // request has not yet been processed, wait for it to do + if (gid == null) { + delay(1000) + continue + } + + val metadata = DownloadListener.getInfo(gid) + event( + DownloadEvent( + downloadedBytes = metadata.downloadedLength, + downloadSpeed = metadata.downloadSpeed, + totalBytes = metadata.totalLength, + connections = metadata.items.sumOf { it.connections } + ) + ) + when (metadata.status) { + // if completed/error/removed then we don't have to wait anymore + DownloadStatusTell.Complete, + DownloadStatusTell.Error, + DownloadStatusTell.Removed -> break + + // if waiting to be added, wait more + DownloadStatusTell.Waiting -> { + delay(1000) + continue + } + + DownloadStatusTell.Active -> { + if (metadata.downloadedLength >= metadata.totalLength && getPlayableFile( + metadata, + minimumBytes = 10 shl 20 + ) != null + ) break + + // as we don't want to waste the users time with torrents that is useless + // we do this to check that at a video file exists + if (!hasFileChecked && metadata.totalLength > (10 shl 20)) { + hasFileChecked = true + if (getPlayableFile( + metadata, + minimumBytes = -1 + ) == null + ) { + throw Exception("Download file has no video") + } + } + + println("downloaded ${metadata.downloadedLength}/${metadata.totalLength}") + delay(1000) + continue + } + + // if downloading then check if we have reached a stable file length + /*DownloadStatusTell.Active -> { + if (getPlayableFile(metadata, minimumBytes = 50 shl 20) != null) { + break + } + delay(1000) + continue + }*/ + + // unpause any pending files + DownloadStatusTell.Paused -> { + Aria2Starter.unpause(gid) + delay(1000) + continue + } + + null -> break + } + } + + val gid = DownloadListener.sessionIdToGid[requestId] + ?: throw Exception("Unable to start download") + + val metadata = DownloadListener.getInfo(gid) + + when (metadata.status) { + DownloadStatusTell.Active, DownloadStatusTell.Complete -> { + val uri = getPlayableFile(metadata, minimumBytes = 10 shl 20) + ?: throw Exception("Not downloaded enough") + activity.runOnUiThread { + //Log.i(TAG, "downloaded data: $metadata") + loadOfflinePlayer( + activity, + ExtractorUri( + // we require at least 10MB to play the file + uri = uri, + name = link.name, + tvType = TvType.Torrent + ) + ) + } + } + + DownloadStatusTell.Waiting -> { + throw Exception("Download was unable to be started") + } + + DownloadStatusTell.Paused -> { + throw Exception("Download is paused") + } + + DownloadStatusTell.Error -> { + throw Exception("Download error") + } + + DownloadStatusTell.Removed -> { + throw Exception("Download removed") + } + + null -> { + throw Exception("Unexpected download error") + } + } + } + + private fun releaseAria2c() = pauseAllAria2c() + private fun pauseAllAria2c() { + for ((_, gid) in DownloadListener.sessionIdToGid) { + Aria2Starter.pause(gid) + } + } + + + @Throws + private suspend fun playAria2c(activity: Activity, link: ExtractorLink) { + // ephemeral id based on url to make it unique + val requestId = link.url.hashCode().toLong() + + val uriReq = newUriRequest( + id = requestId, + uri = link.url, + fileName = null, + seed = false + ) + + val metadata = + DownloadListener.sessionIdToGid[requestId]?.let { gid -> DownloadListener.getInfo(gid) } + + when (metadata?.status) { + DownloadStatusTell.Removed, DownloadStatusTell.Error, null -> { + Aria2Starter.download(uriReq) + } + + else -> Unit + } + + try { + awaitAria2c(activity, link, requestId) + } catch (t: Throwable) { + // if we detect any download error then we delete it as we don't want any useless background tasks + Aria2Starter.delete(DownloadListener.sessionIdToGid[requestId], requestId) + throw t + } + } + + private fun loadAria2c(link: ExtractorLink) { + val act = CommonActivity.activity + if (act == null) { + event(ErrorEvent(IllegalArgumentException("No activity"))) + return + } + + CoroutineScope(Dispatchers.IO).launch { + try { + val defaultDirectory = "${act.cacheDir.path}/torrent_tmp" + + // start the client if not active, lazy init + if (Aria2Starter.client == null) { + Aria2Starter.start( + activity = act, + Aria2Settings( + UUID.randomUUID().toString(), + 4337, + defaultDirectory, + ) + ) + // remove all the cache + //File(defaultDirectory).deleteRecursively() + } + playAria2c(act, link) + } catch (t: Throwable) { + event(ErrorEvent(t)) + } + } + } + + /** hijacks the torrent links and downloads them with aria2c instead to be played */ + private fun loadOnlinePlayer(context: Context, link: ExtractorLink) { + when (link.type) { + ExtractorLinkType.TORRENT, ExtractorLinkType.MAGNET -> loadAria2c(link) + else -> { + loadOnlinePlayerReal(context, link) + } + } + } + override fun loadPlayer( context: Context, sameEpisode: Boolean, @@ -470,6 +752,7 @@ class CS3IPlayer : IPlayer { currentTextRenderer = null exoPlayer = null + releaseAria2c() //simpleCache = null } @@ -871,8 +1154,20 @@ class CS3IPlayer : IPlayer { CSPlayerEvent.SeekForward -> seekTime(seekActionTime, source) CSPlayerEvent.SeekBack -> seekTime(-seekActionTime, source) - CSPlayerEvent.NextEpisode -> event(EpisodeSeekEvent(offset = 1, source = source)) - CSPlayerEvent.PrevEpisode -> event(EpisodeSeekEvent(offset = -1, source = source)) + CSPlayerEvent.NextEpisode -> event( + EpisodeSeekEvent( + offset = 1, + source = source + ) + ) + + CSPlayerEvent.PrevEpisode -> event( + EpisodeSeekEvent( + offset = -1, + source = source + ) + ) + CSPlayerEvent.SkipCurrentChapter -> { //val dur = this@CS3IPlayer.getDuration() ?: return@apply getCurrentTimestamp()?.let { lastTimeStamp -> @@ -1249,7 +1544,7 @@ class CS3IPlayer : IPlayer { } @SuppressLint("UnsafeOptInUsageError") - private fun loadOnlinePlayer(context: Context, link: ExtractorLink) { + private fun loadOnlinePlayerReal(context: Context, link: ExtractorLink) { Log.i(TAG, "loadOnlinePlayer $link") try { currentLink = link diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt index b2542ffaf23..33d50fd3615 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt @@ -7,6 +7,7 @@ import android.content.Context import android.content.Intent import android.content.res.ColorStateList import android.os.Bundle +import android.text.format.Formatter import android.util.Log import android.view.LayoutInflater import android.view.View @@ -92,6 +93,52 @@ class GeneratorPlayer : FullScreenPlayer() { private var binding: FragmentPlayerBinding? = null + override fun playerDimensionsLoaded(width: Int, height: Int) { + setPlayerDimen(width to height) + showDownloadProgress(null) + } + + override fun playerError(exception: Throwable) { + Log.i(TAG, "playerError = $currentSelectedLink") + showDownloadProgress(null) + super.playerError(exception) + } + + override fun onDownload(event: DownloadEvent) { + super.onDownload(event) + showDownloadProgress(event) + } + + private fun showDownloadProgress(event: DownloadEvent?) { + activity?.runOnUiThread { + if(event == null) { + binding?.downloadHeader?.isVisible = false + return@runOnUiThread + } + binding?.downloadHeader?.isVisible = true + binding?.downloadedProgress?.apply { + val indeterminate = event.totalBytes <= 0 || event.downloadedBytes <= 0 + isIndeterminate = indeterminate + if (!indeterminate) { + max = (event.totalBytes / 1000).toInt() + progress = (event.downloadedBytes / 1000).toInt() + } + } + binding?.downloadedProgressText.setText( + txt( + R.string.download_size_format, + Formatter.formatShortFileSize(context, event.downloadedBytes), + Formatter.formatShortFileSize(context, event.totalBytes) + ) + ) + val downloadSpeed = Formatter.formatShortFileSize(context, event.downloadSpeed) + binding?.downloadedProgressSpeedText?.text = event.connections?.let { connections -> + "%s/s - %d Connections".format(downloadSpeed, connections) + } ?: downloadSpeed + } + } + + private fun startLoading() { player.release() currentSelectedSubtitles = null @@ -883,10 +930,6 @@ class GeneratorPlayer : FullScreenPlayer() { } - override fun playerError(exception: Throwable) { - Log.i(TAG, "playerError = $currentSelectedLink") - super.playerError(exception) - } private fun noLinksFound() { showToast(R.string.no_links_found_toast, Toast.LENGTH_SHORT) @@ -945,7 +988,7 @@ class GeneratorPlayer : FullScreenPlayer() { var maxEpisodeSet: Int? = null var hasRequestedStamps: Boolean = false - override fun playerPositionChanged(position: Long, duration : Long) { + override fun playerPositionChanged(position: Long, duration: Long) { // Don't save livestream data if ((currentMeta as? ResultEpisode)?.tvType?.isLiveStream() == true) return @@ -1208,10 +1251,6 @@ class GeneratorPlayer : FullScreenPlayer() { } } - override fun playerDimensionsLoaded(width: Int, height : Int) { - setPlayerDimen(width to height) - } - private fun unwrapBundle(savedInstanceState: Bundle?) { Log.i(TAG, "unwrapBundle = $savedInstanceState") savedInstanceState?.let { bundle -> @@ -1358,6 +1397,11 @@ class GeneratorPlayer : FullScreenPlayer() { activity?.popCurrentPage() } + binding?.playerLoadingGoBack2?.setOnClickListener { + player.release() + activity?.popCurrentPage() + } + observe(viewModel.currentStamps) { stamps -> player.addTimeStamps(stamps) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IGenerator.kt index af74cb57028..007dec268d7 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IGenerator.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IGenerator.kt @@ -18,7 +18,10 @@ fun LoadType.toSet() : Set { LoadType.InApp -> setOf( ExtractorLinkType.VIDEO, ExtractorLinkType.DASH, - ExtractorLinkType.M3U8 + ExtractorLinkType.M3U8, + // testing + ExtractorLinkType.TORRENT, + ExtractorLinkType.MAGNET, ) LoadType.Browser -> setOf( ExtractorLinkType.VIDEO, diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt index ec006234eef..ad722a27591 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt @@ -72,9 +72,20 @@ data class PositionEvent( val durationMs: Long, ) : PlayerEvent() { /** how many ms (+-) we have skipped */ - val seekMs : Long get() = toMs - fromMs + val seekMs: Long get() = toMs - fromMs } +/** Used for torrent to pre-download a video before playing it */ +data class DownloadEvent( + val downloadedBytes: Long, + val totalBytes: Long, + /** bytes / sec */ + val downloadSpeed: Long, + val connections: Int?, + + override val source: PlayerEventSource = PlayerEventSource.Player +) : PlayerEvent() + /** player error when rendering or misc, used to display toast or log */ data class ErrorEvent( val error: Throwable, diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt index 5edff7a1431..ce393c9d7b9 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -280,7 +280,7 @@ enum class ExtractorLinkType { private fun inferTypeFromUrl(url: String): ExtractorLinkType { val path = normalSafeApiCall { URL(url).path } return when { - path?.endsWith(".m3u8") == true -> ExtractorLinkType.M3U8 + path?.endsWith(".m3u8") == true || path?.endsWith(".m3u") == true -> ExtractorLinkType.M3U8 path?.endsWith(".mpd") == true -> ExtractorLinkType.DASH path?.endsWith(".torrent") == true -> ExtractorLinkType.TORRENT url.startsWith("magnet:") -> ExtractorLinkType.MAGNET diff --git a/app/src/main/res/layout/fragment_player.xml b/app/src/main/res/layout/fragment_player.xml index a620b6aee84..93e3fc84b02 100644 --- a/app/src/main/res/layout/fragment_player.xml +++ b/app/src/main/res/layout/fragment_player.xml @@ -29,6 +29,78 @@ app:layout_constraintTop_toTopOf="parent" app:show_timeout="0" /> + + + + + + + + + + + + + + + + + + + + + + app:layout_constraintTop_toTopOf="parent" + tools:visibility="gone"> - + + + + --> \ No newline at end of file From 8105231a6bd10bbc4dcc66d903713443da3f894d Mon Sep 17 00:00:00 2001 From: LagradOst <11805592+LagradOst@users.noreply.github.com> Date: Thu, 26 Oct 2023 01:51:38 +0200 Subject: [PATCH 2/6] testing seq torrent --- app/build.gradle.kts | 2 ++ .../lagradost/cloudstream3/AcraApplication.kt | 3 ++- .../com/lagradost/cloudstream3/MainActivity.kt | 6 +++--- .../cloudstream3/ui/player/CS3IPlayer.kt | 18 ++++++++++++------ app/src/main/res/layout/fragment_player.xml | 2 +- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f52d6e5e55e..005c63153a6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -260,6 +260,8 @@ dependencies { // color palette for images -> colors implementation("androidx.palette:palette-ktx:1.0.0") + + implementation("com.github.recloudstream:Aria2cStream:0.0.1") } tasks.register("androidSourcesJar", Jar::class) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt b/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt index 5f3162b49e7..bc8b963ac7f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt @@ -5,6 +5,7 @@ import android.app.Application import android.content.Context import android.content.ContextWrapper import android.content.Intent +import android.util.Log import android.widget.Toast import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity @@ -41,7 +42,7 @@ import kotlin.system.exitProcess class CustomReportSender : ReportSender { // Sends all your crashes to google forms override fun send(context: Context, errorContent: CrashReportData) { - println("Sending report") + Log.i("Acra", "Sending report: ${errorContent.toMap().map { "${it.key}:${it.value}" }.joinToString()}") val url = "https://docs.google.com/forms/d/e/1FAIpQLSfO4r353BJ79TTY_-t5KWSIJT2xfqcQWY81xjAA1-1N0U2eSg/formResponse" val data = mapOf( diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 44d078e0c63..242a9e86b5a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -1567,7 +1567,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { setKey(HAS_DONE_SETUP_KEY, true) } - /*val defaultDirectory = "${filesDir.path}/torrent_tmp" + //val defaultDirectory = "${filesDir.path}/torrent_tmp" //File(defaultDirectory).deleteRecursively() navigate( R.id.global_to_navigation_player, GeneratorPlayer.newInstance( @@ -1576,7 +1576,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { ExtractorLink( source = "", name = "hello world", - url = "", + url = "magnet:?xt=urn:btih:bbeb7617fb6240230b862edac6221f47d59e586b&dn=%5BOhys-Raws%5D%20Kamierabi%20-%2004%20%28CX%201280x720%20x264%20AAC%29.mp4&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Fexodus.desync.com%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.torrent.eu.org%3A451%2Fannounce", "", Qualities.Unknown.value, type = INFER_TYPE @@ -1585,7 +1585,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { emptyList() ) ) - )*/ + ) // Used to check current focus for TV // main { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt index b44ce003b72..e3499e984dd 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt @@ -262,6 +262,7 @@ class CS3IPlayer : IPlayer { link: ExtractorLink, requestId: Long, ) { + val minimumBytes : Long = 30 shl 20 var hasFileChecked = false while (true) { val gid = DownloadListener.sessionIdToGid[requestId] @@ -294,15 +295,16 @@ class CS3IPlayer : IPlayer { } DownloadStatusTell.Active -> { - if (metadata.downloadedLength >= metadata.totalLength && getPlayableFile( + //metadata.downloadedLength >= metadata.totalLength && + if (getPlayableFile( metadata, - minimumBytes = 10 shl 20 + minimumBytes = minimumBytes ) != null ) break // as we don't want to waste the users time with torrents that is useless // we do this to check that at a video file exists - if (!hasFileChecked && metadata.totalLength > (10 shl 20)) { + if (!hasFileChecked && metadata.totalLength > minimumBytes) { hasFileChecked = true if (getPlayableFile( metadata, @@ -334,7 +336,10 @@ class CS3IPlayer : IPlayer { continue } - null -> break + null -> { + delay(1000) + continue + } } } @@ -345,7 +350,7 @@ class CS3IPlayer : IPlayer { when (metadata.status) { DownloadStatusTell.Active, DownloadStatusTell.Complete -> { - val uri = getPlayableFile(metadata, minimumBytes = 10 shl 20) + val uri = getPlayableFile(metadata, minimumBytes = minimumBytes) ?: throw Exception("Not downloaded enough") activity.runOnUiThread { //Log.i(TAG, "downloaded data: $metadata") @@ -400,7 +405,8 @@ class CS3IPlayer : IPlayer { id = requestId, uri = link.url, fileName = null, - seed = false + seed = false, + stream = true, ) val metadata = diff --git a/app/src/main/res/layout/fragment_player.xml b/app/src/main/res/layout/fragment_player.xml index 93e3fc84b02..64677246f25 100644 --- a/app/src/main/res/layout/fragment_player.xml +++ b/app/src/main/res/layout/fragment_player.xml @@ -30,13 +30,13 @@ app:show_timeout="0" /> Date: Thu, 26 Oct 2023 01:53:43 +0200 Subject: [PATCH 3/6] fix --- .../main/java/com/lagradost/cloudstream3/AcraApplication.kt | 3 ++- app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt b/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt index bc8b963ac7f..2b5909ad360 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt @@ -42,7 +42,8 @@ import kotlin.system.exitProcess class CustomReportSender : ReportSender { // Sends all your crashes to google forms override fun send(context: Context, errorContent: CrashReportData) { - Log.i("Acra", "Sending report: ${errorContent.toMap().map { "${it.key}:${it.value}" }.joinToString()}") + println("Sending report") + //Log.i("Acra", "Sending report: ${errorContent.toMap().map { "${it.key}:${it.value}" }.joinToString()}") val url = "https://docs.google.com/forms/d/e/1FAIpQLSfO4r353BJ79TTY_-t5KWSIJT2xfqcQWY81xjAA1-1N0U2eSg/formResponse" val data = mapOf( diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 242a9e86b5a..c37f7810cd0 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -1576,7 +1576,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { ExtractorLink( source = "", name = "hello world", - url = "magnet:?xt=urn:btih:bbeb7617fb6240230b862edac6221f47d59e586b&dn=%5BOhys-Raws%5D%20Kamierabi%20-%2004%20%28CX%201280x720%20x264%20AAC%29.mp4&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Fexodus.desync.com%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.torrent.eu.org%3A451%2Fannounce", + url = "", "", Qualities.Unknown.value, type = INFER_TYPE From 49b905c089a9d9cc9e9457bd59803e232a8f797e Mon Sep 17 00:00:00 2001 From: LagradOst <11805592+LagradOst@users.noreply.github.com> Date: Thu, 26 Oct 2023 21:40:45 +0200 Subject: [PATCH 4/6] poc --- app/build.gradle.kts | 2 +- .../lagradost/cloudstream3/MainActivity.kt | 5 +- .../ui/player/AbstractPlayerFragment.kt | 2 +- .../cloudstream3/ui/player/CS3IPlayer.kt | 137 ++++++++++++++++-- .../cloudstream3/ui/player/GeneratorPlayer.kt | 8 + app/src/main/res/layout/fragment_player.xml | 14 +- 6 files changed, 147 insertions(+), 21 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 005c63153a6..0b4589adf74 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -261,7 +261,7 @@ dependencies { // color palette for images -> colors implementation("androidx.palette:palette-ktx:1.0.0") - implementation("com.github.recloudstream:Aria2cStream:0.0.1") + implementation("com.github.recloudstream:Aria2cStream:0.0.3") } tasks.register("androidSourcesJar", Jar::class) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index c37f7810cd0..7e6c0628cdc 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -1569,14 +1569,13 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { //val defaultDirectory = "${filesDir.path}/torrent_tmp" //File(defaultDirectory).deleteRecursively() - navigate( + /*navigate( R.id.global_to_navigation_player, GeneratorPlayer.newInstance( ExtractorLinkGenerator( listOf( ExtractorLink( source = "", name = "hello world", - url = "", "", Qualities.Unknown.value, type = INFER_TYPE @@ -1585,7 +1584,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { emptyList() ) ) - ) + )*/ // Used to check current focus for TV // main { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt index efa03bd49c4..f22d5515914 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt @@ -135,7 +135,7 @@ abstract class AbstractPlayerFragment( } } - private fun updateIsPlaying(wasPlaying : CSPlayerLoading, + open fun updateIsPlaying(wasPlaying : CSPlayerLoading, isPlaying : CSPlayerLoading) { val isPlayingRightNow = CSPlayerLoading.IsPlaying == isPlaying val isPausedRightNow = CSPlayerLoading.IsPaused == isPlaying diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt index e3499e984dd..9a1bd421ea2 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt @@ -56,6 +56,7 @@ import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.USER_AGENT import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.mvvm.debugAssert +import com.lagradost.cloudstream3.mvvm.launchSafe import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.ui.subtitles.SaveCaptionStyle @@ -67,10 +68,15 @@ import com.lagradost.cloudstream3.utils.ExtractorLinkPlayList import com.lagradost.cloudstream3.utils.ExtractorLinkType import com.lagradost.cloudstream3.utils.ExtractorUri import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage +import com.lagradost.fetchbutton.aria2c.Aria2Args import com.lagradost.fetchbutton.aria2c.Aria2Settings import com.lagradost.fetchbutton.aria2c.Aria2Starter +import com.lagradost.fetchbutton.aria2c.BtPieceSelector import com.lagradost.fetchbutton.aria2c.DownloadListener import com.lagradost.fetchbutton.aria2c.DownloadStatusTell +import com.lagradost.fetchbutton.aria2c.FileAllocationType +import com.lagradost.fetchbutton.aria2c.FollowMetaLinkType +import com.lagradost.fetchbutton.aria2c.UriRequest import com.lagradost.fetchbutton.aria2c.newUriRequest import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -119,6 +125,9 @@ class CS3IPlayer : IPlayer { private var lastMuteVolume: Float = 1.0f private var currentLink: ExtractorLink? = null + + private var currentAria2cRequestLink: ExtractorLink? = null + private var currentAria2cRequestId: Long? = null private var currentDownloadedFile: ExtractorUri? = null private var hasUsedFirstRender = false @@ -262,7 +271,7 @@ class CS3IPlayer : IPlayer { link: ExtractorLink, requestId: Long, ) { - val minimumBytes : Long = 30 shl 20 + val minimumBytes: Long = 30 shl 20 var hasFileChecked = false while (true) { val gid = DownloadListener.sessionIdToGid[requestId] @@ -354,6 +363,8 @@ class CS3IPlayer : IPlayer { ?: throw Exception("Not downloaded enough") activity.runOnUiThread { //Log.i(TAG, "downloaded data: $metadata") + exoPlayer?.release() + exoPlayer = null loadOfflinePlayer( activity, ExtractorUri( @@ -400,13 +411,64 @@ class CS3IPlayer : IPlayer { private suspend fun playAria2c(activity: Activity, link: ExtractorLink) { // ephemeral id based on url to make it unique val requestId = link.url.hashCode().toLong() + currentAria2cRequestId = requestId + currentAria2cRequestLink = link - val uriReq = newUriRequest( + val uriReq = UriRequest( id = requestId, - uri = link.url, - fileName = null, - seed = false, - stream = true, + uris = listOf(link.url), + args = Aria2Args( + headers = link.headers, + referer = link.referer, + /** torrent specifics to make it possible to stream */ + seedRatio = 0.0f, + seedTimeMin = 0.0f, + btPieceSelector = BtPieceSelector.Inorder, + followTorrent = FollowMetaLinkType.Mem, + fileAllocation = FileAllocationType.None, + btPrioritizePiece = "head=30M,tail=30M", + /** Best trackers to make it faster */ + btTracker = listOf( + "udp://tracker.opentrackr.org:1337/announce", + "https://tracker2.ctix.cn/announce", + "https://tracker1.520.jp:443/announce", + "udp://opentracker.i2p.rocks:6969/announce", + "udp://open.tracker.cl:1337/announce", + "udp://open.demonii.com:1337/announce", + "http://tracker.openbittorrent.com:80/announce", + "udp://tracker.openbittorrent.com:6969/announce", + "udp://open.stealth.si:80/announce", + "udp://exodus.desync.com:6969/announce", + "udp://tracker-udp.gbitt.info:80/announce", + "udp://explodie.org:6969/announce", + "https://tracker.gbitt.info:443/announce", + "http://tracker.gbitt.info:80/announce", + "udp://uploads.gamecoast.net:6969/announce", + "udp://tracker1.bt.moack.co.kr:80/announce", + "udp://tracker.tiny-vps.com:6969/announce", + "udp://tracker.theoks.net:6969/announce", + "udp://tracker.dump.cl:6969/announce", + "udp://tracker.bittor.pw:1337/announce", + "https://tracker1.520.jp:443/announce", + "udp://opentracker.i2p.rocks:6969/announce", + "udp://open.tracker.cl:1337/announce", + "udp://open.demonii.com:1337/announce", + "http://tracker.openbittorrent.com:80/announce", + "udp://tracker.openbittorrent.com:6969/announce", + "udp://open.stealth.si:80/announce", + "udp://exodus.desync.com:6969/announce", + "udp://tracker-udp.gbitt.info:80/announce", + "udp://explodie.org:6969/announce", + "https://tracker.gbitt.info:443/announce", + "http://tracker.gbitt.info:80/announce", + "udp://uploads.gamecoast.net:6969/announce", + "udp://tracker1.bt.moack.co.kr:80/announce", + "udp://tracker.tiny-vps.com:6969/announce", + "udp://tracker.theoks.net:6969/announce", + "udp://tracker.dump.cl:6969/announce", + "udp://tracker.bittor.pw:1337/announce" + ) + ) ) val metadata = @@ -421,6 +483,7 @@ class CS3IPlayer : IPlayer { } try { + saveData() awaitAria2c(activity, link, requestId) } catch (t: Throwable) { // if we detect any download error then we delete it as we don't want any useless background tasks @@ -500,6 +563,7 @@ class CS3IPlayer : IPlayer { if (link != null) { loadOnlinePlayer(context, link) } else if (data != null) { + currentAria2cRequestId = null loadOfflinePlayer(context, data) } } @@ -1320,10 +1384,64 @@ class CS3IPlayer : IPlayer { } override fun onPlayerError(error: PlaybackException) { - // If the Network fails then ignore the exception if the duration is set. - // This is to switch mirrors automatically if the stream has not been fetched, but - // allow playing the buffer without internet as then the duration is fetched. + val aria2cRequestId = currentAria2cRequestId + val loadedLink = currentAria2cRequestLink when { + // if we are loading an torrent, then we will get these errors, in that case + // we just treat it as buffering + aria2cRequestId != null && loadedLink != null && + (error.errorCode == PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE + || error.errorCode == PlaybackException.ERROR_CODE_PARSING_MANIFEST_MALFORMED + || error.errorCode == PlaybackException.ERROR_CODE_PARSING_CONTAINER_MALFORMED + || error.errorCode == PlaybackException.ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED + ) -> { + + val gid = DownloadListener.sessionIdToGid[aria2cRequestId] + Log.i(TAG, "Aria2 error $error error ${error.errorCode}") + if(gid == null) { + event(ErrorEvent(error)) + super.onPlayerError(error) + return + } + event( + StatusEvent( + wasPlaying = CSPlayerLoading.IsPlaying, + isPlaying = CSPlayerLoading.IsBuffering + ) + ) + CoroutineScope(Dispatchers.IO).launchSafe { + for (i in 0..5) { + val metadata = DownloadListener.getInfo(gid) + event( + DownloadEvent( + downloadedBytes = metadata.downloadedLength, + downloadSpeed = metadata.downloadSpeed, + totalBytes = metadata.totalLength, + connections = metadata.items.sumOf { it.connections } + ) + ) + + delay(1000) + } + + // CommonActivity.activity?.runOnUiThread { + // exoPlayer?.prepare() + // } + + // shitty solution to release it every time, however we will get timeout otherwise + CommonActivity.activity?.let { act -> + try { + awaitAria2c(act, loadedLink, aria2cRequestId) + } catch (t : Throwable) { + event(ErrorEvent(t)) + } + } + } + } + + // If the Network fails then ignore the exception if the duration is set. + // This is to switch mirrors automatically if the stream has not been fetched, but + // allow playing the buffer without internet as then the duration is fetched. error.errorCode == PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED && exoPlayer?.duration != TIME_UNSET -> { exoPlayer?.prepare() @@ -1554,6 +1672,7 @@ class CS3IPlayer : IPlayer { Log.i(TAG, "loadOnlinePlayer $link") try { currentLink = link + currentAria2cRequestId = null if (ignoreSSL) { // Disables ssl check diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt index 33d50fd3615..52ebcb16ad4 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt @@ -109,6 +109,14 @@ class GeneratorPlayer : FullScreenPlayer() { showDownloadProgress(event) } + /*override fun updateIsPlaying(wasPlaying: CSPlayerLoading, isPlaying: CSPlayerLoading) { + super.updateIsPlaying(wasPlaying, isPlaying) + if(isPlaying == CSPlayerLoading.IsPlaying) + activity?.runOnUiThread { + + } + }*/ + private fun showDownloadProgress(event: DownloadEvent?) { activity?.runOnUiThread { if(event == null) { diff --git a/app/src/main/res/layout/fragment_player.xml b/app/src/main/res/layout/fragment_player.xml index 64677246f25..67c3146d925 100644 --- a/app/src/main/res/layout/fragment_player.xml +++ b/app/src/main/res/layout/fragment_player.xml @@ -30,19 +30,19 @@ app:show_timeout="0" /> + android:backgroundTint="@android:color/black" + android:id="@+id/download_header" + android:visibility="gone" + tools:visibility="visible" + android:layout_width="match_parent" + android:layout_height="match_parent"> + android:orientation="vertical"> Date: Sat, 28 Oct 2023 15:33:01 +0300 Subject: [PATCH 5/6] another io error (#731) --- .../main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt index 9a1bd421ea2..f548d46d57a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt @@ -1394,6 +1394,7 @@ class CS3IPlayer : IPlayer { || error.errorCode == PlaybackException.ERROR_CODE_PARSING_MANIFEST_MALFORMED || error.errorCode == PlaybackException.ERROR_CODE_PARSING_CONTAINER_MALFORMED || error.errorCode == PlaybackException.ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED + || error.errorCode == PlaybackException.ERROR_CODE_IO_UNSPECIFIED ) -> { val gid = DownloadListener.sessionIdToGid[aria2cRequestId] From bef34c33e9b3dc7d54a9d0983f384d3cd991605c Mon Sep 17 00:00:00 2001 From: LagradOst <11805592+LagradOst@users.noreply.github.com> Date: Thu, 2 Nov 2023 22:08:41 +0100 Subject: [PATCH 6/6] save location --- .../cloudstream3/ui/player/AbstractPlayerFragment.kt | 4 +++- .../java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt | 3 ++- .../java/com/lagradost/cloudstream3/utils/ExtractorApi.kt | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt index f22d5515914..619cf06c297 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt @@ -265,7 +265,9 @@ abstract class AbstractPlayerFragment( context?.getString(R.string.no_links_found_toast) + "\n" + message, Toast.LENGTH_LONG ) - activity?.popCurrentPage() + activity?.runOnUiThread { + activity?.popCurrentPage() + } } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt index f548d46d57a..9db65707dfc 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt @@ -1396,7 +1396,7 @@ class CS3IPlayer : IPlayer { || error.errorCode == PlaybackException.ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED || error.errorCode == PlaybackException.ERROR_CODE_IO_UNSPECIFIED ) -> { - + val position = exoPlayer?.currentPosition ?: 0L val gid = DownloadListener.sessionIdToGid[aria2cRequestId] Log.i(TAG, "Aria2 error $error error ${error.errorCode}") if(gid == null) { @@ -1432,6 +1432,7 @@ class CS3IPlayer : IPlayer { // shitty solution to release it every time, however we will get timeout otherwise CommonActivity.activity?.let { act -> try { + playbackPosition = position awaitAria2c(act, loadedLink, aria2cRequestId) } catch (t : Throwable) { event(ErrorEvent(t)) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt index ce393c9d7b9..6f5f90e07c0 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -278,7 +278,7 @@ enum class ExtractorLinkType { } private fun inferTypeFromUrl(url: String): ExtractorLinkType { - val path = normalSafeApiCall { URL(url).path } + val path = try { URL(url).path } catch (_ : Throwable) { null } return when { path?.endsWith(".m3u8") == true || path?.endsWith(".m3u") == true -> ExtractorLinkType.M3U8 path?.endsWith(".mpd") == true -> ExtractorLinkType.DASH