Improve downloads management (#179)
* Fix deleted downloads This commit fixes downloads getting deleted after a few weeks by android's cleanup system. This is fixed by downloading the files under the .download extension and renaming them when the download is completed. * Add retry download feature * Add indicator when download is ongoing * Refactor download code * Disable button on retry and clean up Co-authored-by: Jarne Demeulemeester <jarnedemeulemeester@gmail.com>
This commit is contained in:
parent
a22a65ec16
commit
45ccea57af
9 changed files with 118 additions and 62 deletions
|
@ -25,4 +25,8 @@ interface DownloadDatabaseDao {
|
||||||
|
|
||||||
@Query("update downloads set downloadId = :downloadId where id = :id")
|
@Query("update downloads set downloadId = :downloadId where id = :id")
|
||||||
fun updateDownloadId(id: UUID, downloadId: Long)
|
fun updateDownloadId(id: UUID, downloadId: Long)
|
||||||
|
|
||||||
|
@Query("SELECT EXISTS (SELECT 1 FROM downloads WHERE id = :id)")
|
||||||
|
fun exists(id: UUID): Boolean
|
||||||
|
|
||||||
}
|
}
|
|
@ -50,6 +50,11 @@ class EpisodeBottomSheetFragment : BottomSheetDialogFragment() {
|
||||||
binding.playButton.setOnClickListener {
|
binding.playButton.setOnClickListener {
|
||||||
binding.playButton.setImageResource(android.R.color.transparent)
|
binding.playButton.setImageResource(android.R.color.transparent)
|
||||||
binding.progressCircular.isVisible = true
|
binding.progressCircular.isVisible = true
|
||||||
|
if (viewModel.canRetry){
|
||||||
|
binding.playButton.isEnabled = false
|
||||||
|
viewModel.download()
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
viewModel.item?.let {
|
viewModel.item?.let {
|
||||||
if (!args.isOffline) {
|
if (!args.isOffline) {
|
||||||
playerViewModel.loadPlayerItems(it)
|
playerViewModel.loadPlayerItems(it)
|
||||||
|
@ -112,7 +117,7 @@ class EpisodeBottomSheetFragment : BottomSheetDialogFragment() {
|
||||||
|
|
||||||
binding.downloadButton.setOnClickListener {
|
binding.downloadButton.setOnClickListener {
|
||||||
binding.downloadButton.isEnabled = false
|
binding.downloadButton.isEnabled = false
|
||||||
viewModel.loadDownloadRequestItem(episodeId)
|
viewModel.download()
|
||||||
binding.downloadButton.setTintColor(R.color.red, requireActivity().theme)
|
binding.downloadButton.setTintColor(R.color.red, requireActivity().theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,8 +160,14 @@ class EpisodeBottomSheetFragment : BottomSheetDialogFragment() {
|
||||||
binding.progressBar.isVisible = true
|
binding.progressBar.isVisible = true
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.playButton.isEnabled = available
|
val clickable = available || canRetry
|
||||||
binding.playButton.alpha = if (!available) 0.5F else 1.0F
|
binding.playButton.isEnabled = clickable
|
||||||
|
binding.playButton.alpha = if (!clickable) 0.5F else 1.0F
|
||||||
|
binding.playButton.setImageResource(if (!canRetry) R.drawable.ic_play else R.drawable.ic_rotate_ccw)
|
||||||
|
if (!clickable) {
|
||||||
|
binding.playButton.setImageResource(android.R.color.transparent)
|
||||||
|
binding.progressCircular.isVisible = true
|
||||||
|
}
|
||||||
|
|
||||||
// Check icon
|
// Check icon
|
||||||
when (played) {
|
when (played) {
|
||||||
|
|
|
@ -121,6 +121,11 @@ class MediaInfoFragment : Fragment() {
|
||||||
binding.playButton.setOnClickListener {
|
binding.playButton.setOnClickListener {
|
||||||
binding.playButton.setImageResource(android.R.color.transparent)
|
binding.playButton.setImageResource(android.R.color.transparent)
|
||||||
binding.progressCircular.isVisible = true
|
binding.progressCircular.isVisible = true
|
||||||
|
if (viewModel.canRetry){
|
||||||
|
binding.playButton.isEnabled = false
|
||||||
|
viewModel.download()
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
viewModel.item?.let { item ->
|
viewModel.item?.let { item ->
|
||||||
if (!args.isOffline) {
|
if (!args.isOffline) {
|
||||||
playerViewModel.loadPlayerItems(item) {
|
playerViewModel.loadPlayerItems(item) {
|
||||||
|
@ -174,7 +179,7 @@ class MediaInfoFragment : Fragment() {
|
||||||
|
|
||||||
binding.downloadButton.setOnClickListener {
|
binding.downloadButton.setOnClickListener {
|
||||||
binding.downloadButton.isEnabled = false
|
binding.downloadButton.isEnabled = false
|
||||||
viewModel.loadDownloadRequestItem(args.itemId)
|
viewModel.download()
|
||||||
binding.downloadButton.imageTintList = ColorStateList.valueOf(
|
binding.downloadButton.imageTintList = ColorStateList.valueOf(
|
||||||
resources.getColor(
|
resources.getColor(
|
||||||
R.color.red,
|
R.color.red,
|
||||||
|
@ -205,8 +210,14 @@ class MediaInfoFragment : Fragment() {
|
||||||
binding.communityRating.isVisible = item.communityRating != null
|
binding.communityRating.isVisible = item.communityRating != null
|
||||||
binding.actors.isVisible = actors.isNotEmpty()
|
binding.actors.isVisible = actors.isNotEmpty()
|
||||||
|
|
||||||
binding.playButton.isEnabled = available
|
val clickable = available || canRetry
|
||||||
binding.playButton.alpha = if (!available) 0.5F else 1.0F
|
binding.playButton.isEnabled = clickable
|
||||||
|
binding.playButton.alpha = if (!clickable) 0.5F else 1.0F
|
||||||
|
binding.playButton.setImageResource(if (!canRetry) R.drawable.ic_play else R.drawable.ic_rotate_ccw)
|
||||||
|
if (!clickable) {
|
||||||
|
binding.playButton.setImageResource(android.R.color.transparent)
|
||||||
|
binding.progressCircular.isVisible = true
|
||||||
|
}
|
||||||
|
|
||||||
// Check icon
|
// Check icon
|
||||||
when (played) {
|
when (played) {
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
package dev.jdtech.jellyfin.models
|
|
||||||
|
|
||||||
import android.os.Parcelable
|
|
||||||
import kotlinx.parcelize.Parcelize
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
@Parcelize
|
|
||||||
data class DownloadRequestItem(
|
|
||||||
val uri: String,
|
|
||||||
val itemId: UUID,
|
|
||||||
val item: DownloadItem
|
|
||||||
) : Parcelable
|
|
|
@ -8,7 +8,6 @@ import androidx.core.content.getSystemService
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import dev.jdtech.jellyfin.database.DownloadDatabaseDao
|
import dev.jdtech.jellyfin.database.DownloadDatabaseDao
|
||||||
import dev.jdtech.jellyfin.models.DownloadItem
|
import dev.jdtech.jellyfin.models.DownloadItem
|
||||||
import dev.jdtech.jellyfin.models.DownloadRequestItem
|
|
||||||
import dev.jdtech.jellyfin.models.DownloadSeriesMetadata
|
import dev.jdtech.jellyfin.models.DownloadSeriesMetadata
|
||||||
import dev.jdtech.jellyfin.models.PlayerItem
|
import dev.jdtech.jellyfin.models.PlayerItem
|
||||||
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
||||||
|
@ -21,31 +20,37 @@ import java.util.UUID
|
||||||
|
|
||||||
var defaultStorage: File? = null
|
var defaultStorage: File? = null
|
||||||
|
|
||||||
fun requestDownload(
|
suspend fun requestDownload(
|
||||||
|
jellyfinRepository: JellyfinRepository,
|
||||||
downloadDatabase: DownloadDatabaseDao,
|
downloadDatabase: DownloadDatabaseDao,
|
||||||
uri: Uri,
|
context: Context,
|
||||||
downloadRequestItem: DownloadRequestItem,
|
itemId: UUID
|
||||||
context: Context
|
|
||||||
) {
|
) {
|
||||||
val downloadRequest = DownloadManager.Request(uri)
|
val episode = jellyfinRepository.getItem(itemId)
|
||||||
.setTitle(downloadRequestItem.item.name)
|
val uri = jellyfinRepository.getStreamUrl(itemId, episode.mediaSources?.get(0)?.id!!)
|
||||||
|
val metadata = baseItemDtoToDownloadMetadata(episode)
|
||||||
|
|
||||||
|
val downloadRequest = DownloadManager.Request(Uri.parse(uri))
|
||||||
|
.setTitle(metadata.name)
|
||||||
.setDescription("Downloading")
|
.setDescription("Downloading")
|
||||||
.setDestinationUri(
|
.setDestinationUri(
|
||||||
Uri.fromFile(
|
Uri.fromFile(
|
||||||
File(
|
File(
|
||||||
defaultStorage,
|
defaultStorage,
|
||||||
downloadRequestItem.itemId.toString()
|
metadata.id.toString() + ".downloading"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
downloadDatabase.insertItem(downloadRequestItem.item)
|
if (downloadDatabase.exists(metadata.id))
|
||||||
if (!File(defaultStorage, downloadRequestItem.itemId.toString()).exists()) {
|
downloadDatabase.deleteItem(metadata.id)
|
||||||
|
downloadDatabase.insertItem(metadata)
|
||||||
|
if (!File(defaultStorage, metadata.id.toString()).exists() && !File(defaultStorage, "${metadata.id}.downloading").exists()) {
|
||||||
val downloadId = downloadFile(downloadRequest, context)
|
val downloadId = downloadFile(downloadRequest, context)
|
||||||
Timber.d("$downloadId")
|
Timber.d("$downloadId")
|
||||||
downloadDatabase.updateDownloadId(downloadRequestItem.itemId, downloadId)
|
downloadDatabase.updateDownloadId(metadata.id, downloadId)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
|
@ -68,6 +73,21 @@ fun loadDownloadLocation(context: Context) {
|
||||||
defaultStorage = context.getExternalFilesDir(Environment.DIRECTORY_MOVIES)
|
defaultStorage = context.getExternalFilesDir(Environment.DIRECTORY_MOVIES)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun checkDownloadStatus(downloadDatabase: DownloadDatabaseDao, context: Context) {
|
||||||
|
val items = downloadDatabase.loadItems()
|
||||||
|
for (item in items) {
|
||||||
|
try{
|
||||||
|
val query = DownloadManager.Query()
|
||||||
|
.setFilterById(item.downloadId!!)
|
||||||
|
val result = context.getSystemService<DownloadManager>()!!.query(query)
|
||||||
|
result.moveToFirst()
|
||||||
|
if (result.getInt(7) == 8) {
|
||||||
|
File(defaultStorage, "${item.id}.downloading").renameTo(File(defaultStorage, item.id.toString()))
|
||||||
|
}
|
||||||
|
} catch (_: Exception) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun loadDownloadedEpisodes(downloadDatabase: DownloadDatabaseDao): List<PlayerItem> {
|
fun loadDownloadedEpisodes(downloadDatabase: DownloadDatabaseDao): List<PlayerItem> {
|
||||||
val items = downloadDatabase.loadItems()
|
val items = downloadDatabase.loadItems()
|
||||||
return items.map {
|
return items.map {
|
||||||
|
@ -88,6 +108,19 @@ fun isItemAvailable(itemId: UUID): Boolean {
|
||||||
return File(defaultStorage, itemId.toString()).exists()
|
return File(defaultStorage, itemId.toString()).exists()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun canRetryDownload(itemId: UUID, downloadDatabaseDao: DownloadDatabaseDao, context: Context): Boolean {
|
||||||
|
if (isItemAvailable(itemId))
|
||||||
|
return false
|
||||||
|
val downloadId = downloadDatabaseDao.loadItem(itemId)?.downloadId ?: return false
|
||||||
|
val query = DownloadManager.Query().setFilterById(downloadId)
|
||||||
|
val result = context.getSystemService<DownloadManager>()!!.query(query)
|
||||||
|
result.moveToFirst()
|
||||||
|
if (result.count == 0)
|
||||||
|
return true
|
||||||
|
val status = result.getInt(result.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS))
|
||||||
|
return status == 16
|
||||||
|
}
|
||||||
|
|
||||||
fun isItemDownloaded(downloadDatabaseDao: DownloadDatabaseDao, itemId: UUID): Boolean {
|
fun isItemDownloaded(downloadDatabaseDao: DownloadDatabaseDao, itemId: UUID): Boolean {
|
||||||
val item = downloadDatabaseDao.loadItem(itemId)
|
val item = downloadDatabaseDao.loadItem(itemId)
|
||||||
return item != null
|
return item != null
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
package dev.jdtech.jellyfin.viewmodels
|
package dev.jdtech.jellyfin.viewmodels
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.*
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dev.jdtech.jellyfin.database.DownloadDatabaseDao
|
import dev.jdtech.jellyfin.database.DownloadDatabaseDao
|
||||||
import dev.jdtech.jellyfin.models.DownloadSection
|
import dev.jdtech.jellyfin.models.DownloadSection
|
||||||
import dev.jdtech.jellyfin.models.DownloadSeriesMetadata
|
import dev.jdtech.jellyfin.models.DownloadSeriesMetadata
|
||||||
import dev.jdtech.jellyfin.models.PlayerItem
|
import dev.jdtech.jellyfin.models.PlayerItem
|
||||||
|
import dev.jdtech.jellyfin.utils.checkDownloadStatus
|
||||||
import dev.jdtech.jellyfin.utils.loadDownloadedEpisodes
|
import dev.jdtech.jellyfin.utils.loadDownloadedEpisodes
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
@ -19,6 +21,7 @@ import javax.inject.Inject
|
||||||
class DownloadViewModel
|
class DownloadViewModel
|
||||||
@Inject
|
@Inject
|
||||||
constructor(
|
constructor(
|
||||||
|
private val application: Application,
|
||||||
private val downloadDatabase: DownloadDatabaseDao,
|
private val downloadDatabase: DownloadDatabaseDao,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
|
private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
|
||||||
|
@ -38,6 +41,7 @@ constructor(
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_uiState.emit(UiState.Loading)
|
_uiState.emit(UiState.Loading)
|
||||||
try {
|
try {
|
||||||
|
checkDownloadStatus(downloadDatabase, application)
|
||||||
val items = loadDownloadedEpisodes(downloadDatabase)
|
val items = loadDownloadedEpisodes(downloadDatabase)
|
||||||
|
|
||||||
val showsMap = mutableMapOf<UUID, MutableList<PlayerItem>>()
|
val showsMap = mutableMapOf<UUID, MutableList<PlayerItem>>()
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
package dev.jdtech.jellyfin.viewmodels
|
package dev.jdtech.jellyfin.viewmodels
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.net.Uri
|
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.*
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dev.jdtech.jellyfin.database.DownloadDatabaseDao
|
import dev.jdtech.jellyfin.database.DownloadDatabaseDao
|
||||||
import dev.jdtech.jellyfin.models.DownloadRequestItem
|
|
||||||
import dev.jdtech.jellyfin.models.PlayerItem
|
import dev.jdtech.jellyfin.models.PlayerItem
|
||||||
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
||||||
import dev.jdtech.jellyfin.utils.*
|
import dev.jdtech.jellyfin.utils.*
|
||||||
|
@ -42,8 +40,8 @@ constructor(
|
||||||
val favorite: Boolean,
|
val favorite: Boolean,
|
||||||
val canDownload: Boolean,
|
val canDownload: Boolean,
|
||||||
val downloaded: Boolean,
|
val downloaded: Boolean,
|
||||||
val downloadEpisode: Boolean,
|
|
||||||
val available: Boolean,
|
val available: Boolean,
|
||||||
|
val canRetry: Boolean
|
||||||
) : UiState()
|
) : UiState()
|
||||||
|
|
||||||
object Loading : UiState()
|
object Loading : UiState()
|
||||||
|
@ -57,12 +55,10 @@ constructor(
|
||||||
var favorite: Boolean = false
|
var favorite: Boolean = false
|
||||||
private var canDownload = false
|
private var canDownload = false
|
||||||
private var downloaded: Boolean = false
|
private var downloaded: Boolean = false
|
||||||
private var downloadEpisode: Boolean = false
|
|
||||||
private var available: Boolean = true
|
private var available: Boolean = true
|
||||||
|
var canRetry: Boolean = false
|
||||||
var playerItems: MutableList<PlayerItem> = mutableListOf()
|
var playerItems: MutableList<PlayerItem> = mutableListOf()
|
||||||
|
|
||||||
private lateinit var downloadRequestItem: DownloadRequestItem
|
|
||||||
|
|
||||||
fun loadEpisode(episodeId: UUID) {
|
fun loadEpisode(episodeId: UUID) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_uiState.emit(UiState.Loading)
|
_uiState.emit(UiState.Loading)
|
||||||
|
@ -84,8 +80,8 @@ constructor(
|
||||||
favorite,
|
favorite,
|
||||||
canDownload,
|
canDownload,
|
||||||
downloaded,
|
downloaded,
|
||||||
downloadEpisode,
|
|
||||||
available,
|
available,
|
||||||
|
canRetry,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -100,7 +96,7 @@ constructor(
|
||||||
playerItems.add(playerItem)
|
playerItems.add(playerItem)
|
||||||
item = downloadMetadataToBaseItemDto(playerItem.item!!)
|
item = downloadMetadataToBaseItemDto(playerItem.item!!)
|
||||||
available = isItemAvailable(playerItem.itemId)
|
available = isItemAvailable(playerItem.itemId)
|
||||||
Timber.d("Available: $available")
|
canRetry = canRetryDownload(playerItem.itemId, downloadDatabase, application)
|
||||||
_uiState.emit(
|
_uiState.emit(
|
||||||
UiState.Normal(
|
UiState.Normal(
|
||||||
item!!,
|
item!!,
|
||||||
|
@ -110,8 +106,8 @@ constructor(
|
||||||
favorite,
|
favorite,
|
||||||
canDownload,
|
canDownload,
|
||||||
downloaded,
|
downloaded,
|
||||||
downloadEpisode,
|
|
||||||
available,
|
available,
|
||||||
|
canRetry,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -161,18 +157,13 @@ constructor(
|
||||||
favorite = false
|
favorite = false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadDownloadRequestItem(itemId: UUID) {
|
fun download() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val episode = item
|
|
||||||
val uri = jellyfinRepository.getStreamUrl(itemId, episode?.mediaSources?.get(0)?.id!!)
|
|
||||||
val metadata = baseItemDtoToDownloadMetadata(episode)
|
|
||||||
downloadRequestItem = DownloadRequestItem(uri, itemId, metadata)
|
|
||||||
downloadEpisode = true
|
|
||||||
requestDownload(
|
requestDownload(
|
||||||
|
jellyfinRepository,
|
||||||
downloadDatabase,
|
downloadDatabase,
|
||||||
Uri.parse(downloadRequestItem.uri),
|
application,
|
||||||
downloadRequestItem,
|
item!!.id
|
||||||
application
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
package dev.jdtech.jellyfin.viewmodels
|
package dev.jdtech.jellyfin.viewmodels
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.net.Uri
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dev.jdtech.jellyfin.database.DownloadDatabaseDao
|
import dev.jdtech.jellyfin.database.DownloadDatabaseDao
|
||||||
import dev.jdtech.jellyfin.models.DownloadRequestItem
|
|
||||||
import dev.jdtech.jellyfin.models.PlayerItem
|
import dev.jdtech.jellyfin.models.PlayerItem
|
||||||
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
||||||
import dev.jdtech.jellyfin.utils.*
|
import dev.jdtech.jellyfin.utils.*
|
||||||
|
@ -50,6 +48,7 @@ constructor(
|
||||||
val favorite: Boolean,
|
val favorite: Boolean,
|
||||||
val canDownload: Boolean,
|
val canDownload: Boolean,
|
||||||
val downloaded: Boolean,
|
val downloaded: Boolean,
|
||||||
|
var canRetry: Boolean = false,
|
||||||
val available: Boolean,
|
val available: Boolean,
|
||||||
) : UiState()
|
) : UiState()
|
||||||
|
|
||||||
|
@ -71,11 +70,9 @@ constructor(
|
||||||
var favorite: Boolean = false
|
var favorite: Boolean = false
|
||||||
private var canDownload: Boolean = false
|
private var canDownload: Boolean = false
|
||||||
private var downloaded: Boolean = false
|
private var downloaded: Boolean = false
|
||||||
private var downloadMedia: Boolean = false
|
var canRetry: Boolean = false
|
||||||
private var available: Boolean = true
|
private var available: Boolean = true
|
||||||
|
|
||||||
private lateinit var downloadRequestItem: DownloadRequestItem
|
|
||||||
|
|
||||||
lateinit var playerItem: PlayerItem
|
lateinit var playerItem: PlayerItem
|
||||||
|
|
||||||
fun loadData(itemId: UUID, itemType: BaseItemKind) {
|
fun loadData(itemId: UUID, itemType: BaseItemKind) {
|
||||||
|
@ -115,6 +112,7 @@ constructor(
|
||||||
favorite,
|
favorite,
|
||||||
canDownload,
|
canDownload,
|
||||||
downloaded,
|
downloaded,
|
||||||
|
canRetry,
|
||||||
available
|
available
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -139,6 +137,7 @@ constructor(
|
||||||
played = tempItem.userData?.played ?: false
|
played = tempItem.userData?.played ?: false
|
||||||
favorite = tempItem.userData?.isFavorite ?: false
|
favorite = tempItem.userData?.isFavorite ?: false
|
||||||
available = isItemAvailable(tempItem.id)
|
available = isItemAvailable(tempItem.id)
|
||||||
|
canRetry = canRetryDownload(tempItem.id, downloadDatabase, application)
|
||||||
_uiState.emit(
|
_uiState.emit(
|
||||||
UiState.Normal(
|
UiState.Normal(
|
||||||
tempItem,
|
tempItem,
|
||||||
|
@ -155,6 +154,7 @@ constructor(
|
||||||
favorite,
|
favorite,
|
||||||
canDownload,
|
canDownload,
|
||||||
downloaded,
|
downloaded,
|
||||||
|
canRetry,
|
||||||
available
|
available
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -253,19 +253,13 @@ constructor(
|
||||||
return dateRange.joinToString(separator = " - ")
|
return dateRange.joinToString(separator = " - ")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadDownloadRequestItem(itemId: UUID) {
|
fun download() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val downloadItem = item
|
|
||||||
val uri =
|
|
||||||
jellyfinRepository.getStreamUrl(itemId, downloadItem?.mediaSources?.get(0)?.id!!)
|
|
||||||
val metadata = baseItemDtoToDownloadMetadata(downloadItem)
|
|
||||||
downloadRequestItem = DownloadRequestItem(uri, itemId, metadata)
|
|
||||||
downloadMedia = true
|
|
||||||
requestDownload(
|
requestDownload(
|
||||||
|
jellyfinRepository,
|
||||||
downloadDatabase,
|
downloadDatabase,
|
||||||
Uri.parse(downloadRequestItem.uri),
|
application,
|
||||||
downloadRequestItem,
|
item!!.id
|
||||||
application
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
20
app/src/main/res/drawable/ic_rotate_ccw.xml
Normal file
20
app/src/main/res/drawable/ic_rotate_ccw.xml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M3,2v6h6"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="@color/white"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M3,13a9,9 0,1 0,3 -7.7L3,8"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="@color/white"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
Loading…
Reference in a new issue