parent
4b413b78fe
commit
6e399b32e1
5 changed files with 271 additions and 99 deletions
|
@ -48,6 +48,8 @@ import android.widget.ImageButton
|
||||||
import android.widget.Spinner
|
import android.widget.Spinner
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.activity.addCallback
|
import androidx.activity.addCallback
|
||||||
|
import androidx.activity.result.ActivityResult
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
@ -60,12 +62,16 @@ import androidx.media3.cast.CastPlayer
|
||||||
import androidx.media3.cast.SessionAvailabilityListener
|
import androidx.media3.cast.SessionAvailabilityListener
|
||||||
import androidx.media3.common.C
|
import androidx.media3.common.C
|
||||||
import androidx.media3.common.C.AUDIO_CONTENT_TYPE_MOVIE
|
import androidx.media3.common.C.AUDIO_CONTENT_TYPE_MOVIE
|
||||||
|
import androidx.media3.common.C.TRACK_TYPE_AUDIO
|
||||||
|
import androidx.media3.common.C.TRACK_TYPE_TEXT
|
||||||
import androidx.media3.common.C.TRACK_TYPE_VIDEO
|
import androidx.media3.common.C.TRACK_TYPE_VIDEO
|
||||||
|
import androidx.media3.common.Format
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
import androidx.media3.common.MimeTypes
|
import androidx.media3.common.MimeTypes
|
||||||
import androidx.media3.common.PlaybackException
|
import androidx.media3.common.PlaybackException
|
||||||
import androidx.media3.common.PlaybackParameters
|
import androidx.media3.common.PlaybackParameters
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
|
import androidx.media3.common.TrackGroup
|
||||||
import androidx.media3.common.TrackSelectionOverride
|
import androidx.media3.common.TrackSelectionOverride
|
||||||
import androidx.media3.common.Tracks
|
import androidx.media3.common.Tracks
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
|
@ -128,13 +134,13 @@ import ani.dantotsu.parsers.SubtitleType
|
||||||
import ani.dantotsu.parsers.Video
|
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.px
|
|
||||||
import ani.dantotsu.settings.PlayerSettingsActivity
|
import ani.dantotsu.settings.PlayerSettingsActivity
|
||||||
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.snackString
|
import ani.dantotsu.snackString
|
||||||
import ani.dantotsu.startMainActivity
|
import ani.dantotsu.startMainActivity
|
||||||
import ani.dantotsu.themes.ThemeManager
|
import ani.dantotsu.themes.ThemeManager
|
||||||
|
import ani.dantotsu.toPx
|
||||||
import ani.dantotsu.toast
|
import ani.dantotsu.toast
|
||||||
import ani.dantotsu.tryWithSuspend
|
import ani.dantotsu.tryWithSuspend
|
||||||
import ani.dantotsu.util.Logger
|
import ani.dantotsu.util.Logger
|
||||||
|
@ -153,10 +159,12 @@ import kotlinx.coroutines.runBlocking
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
|
import java.util.Locale
|
||||||
import java.util.Timer
|
import java.util.Timer
|
||||||
import java.util.TimerTask
|
import java.util.TimerTask
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
import kotlin.collections.set
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
@ -189,6 +197,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
private lateinit var exoSettings: ImageButton
|
private lateinit var exoSettings: ImageButton
|
||||||
private lateinit var exoSubtitle: ImageButton
|
private lateinit var exoSubtitle: ImageButton
|
||||||
private lateinit var exoSubtitleView: SubtitleView
|
private lateinit var exoSubtitleView: SubtitleView
|
||||||
|
private lateinit var exoAudioTrack: ImageButton
|
||||||
private lateinit var exoRotate: ImageButton
|
private lateinit var exoRotate: ImageButton
|
||||||
private lateinit var exoSpeed: ImageButton
|
private lateinit var exoSpeed: ImageButton
|
||||||
private lateinit var exoScreen: ImageButton
|
private lateinit var exoScreen: ImageButton
|
||||||
|
@ -211,6 +220,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
private var orientationListener: OrientationEventListener? = null
|
private var orientationListener: OrientationEventListener? = null
|
||||||
|
|
||||||
private var downloadId: String? = null
|
private var downloadId: String? = null
|
||||||
|
private var hasExtSubtitles = false
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
var initialized = false
|
var initialized = false
|
||||||
|
@ -287,7 +297,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
playerView.findViewById<View>(androidx.media3.ui.R.id.exo_buffering).translationY =
|
playerView.findViewById<View>(androidx.media3.ui.R.id.exo_buffering).translationY =
|
||||||
(if (orientation == Configuration.ORIENTATION_LANDSCAPE) 0 else (notchHeight + 8f.px)).dp
|
(if (orientation == Configuration.ORIENTATION_LANDSCAPE) 0 else (notchHeight + 8.toPx)).dp
|
||||||
exoBrightnessCont.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
exoBrightnessCont.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||||
marginEnd =
|
marginEnd =
|
||||||
if (orientation == Configuration.ORIENTATION_LANDSCAPE) notchHeight else 0
|
if (orientation == Configuration.ORIENTATION_LANDSCAPE) notchHeight else 0
|
||||||
|
@ -434,6 +444,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
exoSource = playerView.findViewById(R.id.exo_source)
|
exoSource = playerView.findViewById(R.id.exo_source)
|
||||||
exoSettings = playerView.findViewById(R.id.exo_settings)
|
exoSettings = playerView.findViewById(R.id.exo_settings)
|
||||||
exoSubtitle = playerView.findViewById(R.id.exo_sub)
|
exoSubtitle = playerView.findViewById(R.id.exo_sub)
|
||||||
|
exoAudioTrack = playerView.findViewById(R.id.exo_audio)
|
||||||
exoSubtitleView = playerView.findViewById(androidx.media3.ui.R.id.exo_subtitles)
|
exoSubtitleView = playerView.findViewById(androidx.media3.ui.R.id.exo_subtitles)
|
||||||
exoRotate = playerView.findViewById(R.id.exo_rotate)
|
exoRotate = playerView.findViewById(R.id.exo_rotate)
|
||||||
exoSpeed = playerView.findViewById(androidx.media3.ui.R.id.exo_playback_speed)
|
exoSpeed = playerView.findViewById(androidx.media3.ui.R.id.exo_playback_speed)
|
||||||
|
@ -475,14 +486,12 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
rotation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
|
rotation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
|
||||||
}
|
}
|
||||||
|
|
||||||
in 225..315 -> {
|
in 225..315 -> {
|
||||||
if (rotation != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
|
if (rotation != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
|
||||||
exoRotate.visibility = View.VISIBLE
|
exoRotate.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
rotation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
rotation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
||||||
}
|
}
|
||||||
|
|
||||||
in 315..360, in 0..45 -> {
|
in 315..360, in 0..45 -> {
|
||||||
if (rotation != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
|
if (rotation != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
|
||||||
exoRotate.visibility = View.VISIBLE
|
exoRotate.visibility = View.VISIBLE
|
||||||
|
@ -501,7 +510,6 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
it.visibility = View.GONE
|
it.visibility = View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setupSubFormatting(playerView)
|
setupSubFormatting(playerView)
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
|
@ -915,8 +923,8 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
isFastForwarding = true
|
isFastForwarding = true
|
||||||
exoPlayer.setPlaybackSpeed(exoPlayer.playbackParameters.speed * 2)
|
exoPlayer.setPlaybackSpeed(exoPlayer.playbackParameters.speed * 2)
|
||||||
fastForward.visibility = View.VISIBLE
|
fastForward.visibility = View.VISIBLE
|
||||||
val speed = "${exoPlayer.playbackParameters.speed}x"
|
val speedText = "${exoPlayer.playbackParameters.speed}x"
|
||||||
fastForward.text = speed
|
fastForward.text = speedText
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stopFastForward() {
|
fun stopFastForward() {
|
||||||
|
@ -1139,6 +1147,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
it
|
it
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
smallImage = RPC.Link("Dantotsu", Discord.small_Image),
|
||||||
buttons = buttons
|
buttons = buttons
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -1208,7 +1217,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
putExtra("subtitle", subtitle)
|
putExtra("subtitle", subtitle)
|
||||||
}
|
}
|
||||||
exoPlayer.pause()
|
exoPlayer.pause()
|
||||||
startActivity(intent)
|
onChangeSettings.launch(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Speed
|
//Speed
|
||||||
|
@ -1364,7 +1373,6 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
val ext = episode.extractors?.find { it.server.name == episode.selectedExtractor } ?: return
|
val ext = episode.extractors?.find { it.server.name == episode.selectedExtractor } ?: return
|
||||||
extractor = ext
|
extractor = ext
|
||||||
video = ext.videos.getOrNull(episode.selectedVideo) ?: return
|
video = ext.videos.getOrNull(episode.selectedVideo) ?: return
|
||||||
|
|
||||||
subtitle = intent.getSerialized("subtitle")
|
subtitle = intent.getSerialized("subtitle")
|
||||||
?: when (val subLang: String? =
|
?: when (val subLang: String? =
|
||||||
PrefManager.getNullableCustomVal("subLang_${media.id}", null, String::class.java)) {
|
PrefManager.getNullableCustomVal("subLang_${media.id}", null, String::class.java)) {
|
||||||
|
@ -1381,18 +1389,22 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
|
|
||||||
//Subtitles
|
//Subtitles
|
||||||
exoSubtitle.isVisible = ext.subtitles.isNotEmpty()
|
hasExtSubtitles = ext.subtitles.isNotEmpty()
|
||||||
exoSubtitle.setOnClickListener {
|
if (hasExtSubtitles) {
|
||||||
subClick()
|
exoSubtitle.isVisible = hasExtSubtitles
|
||||||
|
exoSubtitle.setOnClickListener {
|
||||||
|
subClick()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var sub: MediaItem.SubtitleConfiguration? = null
|
val sub: MutableList<MediaItem.SubtitleConfiguration> = emptyList<MediaItem.SubtitleConfiguration>().toMutableList()
|
||||||
if (subtitle != null) {
|
ext.subtitles.forEach { subtitle ->
|
||||||
|
val subtitleUrl = if (!hasExtSubtitles) video!!.file.url else subtitle.file.url
|
||||||
//var localFile: String? = null
|
//var localFile: String? = null
|
||||||
if (subtitle?.type == SubtitleType.UNKNOWN) {
|
if (subtitle.type == SubtitleType.UNKNOWN) {
|
||||||
runBlocking {
|
runBlocking {
|
||||||
val type = SubtitleDownloader.loadSubtitleType(subtitle!!.file.url)
|
val type = SubtitleDownloader.loadSubtitleType(subtitleUrl)
|
||||||
val fileUri = Uri.parse(subtitle!!.file.url)
|
val fileUri = Uri.parse(subtitleUrl)
|
||||||
sub = MediaItem.SubtitleConfiguration
|
sub += MediaItem.SubtitleConfiguration
|
||||||
.Builder(fileUri)
|
.Builder(fileUri)
|
||||||
.setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
|
.setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
|
||||||
.setMimeType(
|
.setMimeType(
|
||||||
|
@ -1404,16 +1416,17 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.setId("69")
|
.setId("69")
|
||||||
|
.setLanguage(subtitle.language)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
println("sub: $sub")
|
println("sub: $sub")
|
||||||
} else {
|
} else {
|
||||||
val subUri = Uri.parse(subtitle!!.file.url)
|
val subUri = Uri.parse(subtitleUrl)
|
||||||
sub = MediaItem.SubtitleConfiguration
|
sub += MediaItem.SubtitleConfiguration
|
||||||
.Builder(subUri)
|
.Builder(subUri)
|
||||||
.setSelectionFlags(C.SELECTION_FLAG_FORCED)
|
.setSelectionFlags(C.SELECTION_FLAG_FORCED)
|
||||||
.setMimeType(
|
.setMimeType(
|
||||||
when (subtitle?.type) {
|
when (subtitle.type) {
|
||||||
SubtitleType.VTT -> MimeTypes.TEXT_VTT
|
SubtitleType.VTT -> MimeTypes.TEXT_VTT
|
||||||
SubtitleType.ASS -> MimeTypes.TEXT_SSA
|
SubtitleType.ASS -> MimeTypes.TEXT_SSA
|
||||||
SubtitleType.SRT -> MimeTypes.APPLICATION_SUBRIP
|
SubtitleType.SRT -> MimeTypes.APPLICATION_SUBRIP
|
||||||
|
@ -1421,6 +1434,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.setId("69")
|
.setId("69")
|
||||||
|
.setLanguage(subtitle.language)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1462,7 +1476,6 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
|
|
||||||
val downloadedMediaItem = if (ext.server.offline) {
|
val downloadedMediaItem = if (ext.server.offline) {
|
||||||
val key = ext.server.name
|
|
||||||
val titleName = ext.server.name.split("/").first()
|
val titleName = ext.server.name.split("/").first()
|
||||||
val episodeName = ext.server.name.split("/").last()
|
val episodeName = ext.server.name.split("/").last()
|
||||||
|
|
||||||
|
@ -1491,21 +1504,18 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
val builder = MediaItem.Builder().setUri(video!!.file.url).setMimeType(mimeType)
|
val builder = MediaItem.Builder().setUri(video!!.file.url).setMimeType(mimeType)
|
||||||
Logger.log("url: ${video!!.file.url}")
|
Logger.log("url: ${video!!.file.url}")
|
||||||
Logger.log("mimeType: $mimeType")
|
Logger.log("mimeType: $mimeType")
|
||||||
|
builder.setSubtitleConfigurations(sub)
|
||||||
if (sub != null) {
|
|
||||||
val listofnotnullsubs = listOfNotNull(sub)
|
|
||||||
builder.setSubtitleConfigurations(listofnotnullsubs)
|
|
||||||
}
|
|
||||||
builder.build()
|
builder.build()
|
||||||
} else {
|
} else {
|
||||||
val addedSubsDownloadedMediaItem = downloadedMediaItem.buildUpon()
|
if (sub.isNotEmpty()) {
|
||||||
if (sub != null) {
|
val addedSubsDownloadedMediaItem = downloadedMediaItem.buildUpon()
|
||||||
val listofnotnullsubs = listOfNotNull(sub)
|
val addLanguage = sub[0].buildUpon().setLanguage("en").build()
|
||||||
val addLanguage = listofnotnullsubs[0].buildUpon().setLanguage("en").build()
|
|
||||||
addedSubsDownloadedMediaItem.setSubtitleConfigurations(listOf(addLanguage))
|
addedSubsDownloadedMediaItem.setSubtitleConfigurations(listOf(addLanguage))
|
||||||
episode.selectedSubtitle = 0
|
episode.selectedSubtitle = 0
|
||||||
|
addedSubsDownloadedMediaItem.build()
|
||||||
|
} else {
|
||||||
|
downloadedMediaItem
|
||||||
}
|
}
|
||||||
addedSubsDownloadedMediaItem.build()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1516,22 +1526,22 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
|
|
||||||
//Quality Track
|
//Quality Track
|
||||||
trackSelector = DefaultTrackSelector(this)
|
trackSelector = DefaultTrackSelector(this)
|
||||||
trackSelector.setParameters(
|
val parameters = trackSelector.buildUponParameters()
|
||||||
trackSelector.buildUponParameters()
|
.setAllowVideoMixedMimeTypeAdaptiveness(true)
|
||||||
.setAllowVideoMixedMimeTypeAdaptiveness(true)
|
.setAllowVideoNonSeamlessAdaptiveness(true)
|
||||||
.setAllowVideoNonSeamlessAdaptiveness(true)
|
.setSelectUndeterminedTextLanguage(true)
|
||||||
.setSelectUndeterminedTextLanguage(true)
|
.setAllowAudioMixedMimeTypeAdaptiveness(true)
|
||||||
.setAllowAudioMixedMimeTypeAdaptiveness(true)
|
.setAllowMultipleAdaptiveSelections(true)
|
||||||
.setAllowMultipleAdaptiveSelections(true)
|
.setPreferredTextLanguage(subtitle?.language ?: Locale.getDefault().language)
|
||||||
.setPreferredTextLanguage(subtitle?.language ?: "en")
|
.setPreferredTextRoleFlags(C.ROLE_FLAG_SUBTITLE)
|
||||||
.setPreferredTextRoleFlags(C.ROLE_FLAG_SUBTITLE)
|
.setRendererDisabled(TRACK_TYPE_VIDEO, false)
|
||||||
.setRendererDisabled(TRACK_TYPE_VIDEO, false)
|
.setRendererDisabled(TRACK_TYPE_AUDIO, false)
|
||||||
.setRendererDisabled(C.TRACK_TYPE_AUDIO, false)
|
.setRendererDisabled(TRACK_TYPE_TEXT, false)
|
||||||
.setRendererDisabled(C.TRACK_TYPE_TEXT, false)
|
.setMaxVideoSize(1, 1)
|
||||||
.setMaxVideoSize(1, 1)
|
// .setOverrideForType(TrackSelectionOverride(trackSelector, TRACK_TYPE_VIDEO))
|
||||||
//.setOverrideForType(
|
if (PrefManager.getVal(PrefName.SettingsPreferDub))
|
||||||
// TrackSelectionOverride(trackSelector, 2))
|
parameters.setPreferredAudioLanguage(Locale.getDefault().language)
|
||||||
)
|
trackSelector.setParameters(parameters)
|
||||||
|
|
||||||
if (playbackPosition != 0L && !changingServer && !PrefManager.getVal<Boolean>(PrefName.AlwaysContinue)) {
|
if (playbackPosition != 0L && !changingServer && !PrefManager.getVal<Boolean>(PrefName.AlwaysContinue)) {
|
||||||
val time = String.format(
|
val time = String.format(
|
||||||
|
@ -1596,7 +1606,6 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
playerView.player = exoPlayer
|
playerView.player = exoPlayer
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val rightNow = Calendar.getInstance()
|
val rightNow = Calendar.getInstance()
|
||||||
mediaSession = MediaSession.Builder(this, exoPlayer)
|
mediaSession = MediaSession.Builder(this, exoPlayer)
|
||||||
|
@ -1609,32 +1618,11 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
exoPlayer.addListener(this)
|
exoPlayer.addListener(this)
|
||||||
exoPlayer.addAnalyticsListener(EventLogger())
|
exoPlayer.addAnalyticsListener(EventLogger())
|
||||||
isInitialized = true
|
isInitialized = true
|
||||||
}
|
|
||||||
/*private fun selectSubtitleTrack() { saving this for later
|
|
||||||
// Get the current track groups
|
|
||||||
val trackGroups = exoPlayer.currentTrackGroups
|
|
||||||
|
|
||||||
// Prepare a track selector parameters builder
|
if (!hasExtSubtitles && !PrefManager.getVal<Boolean>(PrefName.Subtitles)) {
|
||||||
val parametersBuilder = DefaultTrackSelector.ParametersBuilder(this)
|
onSetTrackGroupOverride(dummyTrack, TRACK_TYPE_TEXT)
|
||||||
|
|
||||||
// Iterate through the track groups to find the subtitle tracks
|
|
||||||
for (i in 0 until trackGroups.length) {
|
|
||||||
val trackGroup = trackGroups[i]
|
|
||||||
for (j in 0 until trackGroup.length) {
|
|
||||||
val trackMetadata = trackGroup.getFormat(j)
|
|
||||||
|
|
||||||
// Check if the track is a subtitle track
|
|
||||||
if (MimeTypes.isText(trackMetadata.sampleMimeType)) {
|
|
||||||
parametersBuilder.setRendererDisabled(i, false) // Enable the renderer for this track group
|
|
||||||
parametersBuilder.setSelectionOverride(i, trackGroups, DefaultTrackSelector.SelectionOverride(j, 0)) // Override to select this track
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Apply the track selector parameters to select the subtitle
|
|
||||||
trackSelector.setParameters(parametersBuilder)
|
|
||||||
}*/
|
|
||||||
|
|
||||||
private fun releasePlayer() {
|
private fun releasePlayer() {
|
||||||
isPlayerPlaying = exoPlayer.playWhenReady
|
isPlayerPlaying = exoPlayer.playWhenReady
|
||||||
|
@ -1809,7 +1797,8 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
if (isInitialized) {
|
if (isInitialized) {
|
||||||
val playerCurrentTime = exoPlayer.currentPosition / 1000
|
val playerCurrentTime = exoPlayer.currentPosition / 1000
|
||||||
currentTimeStamp = model.timeStamps.value?.find { timestamp ->
|
currentTimeStamp = model.timeStamps.value?.find { timestamp ->
|
||||||
timestamp.interval.startTime < playerCurrentTime && playerCurrentTime < (timestamp.interval.endTime - 1)
|
timestamp.interval.startTime < playerCurrentTime
|
||||||
|
&& playerCurrentTime < (timestamp.interval.endTime - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
val new = currentTimeStamp
|
val new = currentTimeStamp
|
||||||
|
@ -1830,11 +1819,13 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
timer = object : CountDownTimer(5000, 1000) {
|
timer = object : CountDownTimer(5000, 1000) {
|
||||||
override fun onTick(millisUntilFinished: Long) {
|
override fun onTick(millisUntilFinished: Long) {
|
||||||
|
if (new == null) {
|
||||||
skipTimeButton.visibility = View.GONE
|
skipTimeButton.visibility = View.GONE
|
||||||
exoSkip.isVisible = PrefManager.getVal<Int>(PrefName.SkipTime) > 0
|
exoSkip.isVisible = PrefManager.getVal<Int>(PrefName.SkipTime) > 0
|
||||||
disappeared = false
|
disappeared = false
|
||||||
functionstarted = false
|
functionstarted = false
|
||||||
cancelTimer()
|
cancelTimer()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFinish() {
|
override fun onFinish() {
|
||||||
|
@ -1886,46 +1877,98 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}, 500)
|
}, 500)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onSetTrackGroupOverride(trackGroup: Tracks.Group, type: @C.TrackType Int, index: Int = 0) {
|
||||||
|
val isDisabled = trackGroup.getTrackFormat(0).language == "none"
|
||||||
|
exoPlayer.trackSelectionParameters = exoPlayer.trackSelectionParameters
|
||||||
|
.buildUpon()
|
||||||
|
.setTrackTypeDisabled(TRACK_TYPE_TEXT, isDisabled)
|
||||||
|
.setOverrideForType(
|
||||||
|
TrackSelectionOverride(trackGroup.mediaTrackGroup, index)
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
if (type == TRACK_TYPE_TEXT) setupSubFormatting(playerView)
|
||||||
|
playerView.subtitleView?.alpha = when (isDisabled) {
|
||||||
|
false -> PrefManager.getVal(PrefName.SubAlpha)
|
||||||
|
true -> 0f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val dummyTrack = Tracks.Group(
|
||||||
|
TrackGroup("Dummy Track", Format.Builder().apply { setLanguage("none") }.build()),
|
||||||
|
true,
|
||||||
|
intArrayOf(1),
|
||||||
|
booleanArrayOf(false)
|
||||||
|
)
|
||||||
|
|
||||||
override fun onTracksChanged(tracks: Tracks) {
|
override fun onTracksChanged(tracks: Tracks) {
|
||||||
|
val audioTracks: ArrayList<Tracks.Group> = arrayListOf()
|
||||||
|
val subTracks: ArrayList<Tracks.Group> = arrayListOf(dummyTrack)
|
||||||
tracks.groups.forEach {
|
tracks.groups.forEach {
|
||||||
println("Track__: $it\nTrack__: ${it.length}\nTrack__: ${it.isSelected}\n" +
|
println("Track__: $it\nTrack__: ${it.length}\nTrack__: ${it.isSelected}\n" +
|
||||||
"Track__: ${it.type}\nTrack__: ${it.mediaTrackGroup.id}")
|
"Track__: ${it.type}\nTrack__: ${it.mediaTrackGroup.id}")
|
||||||
when (it.type) {
|
when (it.type) {
|
||||||
C.TRACK_TYPE_AUDIO -> { }
|
TRACK_TYPE_AUDIO -> {
|
||||||
C.TRACK_TYPE_TEXT -> {
|
if (it.isSupported(true)) audioTracks.add(it)
|
||||||
|
}
|
||||||
|
TRACK_TYPE_TEXT -> {
|
||||||
|
if (!hasExtSubtitles) {
|
||||||
|
if (
|
||||||
|
it.isSupported(true) &&
|
||||||
|
it.mediaTrackGroup.id != "Dummy Track"
|
||||||
|
) subTracks.add(it)
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
if (it.mediaTrackGroup.id == "1:") {
|
if (it.mediaTrackGroup.id == "1:") {
|
||||||
playerView.player?.trackSelectionParameters =
|
onSetTrackGroupOverride(it, TRACK_TYPE_TEXT, it.length - 1)
|
||||||
playerView.player?.trackSelectionParameters?.buildUpon()
|
|
||||||
?.setOverrideForType(
|
|
||||||
TrackSelectionOverride(it.mediaTrackGroup, it.length - 1)
|
|
||||||
)
|
|
||||||
?.build()!!
|
|
||||||
} else {
|
} else {
|
||||||
playerView.player?.trackSelectionParameters =
|
onSetTrackGroupOverride(dummyTrack, TRACK_TYPE_TEXT)
|
||||||
playerView.player?.trackSelectionParameters?.buildUpon()
|
|
||||||
?.addOverride(
|
|
||||||
TrackSelectionOverride(it.mediaTrackGroup, listOf())
|
|
||||||
)
|
|
||||||
?.build()!!
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
C.TRACK_TYPE_VIDEO -> { }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println("Track: ${tracks.groups.size}")
|
exoAudioTrack.isVisible = audioTracks.size > 1
|
||||||
|
exoAudioTrack.setOnClickListener {
|
||||||
|
TrackGroupDialogFragment(this, audioTracks, TRACK_TYPE_AUDIO)
|
||||||
|
.show(supportFragmentManager, "dialog")
|
||||||
|
}
|
||||||
|
if (!hasExtSubtitles) {
|
||||||
|
exoSubtitle.isVisible = subTracks.size > 1
|
||||||
|
exoSubtitle.setOnClickListener {
|
||||||
|
TrackGroupDialogFragment(this, subTracks, TRACK_TYPE_TEXT)
|
||||||
|
.show(supportFragmentManager, "dialog")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val onChangeSettings = registerForActivityResult(
|
||||||
|
ActivityResultContracts.StartActivityForResult()
|
||||||
|
) { _: ActivityResult ->
|
||||||
|
if (!hasExtSubtitles) {
|
||||||
|
exoPlayer.currentTracks.groups.forEach { trackGroup ->
|
||||||
|
when (trackGroup.type) {
|
||||||
|
TRACK_TYPE_TEXT -> {
|
||||||
|
if (PrefManager.getVal(PrefName.Subtitles)) {
|
||||||
|
onSetTrackGroupOverride(trackGroup, TRACK_TYPE_TEXT)
|
||||||
|
} else {
|
||||||
|
onSetTrackGroupOverride(dummyTrack, TRACK_TYPE_TEXT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isInitialized) exoPlayer.play()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPlayerError(error: PlaybackException) {
|
override fun onPlayerError(error: PlaybackException) {
|
||||||
when (error.errorCode) {
|
when (error.errorCode) {
|
||||||
PlaybackException.ERROR_CODE_IO_BAD_HTTP_STATUS, PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED
|
PlaybackException.ERROR_CODE_IO_BAD_HTTP_STATUS,
|
||||||
-> {
|
PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED -> {
|
||||||
toast("Source Exception : ${error.message}")
|
toast("Source Exception : ${error.message}")
|
||||||
isPlayerPlaying = true
|
isPlayerPlaying = true
|
||||||
sourceClick()
|
sourceClick()
|
||||||
}
|
}
|
||||||
|
else -> {
|
||||||
else
|
|
||||||
-> {
|
|
||||||
toast("Player Error ${error.errorCode} (${error.errorCodeName}) : ${error.message}")
|
toast("Player Error ${error.errorCode} (${error.errorCodeName}) : ${error.message}")
|
||||||
Injekt.get<CrashlyticsInterface>().logException(error)
|
Injekt.get<CrashlyticsInterface>().logException(error)
|
||||||
}
|
}
|
||||||
|
@ -1935,7 +1978,6 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
private var isBuffering = true
|
private var isBuffering = true
|
||||||
override fun onPlaybackStateChanged(playbackState: Int) {
|
override fun onPlaybackStateChanged(playbackState: Int) {
|
||||||
if (playbackState == ExoPlayer.STATE_READY) {
|
if (playbackState == ExoPlayer.STATE_READY) {
|
||||||
|
|
||||||
exoPlayer.play()
|
exoPlayer.play()
|
||||||
if (episodeLength == 0f) {
|
if (episodeLength == 0f) {
|
||||||
episodeLength = exoPlayer.duration.toFloat()
|
episodeLength = exoPlayer.duration.toFloat()
|
||||||
|
@ -1990,6 +2032,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("UnsafeIntentLaunch")
|
||||||
override fun onNewIntent(intent: Intent?) {
|
override fun onNewIntent(intent: Intent?) {
|
||||||
super.onNewIntent(intent)
|
super.onNewIntent(intent)
|
||||||
finishAndRemoveTask()
|
finishAndRemoveTask()
|
||||||
|
@ -2019,10 +2062,11 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
// Cast
|
// Cast
|
||||||
private fun cast() {
|
private fun cast() {
|
||||||
val videoURL = video?.file?.url ?: return
|
val videoURL = video?.file?.url ?: return
|
||||||
|
val subtitleUrl = if (!hasExtSubtitles) video!!.file.url else subtitle!!.file.url
|
||||||
val shareVideo = Intent(Intent.ACTION_VIEW)
|
val shareVideo = Intent(Intent.ACTION_VIEW)
|
||||||
shareVideo.setDataAndType(Uri.parse(videoURL), "video/*")
|
shareVideo.setDataAndType(Uri.parse(videoURL), "video/*")
|
||||||
shareVideo.setPackage("com.instantbits.cast.webvideo")
|
shareVideo.setPackage("com.instantbits.cast.webvideo")
|
||||||
if (subtitle != null) shareVideo.putExtra("subtitle", subtitle!!.file.url)
|
if (subtitle != null) shareVideo.putExtra("subtitle", subtitleUrl)
|
||||||
shareVideo.putExtra(
|
shareVideo.putExtra(
|
||||||
"title",
|
"title",
|
||||||
media.userPreferredName + " : Ep " + episodeTitleArr[currentEpisodeIndex]
|
media.userPreferredName + " : Ep " + episodeTitleArr[currentEpisodeIndex]
|
||||||
|
@ -2218,4 +2262,4 @@ class CustomCastButton : MediaRouteButton {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
package ani.dantotsu.media.anime
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.annotation.OptIn
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.media3.common.C.TRACK_TYPE_AUDIO
|
||||||
|
import androidx.media3.common.C.TrackType
|
||||||
|
import androidx.media3.common.Tracks
|
||||||
|
import androidx.media3.common.util.UnstableApi
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import ani.dantotsu.BottomSheetDialogFragment
|
||||||
|
import ani.dantotsu.R
|
||||||
|
import ani.dantotsu.databinding.BottomSheetSubtitlesBinding
|
||||||
|
import ani.dantotsu.databinding.ItemSubtitleTextBinding
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@OptIn(UnstableApi::class)
|
||||||
|
class TrackGroupDialogFragment(
|
||||||
|
instance: ExoplayerView, trackGroups: ArrayList<Tracks.Group>, type : @TrackType Int
|
||||||
|
) : BottomSheetDialogFragment() {
|
||||||
|
private var _binding: BottomSheetSubtitlesBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
private var instance: ExoplayerView
|
||||||
|
private var trackGroups: ArrayList<Tracks.Group>
|
||||||
|
private var type: @TrackType Int
|
||||||
|
|
||||||
|
init {
|
||||||
|
this.instance = instance
|
||||||
|
this.trackGroups = trackGroups
|
||||||
|
this.type = type
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
_binding = BottomSheetSubtitlesBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
if (type == TRACK_TYPE_AUDIO) binding.selectionTitle.text = getString(R.string.audio_tracks)
|
||||||
|
binding.subtitlesRecycler.layoutManager = LinearLayoutManager(requireContext())
|
||||||
|
binding.subtitlesRecycler.adapter = TrackGroupAdapter()
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class TrackGroupAdapter : RecyclerView.Adapter<TrackGroupAdapter.StreamViewHolder>() {
|
||||||
|
inner class StreamViewHolder(val binding: ItemSubtitleTextBinding) :
|
||||||
|
RecyclerView.ViewHolder(binding.root)
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StreamViewHolder =
|
||||||
|
StreamViewHolder(
|
||||||
|
ItemSubtitleTextBinding.inflate(
|
||||||
|
LayoutInflater.from(parent.context),
|
||||||
|
parent,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@OptIn(UnstableApi::class)
|
||||||
|
override fun onBindViewHolder(holder: StreamViewHolder, position: Int) {
|
||||||
|
val binding = holder.binding
|
||||||
|
trackGroups[position].let { trackGroup ->
|
||||||
|
when (val language = trackGroup.getTrackFormat(0).language?.lowercase()) {
|
||||||
|
null -> {
|
||||||
|
binding.subtitleTitle.text = getString(R.string.unknown_track, "Track $position")
|
||||||
|
}
|
||||||
|
"none" -> {
|
||||||
|
binding.subtitleTitle.text = getString(R.string.disabled_track)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val locale = if (language.contains("-")) {
|
||||||
|
val parts = language.split("-")
|
||||||
|
try {
|
||||||
|
Locale(parts[0], parts[1])
|
||||||
|
} catch (ignored: Exception) { null }
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
Locale(language)
|
||||||
|
} catch (ignored: Exception) { null }
|
||||||
|
}
|
||||||
|
binding.subtitleTitle.text = locale?.let {
|
||||||
|
"[${it.language}] ${it.displayName}"
|
||||||
|
|
||||||
|
} ?: getString(R.string.unknown_track, language)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (trackGroup.isSelected) {
|
||||||
|
val selected = "✔ ${binding.subtitleTitle.text}"
|
||||||
|
binding.subtitleTitle.text = selected
|
||||||
|
}
|
||||||
|
binding.root.setOnClickListener {
|
||||||
|
dismiss()
|
||||||
|
instance.onSetTrackGroupOverride(trackGroup, type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = trackGroups.size
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
_binding = null
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,7 @@
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
android:id="@+id/selectionTitle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="16dp"
|
android:layout_margin="16dp"
|
||||||
|
|
|
@ -132,11 +132,22 @@
|
||||||
android:layout_width="48dp"
|
android:layout_width="48dp"
|
||||||
android:layout_height="48dp"
|
android:layout_height="48dp"
|
||||||
android:backgroundTint="#00FFFFFF"
|
android:backgroundTint="#00FFFFFF"
|
||||||
|
android:visibility="gone"
|
||||||
android:scaleX="-1"
|
android:scaleX="-1"
|
||||||
android:src="@drawable/ic_round_subtitles_24"
|
android:src="@drawable/ic_round_subtitles_24"
|
||||||
app:tint="#fff"
|
app:tint="#fff"
|
||||||
tools:ignore="ContentDescription,SpeakableTextPresentCheck" />
|
tools:ignore="ContentDescription,SpeakableTextPresentCheck" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/exo_audio"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:backgroundTint="#00FFFFFF"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:src="@drawable/ic_round_audiotrack_24"
|
||||||
|
app:tint="#fff"
|
||||||
|
tools:ignore="ContentDescription,SpeakableTextPresentCheck" />
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
|
|
@ -891,7 +891,10 @@ Non quae tempore quo provident laudantium qui illo dolor vel quia dolor et exerc
|
||||||
<string name="social">Social</string>
|
<string name="social">Social</string>
|
||||||
<string name="auto_skip_recap">Auto Skip Recap</string>
|
<string name="auto_skip_recap">Auto Skip Recap</string>
|
||||||
<string name="use_anilist_icon">Use AniList Icon</string>
|
<string name="use_anilist_icon">Use AniList Icon</string>
|
||||||
|
<string name="audio_tracks">Audio Tracks</string>
|
||||||
|
<string name="disabled_track">Disabled</string>
|
||||||
|
<string name="invalid_track">Invalid</string>
|
||||||
|
<string name="unknown_track">\[%1$s\] Unknown</string>
|
||||||
<string name="accounts_desc">Anilist, MAL and Discord.\nWhat more could you need?</string>
|
<string name="accounts_desc">Anilist, MAL and Discord.\nWhat more could you need?</string>
|
||||||
<string name="theme_desc">Change the vibe of your app</string>
|
<string name="theme_desc">Change the vibe of your app</string>
|
||||||
<string name="extensions_desc">Manage your reliable repositories</string>
|
<string name="extensions_desc">Manage your reliable repositories</string>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue