feat: multi stream audio support

This commit is contained in:
rebelonion 2024-05-06 21:30:26 -05:00
parent abcf9fcbef
commit 636a56fb7f
15 changed files with 195 additions and 144 deletions

View file

@ -489,7 +489,7 @@ class AnimeWatchAdapter(
val adapter = ArrayAdapter(
fragment.requireContext(),
R.layout.item_dropdown,
parser.extension.sources.map { LanguageMapper.mapLanguageCodeToName(it.lang) }
parser.extension.sources.map { LanguageMapper.getLanguageCode(it.lang) }
)
val items = adapter.count

View file

@ -371,7 +371,7 @@ class AnimeWatchFragment : Fragment() {
var selectedSetting = allSettings[0]
if (allSettings.size > 1) {
val names =
allSettings.map { LanguageMapper.mapLanguageCodeToName(it.lang) }.toTypedArray()
allSettings.map { LanguageMapper.getLanguageCode(it.lang) }.toTypedArray()
val dialog = AlertDialog.Builder(requireContext(), R.style.MyPopup)
.setTitle("Select a Source")
.setSingleChoiceItems(names, -1) { dialog, which ->

View file

@ -82,7 +82,9 @@ import androidx.media3.datasource.cache.CacheDataSource
import androidx.media3.datasource.okhttp.OkHttpDataSource
import androidx.media3.exoplayer.DefaultLoadControl
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.hls.HlsMediaSource
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
import androidx.media3.exoplayer.source.MergingMediaSource
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
import androidx.media3.exoplayer.util.EventLogger
import androidx.media3.session.MediaSession
@ -126,6 +128,7 @@ import ani.dantotsu.media.SubtitleDownloader
import ani.dantotsu.okHttpClient
import ani.dantotsu.others.AniSkip
import ani.dantotsu.others.AniSkip.getType
import ani.dantotsu.others.LanguageMapper
import ani.dantotsu.others.ResettableTimer
import ani.dantotsu.others.getSerialized
import ani.dantotsu.parsers.AnimeSources
@ -190,6 +193,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
private lateinit var cacheFactory: CacheDataSource.Factory
private lateinit var playbackParameters: PlaybackParameters
private lateinit var mediaItem: MediaItem
private lateinit var mediaSource: MergingMediaSource
private var mediaSession: MediaSession? = null
private lateinit var binding: ActivityExoplayerBinding
@ -223,6 +227,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
private var downloadId: String? = null
private var hasExtSubtitles = false
private var audioLanguages = mutableListOf<String>()
companion object {
var initialized = false
@ -1484,8 +1489,8 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
val titleName = ext.server.name.split("/").first()
val episodeName = ext.server.name.split("/").last()
downloadId = PrefManager.getAnimeDownloadPreferences()
.getString("$titleName - $episodeName", null) ?:
PrefManager.getAnimeDownloadPreferences()
.getString("$titleName - $episodeName", null)
?: PrefManager.getAnimeDownloadPreferences()
.getString(ext.server.name, null)
val exoItem = if (downloadId != null) {
Helper.downloadManager(this)
@ -1540,6 +1545,32 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
}
val audioMediaItem = mutableListOf<MediaItem>()
audioLanguages.clear()
ext.audioTracks.forEach {
val code = LanguageMapper.getLanguageCode(it.lang)
audioLanguages.add(code)
audioMediaItem.add(
MediaItem.Builder()
.setUri(it.url)
.setMimeType(MimeTypes.AUDIO_UNKNOWN)
.setTag(code)
.build()
)
}
val audioSources = audioMediaItem.map { mediaItem ->
if (mediaItem.localConfiguration?.uri.toString().endsWith(".m3u8")) {
HlsMediaSource.Factory(cacheFactory).createMediaSource(mediaItem)
} else {
DefaultMediaSourceFactory(cacheFactory).createMediaSource(mediaItem)
}
}.toTypedArray()
val videoMediaSource = DefaultMediaSourceFactory(cacheFactory)
.createMediaSource(mediaItem)
mediaSource = MergingMediaSource(videoMediaSource, *audioSources)
//Source
exoSource.setOnClickListener {
sourceClick()
@ -1615,7 +1646,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
.build().apply {
playWhenReady = true
this.playbackParameters = this@ExoplayerView.playbackParameters
setMediaItem(mediaItem)
setMediaSource(mediaSource)
prepare()
PrefManager.getCustomVal(
"${media.id}_${media.anime!!.selectedEpisode}_max",
@ -1953,7 +1984,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
exoAudioTrack.isVisible = audioTracks.size > 1
exoAudioTrack.setOnClickListener {
TrackGroupDialogFragment(this, audioTracks, TRACK_TYPE_AUDIO)
TrackGroupDialogFragment(this, audioTracks, TRACK_TYPE_AUDIO, audioLanguages)
.show(supportFragmentManager, "dialog")
}
if (!hasExtSubtitles) {

View file

@ -19,19 +19,13 @@ import java.util.Locale
@OptIn(UnstableApi::class)
class TrackGroupDialogFragment(
instance: ExoplayerView, trackGroups: ArrayList<Tracks.Group>, type: @TrackType Int
private var instance: ExoplayerView,
private var trackGroups: ArrayList<Tracks.Group>,
private var type: @TrackType Int,
private var overrideTrackNames: List<String>? = null
) : 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,
@ -67,7 +61,8 @@ class TrackGroupDialogFragment(
override fun onBindViewHolder(holder: StreamViewHolder, position: Int) {
val binding = holder.binding
trackGroups[position].let { trackGroup ->
when (val language = trackGroup.getTrackFormat(0).language?.lowercase()) {
when (val language = overrideTrackNames?.getOrNull(position)
?: trackGroup.getTrackFormat(0).language?.lowercase()) {
null -> {
binding.subtitleTitle.text =
getString(R.string.unknown_track, "Track $position")
@ -94,7 +89,6 @@ class TrackGroupDialogFragment(
}
binding.subtitleTitle.text = locale?.let {
"[${it.language}] ${it.displayName}"
} ?: getString(R.string.unknown_track, language)
}
}

View file

@ -548,7 +548,7 @@ class MangaReadAdapter(
val adapter = ArrayAdapter(
fragment.requireContext(),
R.layout.item_dropdown,
parser.extension.sources.map { LanguageMapper.mapLanguageCodeToName(it.lang) }
parser.extension.sources.map { LanguageMapper.getLanguageCode(it.lang) }
)
val items = adapter.count
binding?.animeSourceLanguageContainer?.isVisible = items > 1

View file

@ -385,7 +385,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
var selectedSetting = allSettings[0]
if (allSettings.size > 1) {
val names =
allSettings.map { LanguageMapper.mapLanguageCodeToName(it.lang) }.toTypedArray()
allSettings.map { LanguageMapper.getLanguageCode(it.lang) }.toTypedArray()
val dialog = AlertDialog.Builder(requireContext(), R.style.MyPopup)
.setTitle("Select a Source")
.setSingleChoiceItems(names, -1) { dialog, which ->

View file

@ -1,122 +1,143 @@
package ani.dantotsu.others
import java.util.Locale
class LanguageMapper {
companion object {
fun mapLanguageCodeToName(code: String): String {
return when (code) {
"all" -> "Multi"
"af" -> "Afrikaans"
"am" -> "Amharic"
"ar" -> "Arabic"
"as" -> "Assamese"
"az" -> "Azerbaijani"
"be" -> "Belarusian"
"bg" -> "Bulgarian"
"bn" -> "Bengali"
"bs" -> "Bosnian"
"ca" -> "Catalan"
"ceb" -> "Cebuano"
"cs" -> "Czech"
"da" -> "Danish"
"de" -> "German"
"el" -> "Greek"
"en" -> "English"
"en-Us" -> "English (United States)"
"eo" -> "Esperanto"
"es" -> "Spanish"
"es-419" -> "Spanish (Latin America)"
"et" -> "Estonian"
"eu" -> "Basque"
"fa" -> "Persian"
"fi" -> "Finnish"
"fil" -> "Filipino"
"fo" -> "Faroese"
"fr" -> "French"
"ga" -> "Irish"
"gn" -> "Guarani"
"gu" -> "Gujarati"
"ha" -> "Hausa"
"he" -> "Hebrew"
"hi" -> "Hindi"
"hr" -> "Croatian"
"ht" -> "Haitian Creole"
"hu" -> "Hungarian"
"hy" -> "Armenian"
"id" -> "Indonesian"
"ig" -> "Igbo"
"is" -> "Icelandic"
"it" -> "Italian"
"ja" -> "Japanese"
"jv" -> "Javanese"
"ka" -> "Georgian"
"kk" -> "Kazakh"
"km" -> "Khmer"
"kn" -> "Kannada"
"ko" -> "Korean"
"ku" -> "Kurdish"
"ky" -> "Kyrgyz"
"la" -> "Latin"
"lb" -> "Luxembourgish"
"lo" -> "Lao"
"lt" -> "Lithuanian"
"lv" -> "Latvian"
"mg" -> "Malagasy"
"mi" -> "Maori"
"mk" -> "Macedonian"
"ml" -> "Malayalam"
"mn" -> "Mongolian"
"mo" -> "Moldovan"
"mr" -> "Marathi"
"ms" -> "Malay"
"mt" -> "Maltese"
"my" -> "Burmese"
"ne" -> "Nepali"
"nl" -> "Dutch"
"no" -> "Norwegian"
"ny" -> "Chichewa"
"pl" -> "Polish"
"pt" -> "Portuguese"
"pt-BR" -> "Portuguese (Brazil)"
"pt-PT" -> "Portuguese (Portugal)"
"ps" -> "Pashto"
"ro" -> "Romanian"
"rm" -> "Romansh"
"ru" -> "Russian"
"sd" -> "Sindhi"
"sh" -> "Serbo-Croatian"
"si" -> "Sinhala"
"sk" -> "Slovak"
"sl" -> "Slovenian"
"sm" -> "Samoan"
"sn" -> "Shona"
"so" -> "Somali"
"sq" -> "Albanian"
"sr" -> "Serbian"
"st" -> "Southern Sotho"
"sv" -> "Swedish"
"sw" -> "Swahili"
"ta" -> "Tamil"
"te" -> "Telugu"
"tg" -> "Tajik"
"th" -> "Thai"
"ti" -> "Tigrinya"
"tk" -> "Turkmen"
"tl" -> "Tagalog"
"to" -> "Tongan"
"tr" -> "Turkish"
"uk" -> "Ukrainian"
"ur" -> "Urdu"
"uz" -> "Uzbek"
"vi" -> "Vietnamese"
"yo" -> "Yoruba"
"zh" -> "Chinese"
"zh-Hans" -> "Chinese (Simplified)"
"zh-Hant" -> "Chinese (Traditional)"
"zh-Habt" -> "Chinese (Hakka)"
"zu" -> "Zulu"
else -> code
private val codeMap: Map<String, String> = mapOf(
"all" to "Multi",
"af" to "Afrikaans",
"am" to "Amharic",
"ar" to "Arabic",
"as" to "Assamese",
"az" to "Azerbaijani",
"be" to "Belarusian",
"bg" to "Bulgarian",
"bn" to "Bengali",
"bs" to "Bosnian",
"ca" to "Catalan",
"ceb" to "Cebuano",
"cs" to "Czech",
"da" to "Danish",
"de" to "German",
"el" to "Greek",
"en" to "English",
"en-Us" to "English (United States)",
"eo" to "Esperanto",
"es" to "Spanish",
"es-419" to "Spanish (Latin America)",
"es-ES" to "Spanish (Spain)",
"et" to "Estonian",
"eu" to "Basque",
"fa" to "Persian",
"fi" to "Finnish",
"fil" to "Filipino",
"fo" to "Faroese",
"fr" to "French",
"ga" to "Irish",
"gn" to "Guarani",
"gu" to "Gujarati",
"ha" to "Hausa",
"he" to "Hebrew",
"hi" to "Hindi",
"hr" to "Croatian",
"ht" to "Haitian Creole",
"hu" to "Hungarian",
"hy" to "Armenian",
"id" to "Indonesian",
"ig" to "Igbo",
"is" to "Icelandic",
"it" to "Italian",
"ja" to "Japanese",
"jv" to "Javanese",
"ka" to "Georgian",
"kk" to "Kazakh",
"km" to "Khmer",
"kn" to "Kannada",
"ko" to "Korean",
"ku" to "Kurdish",
"ky" to "Kyrgyz",
"la" to "Latin",
"lb" to "Luxembourgish",
"lo" to "Lao",
"lt" to "Lithuanian",
"lv" to "Latvian",
"mg" to "Malagasy",
"mi" to "Maori",
"mk" to "Macedonian",
"ml" to "Malayalam",
"mn" to "Mongolian",
"mo" to "Moldovan",
"mr" to "Marathi",
"ms" to "Malay",
"mt" to "Maltese",
"my" to "Burmese",
"ne" to "Nepali",
"nl" to "Dutch",
"no" to "Norwegian",
"ny" to "Chichewa",
"pl" to "Polish",
"pt" to "Portuguese",
"pt-BR" to "Portuguese (Brazil)",
"pt-PT" to "Portuguese (Portugal)",
"ps" to "Pashto",
"ro" to "Romanian",
"rm" to "Romansh",
"ru" to "Russian",
"sd" to "Sindhi",
"sh" to "Serbo-Croatian",
"si" to "Sinhala",
"sk" to "Slovak",
"sl" to "Slovenian",
"sm" to "Samoan",
"sn" to "Shona",
"so" to "Somali",
"sq" to "Albanian",
"sr" to "Serbian",
"st" to "Southern Sotho",
"sv" to "Swedish",
"sw" to "Swahili",
"ta" to "Tamil",
"te" to "Telugu",
"tg" to "Tajik",
"th" to "Thai",
"ti" to "Tigrinya",
"tk" to "Turkmen",
"tl" to "Tagalog",
"to" to "Tongan",
"tr" to "Turkish",
"uk" to "Ukrainian",
"ur" to "Urdu",
"uz" to "Uzbek",
"vi" to "Vietnamese",
"yo" to "Yoruba",
"zh" to "Chinese",
"zh-Hans" to "Chinese (Simplified)",
"zh-Hant" to "Chinese (Traditional)",
"zh-Habt" to "Chinese (Hakka)",
"zu" to "Zulu"
)
fun getLanguage(code: String): String {
return if (code.contains("-")) {
try {
val parts = code.split("-")
Locale(parts[0], parts[1]).displayName
} catch (ignored: Exception) {
code
}
} else {
try {
Locale(code).displayName
} catch (ignored: Exception) {
code
}
}
}
fun getLanguageCode(language: String): String {
return codeMap.filterValues { it.lowercase() == language.lowercase() }.keys.firstOrNull() ?: "all"
}
enum class Language(val code: String) {

View file

@ -511,9 +511,10 @@ class VideoServerPassthrough(private val videoServer: VideoServer) : VideoExtrac
override suspend fun extract(): VideoContainer {
val vidList = listOfNotNull(videoServer.video?.let { aniVideoToSaiVideo(it) })
val subList = videoServer.video?.subtitleTracks?.map { trackToSubtitle(it) } ?: emptyList()
val audioList = videoServer.video?.audioTracks ?: emptyList()
return if (vidList.isNotEmpty()) {
VideoContainer(vidList, subList)
VideoContainer(vidList, subList, audioList)
} else {
throw Exception("No videos found")
}

View file

@ -1,6 +1,7 @@
package ani.dantotsu.parsers
import ani.dantotsu.FileUrl
import eu.kanade.tachiyomi.animesource.model.Track
import java.io.Serializable
/**
@ -12,6 +13,7 @@ abstract class VideoExtractor : Serializable {
abstract val server: VideoServer
var videos: List<Video> = listOf()
var subtitles: List<Subtitle> = listOf()
var audioTracks: List<Track> = listOf()
/**
* Extracts videos & subtitles from the `embed`
@ -29,6 +31,7 @@ abstract class VideoExtractor : Serializable {
extract().also {
videos = it.videos
subtitles = it.subtitles
audioTracks = it.audioTracks
return this
}
}
@ -80,7 +83,8 @@ data class VideoServer(
* **/
data class VideoContainer(
val videos: List<Video>,
val subtitles: List<Subtitle> = listOf()
val subtitles: List<Subtitle> = listOf(),
val audioTracks: List<Track> = listOf(),
) : Serializable
/**

View file

@ -72,7 +72,7 @@ class InstalledAnimeExtensionsFragment : Fragment(), SearchQueryHandler {
if (allSettings.isNotEmpty()) {
var selectedSetting = allSettings[0]
if (allSettings.size > 1) {
val names = allSettings.map { LanguageMapper.mapLanguageCodeToName(it.lang) }
val names = allSettings.map { LanguageMapper.getLanguageCode(it.lang) }
.toTypedArray()
var selectedIndex = 0
val dialog = AlertDialog.Builder(requireContext(), R.style.MyPopup)
@ -295,7 +295,7 @@ class InstalledAnimeExtensionsFragment : Fragment(), SearchQueryHandler {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val extension = getItem(position)
val nsfw = if (extension.isNsfw) "(18+)" else ""
val lang = LanguageMapper.mapLanguageCodeToName(extension.lang)
val lang = LanguageMapper.getLanguageCode(extension.lang)
holder.extensionNameTextView.text = extension.name
val versionText = "$lang ${extension.versionName} $nsfw"
holder.extensionVersionTextView.text = versionText

View file

@ -71,7 +71,7 @@ class InstalledMangaExtensionsFragment : Fragment(), SearchQueryHandler {
if (allSettings.isNotEmpty()) {
var selectedSetting = allSettings[0]
if (allSettings.size > 1) {
val names = allSettings.map { LanguageMapper.mapLanguageCodeToName(it.lang) }
val names = allSettings.map { LanguageMapper.getLanguageCode(it.lang) }
.toTypedArray()
var selectedIndex = 0
val dialog = AlertDialog.Builder(requireContext(), R.style.MyPopup)
@ -293,7 +293,7 @@ class InstalledMangaExtensionsFragment : Fragment(), SearchQueryHandler {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val extension = getItem(position) // Use getItem() from ListAdapter
val nsfw = if (extension.isNsfw) "(18+)" else ""
val lang = LanguageMapper.mapLanguageCodeToName(extension.lang)
val lang = LanguageMapper.getLanguageCode(extension.lang)
holder.extensionNameTextView.text = extension.name
val versionText = "$lang ${extension.versionName} $nsfw"
holder.extensionVersionTextView.text = versionText

View file

@ -222,7 +222,7 @@ class InstalledNovelExtensionsFragment : Fragment(), SearchQueryHandler {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val extension = getItem(position) // Use getItem() from ListAdapter
val nsfw = ""
val lang = LanguageMapper.mapLanguageCodeToName("all")
val lang = LanguageMapper.getLanguageCode("all")
holder.extensionNameTextView.text = extension.name
val versionText = "$lang ${extension.versionName} $nsfw"
holder.extensionVersionTextView.text = versionText

View file

@ -203,7 +203,7 @@ class AnimeExtensionAdapter(private val clickListener: OnAnimeInstallClickListen
fun bind(extension: AnimeExtension.Available) {
val nsfw = if (extension.isNsfw) "(18+)" else ""
val lang = LanguageMapper.mapLanguageCodeToName(extension.lang)
val lang = LanguageMapper.getLanguageCode(extension.lang)
binding.extensionNameTextView.text = extension.name
val versionText = "$lang ${extension.versionName} $nsfw"
binding.extensionVersionTextView.text = versionText

View file

@ -201,7 +201,7 @@ class MangaExtensionAdapter(private val clickListener: OnMangaInstallClickListen
fun bind(extension: MangaExtension.Available) {
val nsfw = if (extension.isNsfw) "(18+)" else ""
val lang = LanguageMapper.mapLanguageCodeToName(extension.lang)
val lang = LanguageMapper.getLanguageCode(extension.lang)
binding.extensionNameTextView.text = extension.name
val versionText = "$lang ${extension.versionName} $nsfw"
binding.extensionVersionTextView.text = versionText

View file

@ -202,7 +202,7 @@ class NovelExtensionAdapter(private val clickListener: OnNovelInstallClickListen
val extensionIconImageView: ImageView = binding.extensionIconImageView
fun bind(extension: NovelExtension.Available) {
val nsfw = ""
val lang = LanguageMapper.mapLanguageCodeToName("all")
val lang = LanguageMapper.getLanguageCode("all")
binding.extensionNameTextView.text = extension.name
binding.extensionVersionTextView.text = "$lang ${extension.versionName} $nsfw"
}