Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dismiss chapter sheet on chapter select #1895

Closed
wants to merge 12 commits into from
Closed

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import com.arthenica.ffmpegkit.Level
import com.arthenica.ffmpegkit.LogCallback
import com.arthenica.ffmpegkit.SessionState
import com.hippo.unifile.UniFile
import eu.kanade.domain.items.episode.model.toSEpisode
import eu.kanade.tachiyomi.animesource.UnmeteredSource
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
Expand All @@ -21,6 +20,8 @@ import eu.kanade.tachiyomi.data.download.anime.model.AnimeDownloadPart
import eu.kanade.tachiyomi.data.library.anime.AnimeLibraryUpdateNotifier
import eu.kanade.tachiyomi.data.notification.NotificationHandler
import eu.kanade.tachiyomi.network.ProgressListener
import eu.kanade.tachiyomi.ui.player.loader.EpisodeLoader
import eu.kanade.tachiyomi.ui.player.loader.HosterLoader
import eu.kanade.tachiyomi.util.size
import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.storage.toFFmpegString
Expand Down Expand Up @@ -367,14 +368,16 @@ class AnimeDownloader(
if (download.video == null) {
// Pull video from network and add them to download object
try {
val fetchedVideo = download.source.getVideoList(download.episode.toSEpisode()).first()
val hosters = EpisodeLoader.getHosters(download.episode, download.anime, download.source)
val fetchedVideo = HosterLoader.getBestVideo(download.source, hosters)!!

download.video = fetchedVideo
} catch (e: Exception) {
throw Exception(context.stringResource(MR.strings.video_list_empty_error))
}
}

if (download.video!!.videoUrl != null) getOrDownloadVideoFile(download, tmpDir)
getOrDownloadVideoFile(download, tmpDir)

ensureSuccessfulAnimeDownload(download, animeDir, tmpDir, episodeDirname)
} catch (e: Exception) {
Expand Down Expand Up @@ -436,7 +439,7 @@ class AnimeDownloader(
}
}

