This commit is contained in:
Finnley Somdahl
2023-12-01 01:22:15 -06:00
parent 1df528c0dc
commit afa960c808
171 changed files with 3458 additions and 1915 deletions

View File

@@ -15,7 +15,7 @@ data class Anime(
var ed: ArrayList<String> = arrayListOf(),
var mainStudio: Studio? = null,
var author: Author?=null,
var author: Author? = null,
var youtube: String? = null,
var nextAiringEpisode: Int? = null,

View File

@@ -1,23 +1,17 @@
package ani.dantotsu.media.anime
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import ani.dantotsu.*
import ani.dantotsu.databinding.ItemAnimeWatchBinding
import ani.dantotsu.databinding.ItemChipBinding
@@ -27,21 +21,11 @@ import ani.dantotsu.media.SourceSearchDialogFragment
import ani.dantotsu.parsers.AnimeSources
import ani.dantotsu.parsers.DynamicAnimeParser
import ani.dantotsu.parsers.WatchSources
import ani.dantotsu.settings.ExtensionsActivity
import ani.dantotsu.settings.extensionprefs.AnimeSourcePreferencesFragment
import ani.dantotsu.subcriptions.Notifications.Companion.openSettings
import ani.dantotsu.subcriptions.Subscription.Companion.getChannelId
import com.google.android.material.chip.Chip
import com.google.android.material.tabs.TabLayout
import com.google.android.material.textfield.TextInputLayout
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.lang.IndexOutOfBoundsException
class AnimeWatchAdapter(
private val media: Media,
@@ -72,23 +56,33 @@ class AnimeWatchAdapter(
}
binding.animeSourceDubbed.isChecked = media.selected!!.preferDub
binding.animeSourceDubbedText.text = if (media.selected!!.preferDub) currActivity()!!.getString(R.string.dubbed) else currActivity()!!.getString(R.string.subbed)
binding.animeSourceDubbedText.text =
if (media.selected!!.preferDub) currActivity()!!.getString(R.string.dubbed) else currActivity()!!.getString(
R.string.subbed
)
//PreferDub
var changing = false
binding.animeSourceDubbed.setOnCheckedChangeListener { _, isChecked ->
binding.animeSourceDubbedText.text = if (isChecked) currActivity()!!.getString(R.string.dubbed) else currActivity()!!.getString(R.string.subbed)
binding.animeSourceDubbedText.text =
if (isChecked) currActivity()!!.getString(R.string.dubbed) else currActivity()!!.getString(
R.string.subbed
)
if (!changing) fragment.onDubClicked(isChecked)
}
//Wrong Title
binding.animeSourceSearch.setOnClickListener {
SourceSearchDialogFragment().show(fragment.requireActivity().supportFragmentManager, null)
SourceSearchDialogFragment().show(
fragment.requireActivity().supportFragmentManager,
null
)
}
//Source Selection
var source = media.selected!!.sourceIndex.let { if (it >= watchSources.names.size) 0 else it }
setLanguageList(media.selected!!.langIndex,source)
var source =
media.selected!!.sourceIndex.let { if (it >= watchSources.names.size) 0 else it }
setLanguageList(media.selected!!.langIndex, source)
if (watchSources.names.isNotEmpty() && source in 0 until watchSources.names.size) {
binding.animeSource.setText(watchSources.names[source])
watchSources[source].apply {
@@ -100,7 +94,13 @@ class AnimeWatchAdapter(
}
}
binding.animeSource.setAdapter(ArrayAdapter(fragment.requireContext(), R.layout.item_dropdown, watchSources.names))
binding.animeSource.setAdapter(
ArrayAdapter(
fragment.requireContext(),
R.layout.item_dropdown,
watchSources.names
)
)
binding.animeSourceTitle.isSelected = true
binding.animeSource.setOnItemClickListener { _, _, i, _ ->
fragment.onSourceChange(i).apply {
@@ -109,9 +109,10 @@ class AnimeWatchAdapter(
changing = true
binding.animeSourceDubbed.isChecked = selectDub
changing = false
binding.animeSourceDubbedCont.visibility = if (isDubAvailableSeparately) View.VISIBLE else View.GONE
binding.animeSourceDubbedCont.visibility =
if (isDubAvailableSeparately) View.VISIBLE else View.GONE
source = i
setLanguageList(0,i)
setLanguageList(0, i)
}
subscribeButton(false)
fragment.loadEpisodes(i, false)
@@ -124,11 +125,13 @@ class AnimeWatchAdapter(
fragment.onLangChange(i)
fragment.onSourceChange(media.selected!!.sourceIndex).apply {
binding.animeSourceTitle.text = showUserText
showUserTextListener = { MainScope().launch { binding.animeSourceTitle.text = it } }
showUserTextListener =
{ MainScope().launch { binding.animeSourceTitle.text = it } }
changing = true
binding.animeSourceDubbed.isChecked = selectDub
changing = false
binding.animeSourceDubbedCont.visibility = if (isDubAvailableSeparately) View.VISIBLE else View.GONE
binding.animeSourceDubbedCont.visibility =
if (isDubAvailableSeparately) View.VISIBLE else View.GONE
setLanguageList(i, source)
}
subscribeButton(false)
@@ -146,7 +149,7 @@ class AnimeWatchAdapter(
//Subscription
subscribe = MediaDetailsActivity.PopImageButton(
subscribe = MediaDetailsActivity.PopImageButton(
fragment.lifecycleScope,
binding.animeSourceSubscribe,
R.drawable.ic_round_notifications_active_24,
@@ -161,7 +164,7 @@ class AnimeWatchAdapter(
subscribeButton(false)
binding.animeSourceSubscribe.setOnLongClickListener {
openSettings(fragment.requireContext(),getChannelId(true,media.id))
openSettings(fragment.requireContext(), getChannelId(true, media.id))
}
//Icons
@@ -200,12 +203,12 @@ class AnimeWatchAdapter(
style = 2
fragment.onIconPressed(style, reversed)
}
binding.animeScanlatorTop.visibility= View.GONE
binding.animeScanlatorTop.visibility = View.GONE
//Episode Handling
handleEpisodes()
}
fun subscribeButton(enabled : Boolean) {
fun subscribeButton(enabled: Boolean) {
subscribe?.enabled(enabled)
}
@@ -219,14 +222,26 @@ class AnimeWatchAdapter(
for (position in arr.indices) {
val last = if (position + 1 == arr.size) names.size else (limit * (position + 1))
val chip =
ItemChipBinding.inflate(LayoutInflater.from(fragment.context), binding.animeSourceChipGroup, false).root
ItemChipBinding.inflate(
LayoutInflater.from(fragment.context),
binding.animeSourceChipGroup,
false
).root
chip.isCheckable = true
fun selected() {
chip.isChecked = true
binding.animeWatchChipScroll.smoothScrollTo((chip.left - screenWidth / 2) + (chip.width / 2), 0)
binding.animeWatchChipScroll.smoothScrollTo(
(chip.left - screenWidth / 2) + (chip.width / 2),
0
)
}
chip.text = "${names[limit * (position)]} - ${names[last - 1]}"
chip.setTextColor(ContextCompat.getColorStateList(fragment.requireContext(), R.color.chip_text_color))
chip.setTextColor(
ContextCompat.getColorStateList(
fragment.requireContext(),
R.color.chip_text_color
)
)
chip.setOnClickListener {
selected()
@@ -239,7 +254,14 @@ class AnimeWatchAdapter(
}
}
if (select != null)
binding.animeWatchChipScroll.apply { post { scrollTo((select.left - screenWidth / 2) + (select.width / 2), 0) } }
binding.animeWatchChipScroll.apply {
post {
scrollTo(
(select.left - screenWidth / 2) + (select.width / 2),
0
)
}
}
}
}
@@ -281,7 +303,9 @@ class AnimeWatchAdapter(
}
}
val ep = media.anime.episodes!![continueEp]!!
binding.itemEpisodeImage.loadImage(ep.thumb ?: FileUrl[media.banner ?: media.cover], 0)
binding.itemEpisodeImage.loadImage(
ep.thumb ?: FileUrl[media.banner ?: media.cover], 0
)
if (ep.filler) binding.itemEpisodeFillerView.visibility = View.VISIBLE
binding.animeSourceContinueText.text =
currActivity()!!.getString(R.string.continue_episode) + "${ep.number}${if (ep.filler) " - Filler" else ""}${if (ep.title != null) "\n${ep.title}" else ""}"
@@ -321,10 +345,17 @@ class AnimeWatchAdapter(
}
try {
binding?.animeSourceLanguage?.setText(parser.extension.sources[lang].lang)
}catch (e: IndexOutOfBoundsException) {
binding?.animeSourceLanguage?.setText(parser.extension.sources.firstOrNull()?.lang ?: "Unknown")
} catch (e: IndexOutOfBoundsException) {
binding?.animeSourceLanguage?.setText(
parser.extension.sources.firstOrNull()?.lang ?: "Unknown"
)
}
binding?.animeSourceLanguage?.setAdapter(ArrayAdapter(fragment.requireContext(), R.layout.item_dropdown, parser.extension.sources.map { it.lang }))
binding?.animeSourceLanguage?.setAdapter(
ArrayAdapter(
fragment.requireContext(),
R.layout.item_dropdown,
parser.extension.sources.map { it.lang })
)
}
}
@@ -332,7 +363,8 @@ class AnimeWatchAdapter(
override fun getItemCount(): Int = 1
inner class ViewHolder(val binding: ItemAnimeWatchBinding) : RecyclerView.ViewHolder(binding.root) {
inner class ViewHolder(val binding: ItemAnimeWatchBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
//Timer
countDown(media, binding.animeSourceContainer)

View File

@@ -8,8 +8,6 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.Toast
import androidx.cardview.widget.CardView
import androidx.core.math.MathUtils
@@ -28,8 +26,6 @@ import ani.dantotsu.media.MediaDetailsViewModel
import ani.dantotsu.parsers.AnimeParser
import ani.dantotsu.parsers.AnimeSources
import ani.dantotsu.parsers.HAnimeSources
import ani.dantotsu.settings.ExtensionsActivity
import ani.dantotsu.settings.InstalledAnimeExtensionsFragment
import ani.dantotsu.settings.PlayerSettings
import ani.dantotsu.settings.UserInterfaceSettings
import ani.dantotsu.settings.extensionprefs.AnimeSourcePreferencesFragment
@@ -40,17 +36,12 @@ import ani.dantotsu.subcriptions.SubscriptionHelper
import ani.dantotsu.subcriptions.SubscriptionHelper.Companion.saveSubscription
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.navigationrail.NavigationRailView
import com.google.android.material.tabs.TabLayout
import com.google.android.material.textfield.TextInputLayout
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.launch
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.roundToInt
@@ -96,8 +87,10 @@ class AnimeWatchFragment : Fragment() {
maxGridSize = max(4, maxGridSize - (maxGridSize % 2))
playerSettings =
loadData("player_settings", toast = false) ?: PlayerSettings().apply { saveData("player_settings", this) }
uiSettings = loadData("ui_settings", toast = false) ?: UserInterfaceSettings().apply { saveData("ui_settings", this) }
loadData("player_settings", toast = false)
?: PlayerSettings().apply { saveData("player_settings", this) }
uiSettings = loadData("ui_settings", toast = false)
?: UserInterfaceSettings().apply { saveData("ui_settings", this) }
val gridLayoutManager = GridLayoutManager(requireContext(), maxGridSize)
@@ -106,11 +99,11 @@ class AnimeWatchFragment : Fragment() {
val style = episodeAdapter.getItemViewType(position)
return when (position) {
0 -> maxGridSize
0 -> maxGridSize
else -> when (style) {
0 -> maxGridSize
1 -> 2
2 -> 1
0 -> maxGridSize
1 -> 2
2 -> 1
else -> maxGridSize
}
}
@@ -129,7 +122,8 @@ class AnimeWatchFragment : Fragment() {
media = it
media.selected = model.loadSelected(media)
subscribed = SubscriptionHelper.getSubscriptions(requireContext()).containsKey(media.id)
subscribed =
SubscriptionHelper.getSubscriptions(requireContext()).containsKey(media.id)
style = media.selected!!.recyclerStyle
reverse = media.selected!!.recyclerReversed
@@ -141,9 +135,11 @@ class AnimeWatchFragment : Fragment() {
model.watchSources = if (media.isAdult) HAnimeSources else AnimeSources
headerAdapter = AnimeWatchAdapter(it, this, model.watchSources!!)
episodeAdapter = EpisodeAdapter(style ?: uiSettings.animeDefaultView, media, this)
episodeAdapter =
EpisodeAdapter(style ?: uiSettings.animeDefaultView, media, this)
binding.animeSourceRecycler.adapter = ConcatAdapter(headerAdapter, episodeAdapter)
binding.animeSourceRecycler.adapter =
ConcatAdapter(headerAdapter, episodeAdapter)
lifecycleScope.launch(Dispatchers.IO) {
awaitAll(
@@ -165,15 +161,20 @@ class AnimeWatchFragment : Fragment() {
episodes.forEach { (i, episode) ->
if (media.anime?.fillerEpisodes != null) {
if (media.anime!!.fillerEpisodes!!.containsKey(i)) {
episode.title = episode.title ?: media.anime!!.fillerEpisodes!![i]?.title
episode.title =
episode.title ?: media.anime!!.fillerEpisodes!![i]?.title
episode.filler = media.anime!!.fillerEpisodes!![i]?.filler ?: false
}
}
if (media.anime?.kitsuEpisodes != null) {
if (media.anime!!.kitsuEpisodes!!.containsKey(i)) {
episode.desc = episode.desc ?: media.anime!!.kitsuEpisodes!![i]?.desc
episode.title = episode.title ?: media.anime!!.kitsuEpisodes!![i]?.title
episode.thumb = episode.thumb ?: media.anime!!.kitsuEpisodes!![i]?.thumb ?: FileUrl[media.cover]
episode.desc =
episode.desc ?: media.anime!!.kitsuEpisodes!![i]?.desc
episode.title =
episode.title ?: media.anime!!.kitsuEpisodes!![i]?.title
episode.thumb =
episode.thumb ?: media.anime!!.kitsuEpisodes!![i]?.thumb
?: FileUrl[media.cover]
}
}
}
@@ -187,7 +188,7 @@ class AnimeWatchFragment : Fragment() {
val limit = when {
(divisions < 25) -> 25
(divisions < 50) -> 50
else -> 100
else -> 100
}
headerAdapter.clearChips()
if (total > limit) {
@@ -247,7 +248,12 @@ class AnimeWatchFragment : Fragment() {
selected.preferDub = checked
model.saveSelected(media.id, selected, requireActivity())
media.selected = selected
lifecycleScope.launch(Dispatchers.IO) { model.forceLoadEpisode(media, selected.sourceIndex) }
lifecycleScope.launch(Dispatchers.IO) {
model.forceLoadEpisode(
media,
selected.sourceIndex
)
}
}
fun loadEpisodes(i: Int, invalidate: Boolean) {
@@ -289,7 +295,8 @@ class AnimeWatchFragment : Fragment() {
else getString(R.string.unsubscribed_notification)
)
}
fun openSettings(pkg: AnimeExtension.Installed){
fun openSettings(pkg: AnimeExtension.Installed) {
val changeUIVisibility: (Boolean) -> Unit = { show ->
val activity = requireActivity() as MediaDetailsActivity
val visibility = if (show) View.VISIBLE else View.GONE
@@ -297,9 +304,9 @@ class AnimeWatchFragment : Fragment() {
activity.findViewById<ViewPager2>(R.id.mediaViewPager).visibility = visibility
activity.findViewById<CardView>(R.id.mediaCover).visibility = visibility
activity.findViewById<CardView>(R.id.mediaClose).visibility = visibility
try{
try {
activity.findViewById<CustomBottomNavBar>(R.id.mediaTab).visibility = visibility
}catch (e: ClassCastException){
} catch (e: ClassCastException) {
activity.findViewById<NavigationRailView>(R.id.mediaTab).visibility = visibility
}
activity.findViewById<FrameLayout>(R.id.fragmentExtensionsContainer).visibility =
@@ -363,46 +370,46 @@ class AnimeWatchFragment : Fragment() {
}
}
fun onEpisodeClick(i: String) {
model.continueMedia = false
model.saveSelected(media.id, media.selected!!, requireActivity())
model.onEpisodeClick(media, i, requireActivity().supportFragmentManager)
fun onEpisodeClick(i: String) {
model.continueMedia = false
model.saveSelected(media.id, media.selected!!, requireActivity())
model.onEpisodeClick(media, i, requireActivity().supportFragmentManager)
}
@SuppressLint("NotifyDataSetChanged")
private fun reload() {
val selected = model.loadSelected(media)
//Find latest episode for subscription
selected.latest =
media.anime?.episodes?.values?.maxOfOrNull { it.number.toFloatOrNull() ?: 0f } ?: 0f
selected.latest =
media.userProgress?.toFloat()?.takeIf { selected.latest < it } ?: selected.latest
model.saveSelected(media.id, selected, requireActivity())
headerAdapter.handleEpisodes()
episodeAdapter.notifyItemRangeRemoved(0, episodeAdapter.arr.size)
var arr: ArrayList<Episode> = arrayListOf()
if (media.anime!!.episodes != null) {
val end = if (end != null && end!! < media.anime!!.episodes!!.size) end else null
arr.addAll(
media.anime!!.episodes!!.values.toList()
.slice(start..(end ?: (media.anime!!.episodes!!.size - 1)))
)
if (reverse)
arr = (arr.reversed() as? ArrayList<Episode>) ?: arr
}
episodeAdapter.arr = arr
episodeAdapter.updateType(style ?: uiSettings.animeDefaultView)
episodeAdapter.notifyItemRangeInserted(0, arr.size)
}
@SuppressLint("NotifyDataSetChanged")
private fun reload() {
val selected = model.loadSelected(media)
override fun onDestroy() {
model.watchSources?.flushText()
super.onDestroy()
}
//Find latest episode for subscription
selected.latest =
media.anime?.episodes?.values?.maxOfOrNull { it.number.toFloatOrNull() ?: 0f } ?: 0f
selected.latest =
media.userProgress?.toFloat()?.takeIf { selected.latest < it } ?: selected.latest
model.saveSelected(media.id, selected, requireActivity())
headerAdapter.handleEpisodes()
episodeAdapter.notifyItemRangeRemoved(0, episodeAdapter.arr.size)
var arr: ArrayList<Episode> = arrayListOf()
if (media.anime!!.episodes != null) {
val end = if (end != null && end!! < media.anime!!.episodes!!.size) end else null
arr.addAll(
media.anime!!.episodes!!.values.toList()
.slice(start..(end ?: (media.anime!!.episodes!!.size - 1)))
)
if (reverse)
arr = (arr.reversed() as? ArrayList<Episode>) ?: arr
}
episodeAdapter.arr = arr
episodeAdapter.updateType(style ?: uiSettings.animeDefaultView)
episodeAdapter.notifyItemRangeInserted(0, arr.size)
}
override fun onDestroy() {
model.watchSources?.flushText()
super.onDestroy()
}
var state: Parcelable? = null
var state: Parcelable? = null
override fun onResume() {
super.onResume()
binding.mediaInfoProgressBar.visibility = progress

View File

@@ -14,12 +14,12 @@ data class Episode(
var selectedExtractor: String? = null,
var selectedVideo: Int = 0,
var selectedSubtitle: Int? = -1,
var extractors: MutableList<VideoExtractor>?=null,
@Transient var extractorCallback: ((VideoExtractor) -> Unit)?=null,
var extractors: MutableList<VideoExtractor>? = null,
@Transient var extractorCallback: ((VideoExtractor) -> Unit)? = null,
var allStreams: Boolean = false,
var watched: Long? = null,
var maxLength: Long? = null,
val extra: Map<String,String>?=null,
val extra: Map<String, String>? = null,
val sEpisode: eu.kanade.tachiyomi.animesource.model.SEpisode? = null
) : Serializable

View File

@@ -41,15 +41,30 @@ class EpisodeAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return (when (viewType) {
0 -> EpisodeListViewHolder(ItemEpisodeListBinding.inflate(LayoutInflater.from(parent.context), parent, false))
1 -> EpisodeGridViewHolder(ItemEpisodeGridBinding.inflate(LayoutInflater.from(parent.context), parent, false))
2 -> EpisodeCompactViewHolder(
0 -> EpisodeListViewHolder(
ItemEpisodeListBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
1 -> EpisodeGridViewHolder(
ItemEpisodeGridBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
2 -> EpisodeCompactViewHolder(
ItemEpisodeCompactBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
else -> throw IllegalArgumentException()
})
}
@@ -62,15 +77,21 @@ class EpisodeAdapter(
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val ep = arr[position]
val title =
"${if (!ep.title.isNullOrEmpty() && ep.title != "null") "" else currContext()!!.getString(R.string.episode_singular)} ${if (!ep.title.isNullOrEmpty() && ep.title != "null") ep.title else ep.number}"
"${
if (!ep.title.isNullOrEmpty() && ep.title != "null") "" else currContext()!!.getString(
R.string.episode_singular
)
} ${if (!ep.title.isNullOrEmpty() && ep.title != "null") ep.title else ep.number}"
when (holder) {
is EpisodeListViewHolder -> {
is EpisodeListViewHolder -> {
val binding = holder.binding
setAnimation(fragment.requireContext(), holder.binding.root, fragment.uiSettings)
val thumb = ep.thumb?.let { if(it.url.isNotEmpty()) GlideUrl(it.url) { it.headers } else null }
Glide.with(binding.itemEpisodeImage).load(thumb?:media.cover).override(400,0).into(binding.itemEpisodeImage)
val thumb =
ep.thumb?.let { if (it.url.isNotEmpty()) GlideUrl(it.url) { it.headers } else null }
Glide.with(binding.itemEpisodeImage).load(thumb ?: media.cover).override(400, 0)
.into(binding.itemEpisodeImage)
binding.itemEpisodeNumber.text = ep.number
binding.itemEpisodeTitle.text = title
@@ -81,7 +102,8 @@ class EpisodeAdapter(
binding.itemEpisodeFiller.visibility = View.GONE
binding.itemEpisodeFillerView.visibility = View.GONE
}
binding.itemEpisodeDesc.visibility = if (ep.desc != null && ep.desc?.trim(' ') != "") View.VISIBLE else View.GONE
binding.itemEpisodeDesc.visibility =
if (ep.desc != null && ep.desc?.trim(' ') != "") View.VISIBLE else View.GONE
binding.itemEpisodeDesc.text = ep.desc ?: ""
if (media.userProgress != null) {
@@ -110,12 +132,14 @@ class EpisodeAdapter(
)
}
is EpisodeGridViewHolder -> {
is EpisodeGridViewHolder -> {
val binding = holder.binding
setAnimation(fragment.requireContext(), holder.binding.root, fragment.uiSettings)
val thumb = ep.thumb?.let { if(it.url.isNotEmpty()) GlideUrl(it.url) { it.headers } else null }
Glide.with(binding.itemEpisodeImage).load(thumb?:media.cover).override(400,0).into(binding.itemEpisodeImage)
val thumb =
ep.thumb?.let { if (it.url.isNotEmpty()) GlideUrl(it.url) { it.headers } else null }
Glide.with(binding.itemEpisodeImage).load(thumb ?: media.cover).override(400, 0)
.into(binding.itemEpisodeImage)
binding.itemEpisodeNumber.text = ep.number
binding.itemEpisodeTitle.text = title
@@ -155,7 +179,8 @@ class EpisodeAdapter(
val binding = holder.binding
setAnimation(fragment.requireContext(), holder.binding.root, fragment.uiSettings)
binding.itemEpisodeNumber.text = ep.number
binding.itemEpisodeFillerView.visibility = if (ep.filler) View.VISIBLE else View.GONE
binding.itemEpisodeFillerView.visibility =
if (ep.filler) View.VISIBLE else View.GONE
if (media.userProgress != null) {
if ((ep.number.toFloatOrNull() ?: 9999f) <= media.userProgress!!.toFloat())
binding.itemEpisodeViewedCover.visibility = View.VISIBLE
@@ -180,7 +205,8 @@ class EpisodeAdapter(
override fun getItemCount(): Int = arr.size
inner class EpisodeCompactViewHolder(val binding: ItemEpisodeCompactBinding) : RecyclerView.ViewHolder(binding.root) {
inner class EpisodeCompactViewHolder(val binding: ItemEpisodeCompactBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
itemView.setOnClickListener {
if (bindingAdapterPosition < arr.size && bindingAdapterPosition >= 0)
@@ -189,7 +215,8 @@ class EpisodeAdapter(
}
}
inner class EpisodeGridViewHolder(val binding: ItemEpisodeGridBinding) : RecyclerView.ViewHolder(binding.root) {
inner class EpisodeGridViewHolder(val binding: ItemEpisodeGridBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
itemView.setOnClickListener {
if (bindingAdapterPosition < arr.size && bindingAdapterPosition >= 0)
@@ -198,7 +225,8 @@ class EpisodeAdapter(
}
}
inner class EpisodeListViewHolder(val binding: ItemEpisodeListBinding) : RecyclerView.ViewHolder(binding.root) {
inner class EpisodeListViewHolder(val binding: ItemEpisodeListBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
itemView.setOnClickListener {
if (bindingAdapterPosition < arr.size && bindingAdapterPosition >= 0)

View File

@@ -17,7 +17,6 @@ import android.graphics.drawable.Animatable
import android.hardware.SensorManager
import android.media.AudioManager
import android.media.AudioManager.*
import android.media.PlaybackParams
import android.net.Uri
import android.os.Build
import android.os.Bundle
@@ -74,6 +73,7 @@ import ani.dantotsu.media.SubtitleDownloader
import ani.dantotsu.others.AniSkip
import ani.dantotsu.others.AniSkip.getType
import ani.dantotsu.others.Download.download
import ani.dantotsu.others.LangSet
import ani.dantotsu.others.ResettableTimer
import ani.dantotsu.others.getSerialized
import ani.dantotsu.parsers.*
@@ -81,7 +81,6 @@ import ani.dantotsu.settings.PlayerSettings
import ani.dantotsu.settings.PlayerSettingsActivity
import ani.dantotsu.settings.UserInterfaceSettings
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.others.LangSet
import com.bumptech.glide.Glide
import com.google.android.material.slider.Slider
import com.google.firebase.crashlytics.FirebaseCrashlytics
@@ -1325,7 +1324,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener {
.setAllowMultipleAdaptiveSelections(true)
.setPreferredTextLanguage(subtitle?.language ?: "en")
.setPreferredTextRoleFlags(C.ROLE_FLAG_SUBTITLE)
.setRendererDisabled(C.TRACK_TYPE_VIDEO, false)
.setRendererDisabled(TRACK_TYPE_VIDEO, false)
.setRendererDisabled(C.TRACK_TYPE_AUDIO, false)
.setRendererDisabled(C.TRACK_TYPE_TEXT, false)
.setMinVideoSize(

View File

@@ -25,8 +25,6 @@ import ani.dantotsu.media.MediaDetailsViewModel
import ani.dantotsu.others.Download.download
import ani.dantotsu.parsers.VideoExtractor
import ani.dantotsu.parsers.VideoType
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.others.LangSet
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -54,7 +52,11 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = BottomSheetSelectorBinding.inflate(inflater, container, false)
val window = dialog?.window
window?.statusBarColor = Color.TRANSPARENT
@@ -71,7 +73,7 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
media = m
if (media != null && !loaded) {
loaded = true
val ep = media?.anime?.episodes?.get(media?.anime?.selectedEpisode)
val ep = media?.anime?.episodes?.get(media?.anime?.selectedEpisode)
episode = ep
if (ep != null) {
@@ -92,14 +94,17 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
}
fun load() {
val size = ep.extractors?.find { it.server.name == selected }?.videos?.size
if (size!=null && size >= media!!.selected!!.video) {
media!!.anime!!.episodes?.get(media!!.anime!!.selectedEpisode!!)?.selectedExtractor = selected
media!!.anime!!.episodes?.get(media!!.anime!!.selectedEpisode!!)?.selectedVideo = media!!.selected!!.video
val size =
ep.extractors?.find { it.server.name == selected }?.videos?.size
if (size != null && size >= media!!.selected!!.video) {
media!!.anime!!.episodes?.get(media!!.anime!!.selectedEpisode!!)?.selectedExtractor =
selected
media!!.anime!!.episodes?.get(media!!.anime!!.selectedEpisode!!)?.selectedVideo =
media!!.selected!!.video
startExoplayer(media!!)
} else fail()
}
if (ep.extractors.isNullOrEmpty()) {
model.getEpisode().observe(this) {
if (it != null) {
@@ -116,8 +121,7 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
}) fail()
}
} else load()
}
else {
} else {
binding.selectorRecyclerView.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = navBarHeight
}
@@ -130,10 +134,14 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
saveData("make_default", makeDefault)
}
binding.selectorRecyclerView.layoutManager =
LinearLayoutManager(requireActivity(), LinearLayoutManager.VERTICAL, false)
LinearLayoutManager(
requireActivity(),
LinearLayoutManager.VERTICAL,
false
)
val adapter = ExtractorAdapter()
binding.selectorRecyclerView.adapter = adapter
if (!ep.allStreams ) {
if (!ep.allStreams) {
ep.extractorCallback = {
scope.launch {
adapter.add(it)
@@ -141,12 +149,15 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
}
model.getEpisode().observe(this) {
if (it != null) {
media!!.anime?.episodes?.set(media!!.anime?.selectedEpisode!!, ep)
media!!.anime?.episodes?.set(
media!!.anime?.selectedEpisode!!,
ep
)
}
}
scope.launch(Dispatchers.IO) {
model.loadEpisodeVideos(ep, media!!.selected!!.sourceIndex)
withContext(Dispatchers.Main){
withContext(Dispatchers.Main) {
binding.selectorProgressBar.visibility = View.GONE
}
}
@@ -175,7 +186,10 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
ExoplayerView.initialized = true
startActivity(intent)
} else {
model.setEpisode(media.anime!!.episodes!![media.anime.selectedEpisode!!]!!, "startExo no launch")
model.setEpisode(
media.anime!!.episodes!![media.anime.selectedEpisode!!]!!,
"startExo no launch"
)
}
}
@@ -186,54 +200,72 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
}
}
private inner class ExtractorAdapter : RecyclerView.Adapter<ExtractorAdapter.StreamViewHolder>() {
private inner class ExtractorAdapter :
RecyclerView.Adapter<ExtractorAdapter.StreamViewHolder>() {
val links = mutableListOf<VideoExtractor>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StreamViewHolder =
StreamViewHolder(ItemStreamBinding.inflate(LayoutInflater.from(parent.context), parent, false))
StreamViewHolder(
ItemStreamBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun onBindViewHolder(holder: StreamViewHolder, position: Int) {
val extractor = links[position]
holder.binding.streamName.text = extractor.server.name
holder.binding.streamRecyclerView.layoutManager = LinearLayoutManager(requireContext())
holder.binding.streamRecyclerView.adapter = VideoAdapter(extractor)
holder.binding.streamRecyclerView.layoutManager = LinearLayoutManager(requireContext())
holder.binding.streamRecyclerView.adapter = VideoAdapter(extractor)
}
override fun getItemCount(): Int = links.size
fun add(videoExtractor: VideoExtractor){
if(videoExtractor.videos.isNotEmpty()) {
fun add(videoExtractor: VideoExtractor) {
if (videoExtractor.videos.isNotEmpty()) {
links.add(videoExtractor)
notifyItemInserted(links.size - 1)
}
}
fun addAll(extractors: List<VideoExtractor>?) {
links.addAll(extractors?:return)
notifyItemRangeInserted(0,extractors.size)
links.addAll(extractors ?: return)
notifyItemRangeInserted(0, extractors.size)
}
private inner class StreamViewHolder(val binding: ItemStreamBinding) : RecyclerView.ViewHolder(binding.root)
private inner class StreamViewHolder(val binding: ItemStreamBinding) :
RecyclerView.ViewHolder(binding.root)
}
private inner class VideoAdapter(private val extractor : VideoExtractor) : RecyclerView.Adapter<VideoAdapter.UrlViewHolder>() {
private inner class VideoAdapter(private val extractor: VideoExtractor) :
RecyclerView.Adapter<VideoAdapter.UrlViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UrlViewHolder {
return UrlViewHolder(ItemUrlBinding.inflate(LayoutInflater.from(parent.context), parent, false))
return UrlViewHolder(
ItemUrlBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: UrlViewHolder, position: Int) {
val binding = holder.binding
val video = extractor.videos[position]
binding.urlQuality.text = if(video.quality!=null) "${video.quality}p" else "Default Quality"
binding.urlQuality.text =
if (video.quality != null) "${video.quality}p" else "Default Quality"
binding.urlNote.text = video.extraNote ?: ""
binding.urlNote.visibility = if (video.extraNote != null) View.VISIBLE else View.GONE
binding.urlDownload.visibility = View.VISIBLE
binding.urlDownload.setSafeOnClickListener {
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!.selectedExtractor = extractor.server.name
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!.selectedVideo = position
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!.selectedExtractor =
extractor.server.name
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!.selectedVideo =
position
binding.urlDownload.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
download(
requireActivity(),
@@ -245,10 +277,11 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
if (video.format == VideoType.CONTAINER) {
binding.urlSize.visibility = if (video.size != null) View.VISIBLE else View.GONE
binding.urlSize.text =
// if video size is null or 0, show "Unknown Size" else show the size in MB
(if (video.extraNote != null) " : " else "") + (if (video.size == 0.0) "Unknown Size" else (DecimalFormat("#.##").format(video.size ?: 0).toString()+ " MB"))
}
else {
// if video size is null or 0, show "Unknown Size" else show the size in MB
(if (video.extraNote != null) " : " else "") + (if (video.size == 0.0) "Unknown Size" else (DecimalFormat(
"#.##"
).format(video.size ?: 0).toString() + " MB"))
} else {
binding.urlQuality.text = "Multi Quality"
if ((loadData<Int>("settings_download_manager") ?: 0) == 0) {
binding.urlDownload.visibility = View.GONE
@@ -258,12 +291,15 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
override fun getItemCount(): Int = extractor.videos.size
private inner class UrlViewHolder(val binding: ItemUrlBinding) : RecyclerView.ViewHolder(binding.root) {
private inner class UrlViewHolder(val binding: ItemUrlBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
itemView.setSafeOnClickListener {
tryWith(true) {
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]?.selectedExtractor = extractor.server.name
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]?.selectedVideo = bindingAdapterPosition
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]?.selectedExtractor =
extractor.server.name
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]?.selectedVideo =
bindingAdapterPosition
if (makeDefault) {
media!!.selected!!.server = extractor.server.name
media!!.selected!!.video = bindingAdapterPosition
@@ -274,12 +310,12 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
}
itemView.setOnLongClickListener {
val video = extractor.videos[bindingAdapterPosition]
val intent= Intent(Intent.ACTION_VIEW).apply {
setDataAndType(Uri.parse(video.file.url),"video/*")
val intent = Intent(Intent.ACTION_VIEW).apply {
setDataAndType(Uri.parse(video.file.url), "video/*")
}
copyToClipboard(video.file.url,true)
copyToClipboard(video.file.url, true)
dismiss()
startActivity(Intent.createChooser(intent,"Open Video in :"))
startActivity(Intent.createChooser(intent, "Open Video in :"))
true
}
}
@@ -287,7 +323,11 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
}
companion object {
fun newInstance(server: String? = null, la: Boolean = true, prev: String? = null): SelectorDialogFragment =
fun newInstance(
server: String? = null,
la: Boolean = true,
prev: String? = null
): SelectorDialogFragment =
SelectorDialogFragment().apply {
arguments = Bundle().apply {
putString("server", server)

View File

@@ -24,7 +24,11 @@ class SubtitleDialogFragment : BottomSheetDialogFragment() {
val model: MediaDetailsViewModel by activityViewModels()
private lateinit var episode: Episode
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = BottomSheetSubtitlesBinding.inflate(inflater, container, false)
return binding.root
}
@@ -34,17 +38,27 @@ class SubtitleDialogFragment : BottomSheetDialogFragment() {
model.getMedia().observe(viewLifecycleOwner) { media ->
episode = media?.anime?.episodes?.get(media.anime.selectedEpisode) ?: return@observe
val currentExtractor = episode.extractors?.find { it.server.name == episode.selectedExtractor } ?: return@observe
val currentExtractor =
episode.extractors?.find { it.server.name == episode.selectedExtractor }
?: return@observe
binding.subtitlesRecycler.layoutManager = LinearLayoutManager(requireContext())
binding.subtitlesRecycler.adapter = SubtitleAdapter(currentExtractor.subtitles)
}
}
inner class SubtitleAdapter(val subtitles: List<Subtitle>) : RecyclerView.Adapter<SubtitleAdapter.StreamViewHolder>() {
inner class StreamViewHolder(val binding: ItemSubtitleTextBinding) : RecyclerView.ViewHolder(binding.root)
inner class SubtitleAdapter(val subtitles: List<Subtitle>) :
RecyclerView.Adapter<SubtitleAdapter.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))
StreamViewHolder(
ItemSubtitleTextBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun onBindViewHolder(holder: StreamViewHolder, position: Int) {
val binding = holder.binding
@@ -60,7 +74,7 @@ class SubtitleDialogFragment : BottomSheetDialogFragment() {
binding.root.setOnClickListener {
episode.selectedSubtitle = null
model.setEpisode(episode, "Subtitle")
model.getMedia().observe(viewLifecycleOwner){media ->
model.getMedia().observe(viewLifecycleOwner) { media ->
val mediaID: Int = media.id
saveData("subLang_${mediaID}", "None", activity)
}
@@ -87,7 +101,7 @@ class SubtitleDialogFragment : BottomSheetDialogFragment() {
"pl-PL" -> "[pl-PL] Polish"
"ro-RO" -> "[ro-RO] Romanian"
"sv-SE" -> "[sv-SE] Swedish"
else -> if(subtitles[position - 1].language matches Regex("([a-z]{2})-([A-Z]{2}|\\d{3})")) "[${subtitles[position - 1].language}]" else subtitles[position - 1].language
else -> if (subtitles[position - 1].language matches Regex("([a-z]{2})-([A-Z]{2}|\\d{3})")) "[${subtitles[position - 1].language}]" else subtitles[position - 1].language
}
model.getMedia().observe(viewLifecycleOwner) { media ->
val mediaID: Int = media.id
@@ -100,7 +114,7 @@ class SubtitleDialogFragment : BottomSheetDialogFragment() {
binding.root.setOnClickListener {
episode.selectedSubtitle = position - 1
model.setEpisode(episode, "Subtitle")
model.getMedia().observe(viewLifecycleOwner){media ->
model.getMedia().observe(viewLifecycleOwner) { media ->
val mediaID: Int = media.id
saveData("subLang_${mediaID}", subtitles[position - 1].language, activity)
}

View File

@@ -14,7 +14,10 @@ object VideoCache {
val databaseProvider = StandaloneDatabaseProvider(context)
if (simpleCache == null)
simpleCache = SimpleCache(
File(context.cacheDir, "exoplayer").also { it.deleteOnExit() }, // Ensures always fresh file
File(
context.cacheDir,
"exoplayer"
).also { it.deleteOnExit() }, // Ensures always fresh file
LeastRecentlyUsedCacheEvictor(300L * 1024L * 1024L),
databaseProvider
)