download episode images

This commit is contained in:
Finnley Somdahl 2023-12-31 00:17:18 -06:00
parent 3ff492d94c
commit 7dbf951d5a
4 changed files with 56 additions and 34 deletions

View file

@ -29,6 +29,7 @@ import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.media3.common.util.UnstableApi import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.offline.Download
import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.adapter.FragmentStateAdapter
import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.AnilistHomeViewModel import ani.dantotsu.connections.anilist.AnilistHomeViewModel
@ -249,15 +250,19 @@ class MainActivity : AppCompatActivity() {
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
val index = Helper.downloadManager(this@MainActivity).downloadIndex val index = Helper.downloadManager(this@MainActivity).downloadIndex
if (index != null) {
val downloadCursor = index.getDownloads() val downloadCursor = index.getDownloads()
if (downloadCursor != null) {
while (downloadCursor.moveToNext()) { while (downloadCursor.moveToNext()) {
val download = downloadCursor.download val download = downloadCursor.download
Log.e("Downloader", download.request.uri.toString()) Log.e("Downloader", download.request.uri.toString())
Log.e("Downloader", download.request.id.toString()) Log.e("Downloader", download.request.id.toString())
Log.e("Downloader", download.request.mimeType.toString()) Log.e("Downloader", download.request.mimeType.toString())
} Log.e("Downloader", download.request.data.size.toString())
Log.e("Downloader", download.bytesDownloaded.toString())
Log.e("Downloader", download.state.toString())
Log.e("Downloader", download.failureReason.toString())
if (download.state == Download.STATE_FAILED) { //simple cleanup
Helper.downloadManager(this@MainActivity).removeDownload(download.request.id)
} }
} }
} }

View file