video.videoUrl = file.uri.path
video.videoUrl = file.uri.path ?: ""
download.progress = 100
video.status = Video.State.READY
progressJob?.cancel()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ internal object AnimeExtensionLoader {
private const val METADATA_HAS_README = "tachiyomi.animeextension.hasReadme"
private const val METADATA_HAS_CHANGELOG = "tachiyomi.animeextension.hasChangelog"
const val LIB_VERSION_MIN = 12
const val LIB_VERSION_MAX = 15
const val LIB_VERSION_MAX = 16

@Suppress("DEPRECATION")
private val PACKAGE_FLAGS = PackageManager.GET_CONFIGURATIONS or
Expand Down
14 changes: 12 additions & 2 deletions app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import eu.kanade.presentation.more.settings.screen.data.RestoreBackupScreen
import eu.kanade.presentation.util.AssistContentScreen
import eu.kanade.presentation.util.DefaultNavigatorScreenTransition
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.animesource.model.Hoster
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.core.common.Constants
import eu.kanade.tachiyomi.data.cache.ChapterCache
Expand Down Expand Up @@ -551,7 +552,9 @@ class MainActivity : BaseActivity() {
episodeId: Long,
extPlayer: Boolean,
video: Video? = null,
videoList: List<Video>? = null,
hosterIndex: Int = -1,
videoIndex: Int = -1,
hosterList: List<Hoster>? = null,
) {
if (extPlayer) {
val intent = try {
Expand All @@ -564,7 +567,14 @@ class MainActivity : BaseActivity() {
externalPlayerResult?.launch(intent) ?: return
} else {
context.startActivity(
PlayerActivity.newIntent(context, animeId, episodeId, videoList, videoList?.indexOf(video)),
PlayerActivity.newIntent(
context,
animeId,
episodeId,
hosterList,
hosterIndex,
videoIndex,
),
)
}
}
Expand Down
20 changes: 14 additions & 6 deletions app/src/main/java/eu/kanade/tachiyomi/ui/player/ExternalIntents.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import eu.kanade.tachiyomi.data.download.anime.AnimeDownloadManager
import eu.kanade.tachiyomi.data.track.AnimeTracker
import eu.kanade.tachiyomi.data.track.TrackerManager
import eu.kanade.tachiyomi.ui.player.loader.EpisodeLoader
import eu.kanade.tachiyomi.ui.player.loader.HosterLoader
import eu.kanade.tachiyomi.ui.player.settings.PlayerPreferences
import eu.kanade.tachiyomi.util.system.LocaleHelper
import eu.kanade.tachiyomi.util.system.isOnline
Expand Down Expand Up @@ -79,12 +80,13 @@ class ExternalIntents {
anime = getAnime.await(animeId) ?: return null
source = sourceManager.get(anime.source) ?: return null
episode = getEpisodesByAnimeId.await(anime.id).find { it.id == episodeId } ?: return null
val hosters = EpisodeLoader.getHosters(episode, anime, source)

val video = chosenVideo
?: EpisodeLoader.getLinks(episode, anime, source).firstOrNull()
?: HosterLoader.getBestVideo(source, hosters)
?: throw Exception("Video list is empty")

val videoUrl = getVideoUrl(context, video) ?: return null
val videoUrl = getVideoUrl(source, context, video) ?: return null

val pkgName = playerPreferences.externalPlayerPreference().get()

Expand All @@ -105,12 +107,18 @@ class ExternalIntents {
* @param context the application context.
* @param video the video being sent to the external player.
*/
private suspend fun getVideoUrl(context: Context, video: Video): Uri? {
if (video.videoUrl == null) {
makeErrorToast(context, Exception("Video URL is null."))
private suspend fun getVideoUrl(source: AnimeSource, context: Context, video: Video): Uri? {
val newVideoUrl = if (!video.initialized && source is AnimeHttpSource) {
source.resolveVideoUrl(video)
} else {
video.videoUrl
}

if (newVideoUrl.isEmpty()) {
makeErrorToast(context, Exception("Video URL is empty."))
return null
} else {
val uri = video.videoUrl!!.toUri()
val uri = newVideoUrl.toUri()

val isOnDevice = if (anime.source == LocalAnimeSource.ID) {
true
Expand Down
113 changes: 60 additions & 53 deletions app/src/main/java/eu/kanade/tachiyomi/ui/player/PlayerActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ import androidx.media.AudioFocusRequestCompat
import androidx.media.AudioManagerCompat
import com.hippo.unifile.UniFile
import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.tachiyomi.animesource.model.Hoster
import eu.kanade.tachiyomi.animesource.model.SerializableHoster.Companion.serialize
import eu.kanade.tachiyomi.animesource.model.SerializableVideo.Companion.serialize
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
Expand Down Expand Up @@ -153,14 +155,16 @@ class PlayerActivity : BaseActivity() {
context: Context,
animeId: Long?,
episodeId: Long?,
vidList: List<Video>? = null,
hostList: List<Hoster>? = null,
hostIndex: Int? = null,
vidIndex: Int? = null,
): Intent {
return Intent(context, PlayerActivity::class.java).apply {
putExtra("animeId", animeId)
putExtra("episodeId", episodeId)
hostIndex?.let { putExtra("hostIndex", it) }
vidIndex?.let { putExtra("vidIndex", it) }
vidList?.let { putExtra("vidList", it.serialize()) }
hostList?.let { putExtra("hostList", it.serialize()) }
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
}
}
Expand All @@ -171,8 +175,9 @@ class PlayerActivity : BaseActivity() {

val animeId = intent.extras?.getLong("animeId") ?: -1
val episodeId = intent.extras?.getLong("episodeId") ?: -1
val vidList = intent.extras?.getString("vidList") ?: ""
val vidIndex = intent.extras?.getInt("vidIndex") ?: 0
val hostList = intent.extras?.getString("hostList") ?: ""
val hostIndex = intent.extras?.getInt("hostIndex") ?: -1
val vidIndex = intent.extras?.getInt("vidIndex") ?: -1
if (animeId == -1L || episodeId == -1L) {
finish()
return
Expand All @@ -187,8 +192,9 @@ class PlayerActivity : BaseActivity() {

lifecycleScope.launchNonCancellable {
viewModel.updateIsLoadingEpisode(true)
viewModel.updateIsLoadingHosters(true)

val initResult = viewModel.init(animeId, episodeId, vidList, vidIndex)
val initResult = viewModel.init(animeId, episodeId, hostList, hostIndex, vidIndex)
if (!initResult.second.getOrDefault(false)) {
val exception = initResult.second.exceptionOrNull() ?: IllegalStateException(
"Unknown error",
Expand All @@ -198,11 +204,14 @@ class PlayerActivity : BaseActivity() {
}
}

viewModel.updateIsLoadingHosters(false)

lifecycleScope.launch {
setVideoList(
qualityIndex = initResult.first.videoIndex,
videos = initResult.first.videoList,
position = initResult.first.position,
viewModel.loadHosters(
source = viewModel.currentSource.value!!,
hosterList = initResult.first.hosterList ?: emptyList(),
hosterIndex = initResult.first.videoIndex.first,
videoIndex = initResult.first.videoIndex.second,
)
}
}
Expand All @@ -223,7 +232,9 @@ class PlayerActivity : BaseActivity() {
setupPlayerOrientation()

Thread.setDefaultUncaughtExceptionHandler { _, throwable ->
toast(throwable.message)
runOnUiThread {
toast(throwable.message)
}
logcat(LogPriority.ERROR, throwable)
finish()
}
Expand Down Expand Up @@ -950,10 +961,14 @@ class PlayerActivity : BaseActivity() {

lifecycleScope.launch {
viewModel.updateIsLoadingEpisode(true)
viewModel.updateIsLoadingHosters(true)

val pipEpisodeToasts = playerPreferences.pipEpisodeToasts().get()
val switchMethod = viewModel.loadEpisode(episodeId)

viewModel.updateIsLoadingHosters(false)

when (val switchMethod = viewModel.loadEpisode(episodeId)) {
when (switchMethod) {
null -> {
if (viewModel.currentAnime.value != null && !autoPlay) {
launchUI { toast(MR.strings.no_next_episode) }
Expand All @@ -962,21 +977,26 @@ class PlayerActivity : BaseActivity() {
}

else -> {
if (switchMethod.first != null) {
if (switchMethod.hosterList != null) {
when {
switchMethod.first!!.isEmpty() -> setInitialEpisodeError(
switchMethod.hosterList.isEmpty() -> setInitialEpisodeError(
Exception("Video list is empty."),
)
else -> {
setVideoList(qualityIndex = 0, switchMethod.first!!)
viewModel.loadHosters(
source = switchMethod.source,
hosterList = switchMethod.hosterList,
hosterIndex = -1,
videoIndex = -1,
)
}
}
} else {
logcat(LogPriority.ERROR) { "Error getting links" }
}

if (isInPictureInPictureMode && pipEpisodeToasts) {
launchUI { toast(switchMethod.second) }
launchUI { toast(switchMethod.episodeTitle) }
}
}
}
Expand All @@ -990,38 +1010,30 @@ class PlayerActivity : BaseActivity() {
)
}

fun setVideoList(
qualityIndex: Int,
videos: List<Video>?,
fromStart: Boolean = false,
position: Long? = null,
) {
fun setVideo(video: Video?, position: Long? = null) {
if (player.isExiting) return
viewModel.updateVideoList(videos ?: emptyList())
if (videos == null) return

videos.getOrNull(qualityIndex)?.let {
viewModel.setVideoIndex(qualityIndex)
setHttpOptions(it)
if (viewModel.isLoadingEpisode.value) {
viewModel.currentEpisode.value?.let { episode ->
val preservePos = playerPreferences.preserveWatchingPosition().get()
val resumePosition = position
?: if ((episode.seen && !preservePos) || fromStart) {
0L
} else {
episode.last_second_seen
}
MPVLib.command(arrayOf("set", "start", "${resumePosition / 1000F}"))
}
} else {
player.timePos?.let {
MPVLib.command(arrayOf("set", "start", "${player.timePos}"))
}
}
if (video == null) return

MPVLib.command(arrayOf("loadfile", parseVideoUrl(it.videoUrl)))
setHttpOptions(video)

if (viewModel.isLoadingEpisode.value) {
viewModel.currentEpisode.value?.let { episode ->
val preservePos = playerPreferences.preserveWatchingPosition().get()
val resumePosition = position
?: if (episode.seen && !preservePos) {
0L
} else {
episode.last_second_seen
}
MPVLib.command(arrayOf("set", "start", "${resumePosition / 1000F}"))
}
} else {
player.timePos?.let {
MPVLib.command(arrayOf("set", "start", "${player.timePos}"))
}
}

MPVLib.command(arrayOf("loadfile", parseVideoUrl(video.videoUrl)))
}

/**
Expand All @@ -1034,12 +1046,12 @@ class PlayerActivity : BaseActivity() {
finish()
}

private fun parseVideoUrl(videoUrl: String?): String? {
fun parseVideoUrl(videoUrl: String?): String? {
return Uri.parse(videoUrl).resolveUri(this)
?: videoUrl
}

private fun setHttpOptions(video: Video) {
fun setHttpOptions(video: Video) {
if (viewModel.isEpisodeOnline() != true) return
val source = viewModel.currentSource.value as? AnimeHttpSource ?: return

Expand Down Expand Up @@ -1133,10 +1145,8 @@ class PlayerActivity : BaseActivity() {
if (player.isExiting) return
viewModel.isLoadingTracks.update { _ -> true }

val audioTracks = viewModel.videoList.value.getOrNull(viewModel.selectedVideoIndex.value)
?.audioTracks?.takeIf { it.isNotEmpty() }
val subtitleTracks = viewModel.videoList.value.getOrNull(viewModel.selectedVideoIndex.value)
?.subtitleTracks?.takeIf { it.isNotEmpty() }
val audioTracks = viewModel.currentVideo.value?.audioTracks?.takeIf { it.isNotEmpty() }
val subtitleTracks = viewModel.currentVideo.value?.subtitleTracks?.takeIf { it.isNotEmpty() }

// If no external audio or subtitle tracks are present, loadTracks() won't be
// called and we need to call onFinishLoadingTracks() manually
Expand All @@ -1160,9 +1170,6 @@ class PlayerActivity : BaseActivity() {
val anime = viewModel.currentAnime.value ?: return
val episode = viewModel.currentEpisode.value ?: return

viewModel.animeTitle.update { _ -> anime.title }
viewModel.mediaTitle.update { _ -> episode.name }

// Write to mpv table
MPVLib.setPropertyString("user-data/current-anime/episode-title", episode.name)

Expand Down
Loading
Loading