@ -20,6 +20,7 @@ import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.offline.Download import androidx.media3.exoplayer.offline.Download
import androidx.media3.exoplayer.offline.DownloadManager import androidx.media3.exoplayer.offline.DownloadManager
import androidx.media3.exoplayer.offline.DownloadService import androidx.media3.exoplayer.offline.DownloadService
import ani.dantotsu.FileUrl
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.currActivity import ani.dantotsu.currActivity
import ani.dantotsu.download.DownloadedType import ani.dantotsu.download.DownloadedType
@ -84,7 +85,6 @@ class AnimeDownloaderService : Service() {
setSmallIcon(R.drawable.ic_round_download_24) setSmallIcon(R.drawable.ic_round_download_24)
priority = NotificationCompat.PRIORITY_DEFAULT priority = NotificationCompat.PRIORITY_DEFAULT
setOnlyAlertOnce(true) setOnlyAlertOnce(true)
setProgress(0, 0, false)
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground( startForeground(
@ -216,43 +216,39 @@ class AnimeDownloaderService : Service() {
} }
saveMediaInfo(task) saveMediaInfo(task)
var continueDownload = false val downloadStarted = hasDownloadStarted(downloadManager, task, 30000) // 30 seconds timeout
downloadManager.addListener(
object : androidx.media3.exoplayer.offline.DownloadManager.Listener {
override fun onDownloadChanged(
downloadManager: DownloadManager,
download: Download,
finalException: Exception?
) {
continueDownload = true
}
}
)
//set an async timeout of 30 seconds before setting continueDownload to true if (!downloadStarted) {
launch { logger("Download failed to start")
kotlinx.coroutines.delay(30000) builder.setContentText("${task.title} - ${task.episode} Download failed to start")
continueDownload = true notificationManager.notify(NOTIFICATION_ID, builder.build())
snackString("${task.title} - ${task.episode} Download failed to start")
broadcastDownloadFailed(task.getTaskName())
return@withContext
} }
// periodically check if the download is complete // periodically check if the download is complete
while (downloadManager.downloadIndex.getDownload(task.video.file.url) != null || continueDownload == false) { while (downloadManager.downloadIndex.getDownload(task.video.file.url) != null) {
val download = downloadManager.downloadIndex.getDownload(task.video.file.url) val download = downloadManager.downloadIndex.getDownload(task.video.file.url)
if (download != null) { if (download != null) {
if (download.state == androidx.media3.exoplayer.offline.Download.STATE_FAILED) { if (download.state == androidx.media3.exoplayer.offline.Download.STATE_FAILED) {
logger("Download failed") logger("Download failed")
builder.setContentText("${task.title} - ${task.episode} Download failed") builder.setContentText("${task.title} - ${task.episode} Download failed")
.setProgress(0, 0, false)
notificationManager.notify(NOTIFICATION_ID, builder.build()) notificationManager.notify(NOTIFICATION_ID, builder.build())
snackString("${task.title} - ${task.episode} Download failed") snackString("${task.title} - ${task.episode} Download failed")
logger("Download failed: ${download.failureReason}")
FirebaseCrashlytics.getInstance().recordException(Exception("Anime Download failed:" +
" ${download.failureReason}" +
" url: ${task.video.file.url}" +
" title: ${task.title}" +
" episode: ${task.episode}"))
broadcastDownloadFailed(task.getTaskName()) broadcastDownloadFailed(task.getTaskName())
break break
} }
if (download.state == androidx.media3.exoplayer.offline.Download.STATE_COMPLETED) { if (download.state == androidx.media3.exoplayer.offline.Download.STATE_COMPLETED) {
logger("Download completed") logger("Download completed")
builder.setContentText("${task.title} - ${task.episode} Download completed") builder.setContentText("${task.title} - ${task.episode} Download completed")
.setProgress(0, 0, false)
notificationManager.notify(NOTIFICATION_ID, builder.build()) notificationManager.notify(NOTIFICATION_ID, builder.build())
snackString("${task.title} - ${task.episode} Download completed") snackString("${task.title} - ${task.episode} Download completed")
getSharedPreferences(getString(R.string.anime_downloads), Context.MODE_PRIVATE).edit().putString( getSharedPreferences(getString(R.string.anime_downloads), Context.MODE_PRIVATE).edit().putString(
@ -272,13 +268,11 @@ class AnimeDownloaderService : Service() {
if (download.state == androidx.media3.exoplayer.offline.Download.STATE_STOPPED) { if (download.state == androidx.media3.exoplayer.offline.Download.STATE_STOPPED) {
logger("Download stopped") logger("Download stopped")
builder.setContentText("${task.title} - ${task.episode} Download stopped") builder.setContentText("${task.title} - ${task.episode} Download stopped")
.setProgress(0, 0, false)
notificationManager.notify(NOTIFICATION_ID, builder.build()) notificationManager.notify(NOTIFICATION_ID, builder.build())
snackString("${task.title} - ${task.episode} Download stopped") snackString("${task.title} - ${task.episode} Download stopped")
break break
} }
broadcastDownloadProgress(task.getTaskName(), download.percentDownloaded.toInt()) broadcastDownloadProgress(task.getTaskName(), download.percentDownloaded.toInt())
builder.setProgress(100, download.percentDownloaded.toInt(), false)
if (notifi) { if (notifi) {
notificationManager.notify(NOTIFICATION_ID, builder.build()) notificationManager.notify(NOTIFICATION_ID, builder.build())
} }
@ -294,6 +288,19 @@ class AnimeDownloaderService : Service() {
} }
} }
@androidx.annotation.OptIn(UnstableApi::class) suspend fun hasDownloadStarted(downloadManager: DownloadManager, task: DownloadTask, timeout: Long): Boolean {
val startTime = System.currentTimeMillis()
while (System.currentTimeMillis() - startTime < timeout) {
val download = downloadManager.downloadIndex.getDownload(task.video.file.url)
if (download != null) {
return true
}
// Delay between each poll
kotlinx.coroutines.delay(500)
}
return false
}
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
private fun saveMediaInfo(task: DownloadTask) { private fun saveMediaInfo(task: DownloadTask) {
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
@ -323,6 +330,13 @@ class AnimeDownloaderService : Service() {
media.cover = media.cover?.let { downloadImage(it, directory, "cover.jpg") } media.cover = media.cover?.let { downloadImage(it, directory, "cover.jpg") }
media.banner = media.banner?.let { downloadImage(it, directory, "banner.jpg") } media.banner = media.banner?.let { downloadImage(it, directory, "banner.jpg") }
if (task.episodeImage != null) { if (task.episodeImage != null) {
media.anime?.episodes?.get(task.episode)?.let { episode ->
episode.thumb = downloadImage(task.episodeImage, episodeDirectory, "episodeImage.jpg")?.let {
FileUrl(
it
)
}
}
downloadImage(task.episodeImage, episodeDirectory, "episodeImage.jpg") downloadImage(task.episodeImage, episodeDirectory, "episodeImage.jpg")
} }

View file

@ -219,7 +219,8 @@ object Helper {
episode: String, episode: String,
video: Video, video: Video,
subtitle: Subtitle? = null, subtitle: Subtitle? = null,
sourceMedia: Media? = null sourceMedia: Media? = null,
episodeImage: String? = null
) { ) {
if (!isNotificationPermissionGranted(context)) { if (!isNotificationPermissionGranted(context)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
@ -236,7 +237,8 @@ object Helper {
episode, episode,
video, video,
subtitle, subtitle,
sourceMedia sourceMedia,
episodeImage
) )
val downloadsManger = Injekt.get<DownloadsManager>() val downloadsManger = Injekt.get<DownloadsManager>()

View file

@ -283,7 +283,8 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
episode.number, episode.number,
video, video,
null, null,
media media,
episode.thumb?.url?: media!!.banner?: media!!.cover
) )
} }
dismiss() dismiss()