Add "Refetch" option for albums and artists (#93)
This commit is contained in:
@@ -156,6 +156,7 @@ interface Database {
|
||||
authorsText = null,
|
||||
thumbnailUrl = null,
|
||||
shareUrl = null,
|
||||
timestamp = null,
|
||||
).also(::insert)
|
||||
|
||||
upsert(
|
||||
@@ -174,7 +175,8 @@ interface Database {
|
||||
id = artistIds[index],
|
||||
name = artistName,
|
||||
thumbnailUrl = null,
|
||||
info = null
|
||||
info = null,
|
||||
timestamp = null,
|
||||
).also(::insert)
|
||||
}
|
||||
}
|
||||
@@ -212,6 +214,9 @@ interface Database {
|
||||
@Delete
|
||||
fun delete(playlist: Playlist)
|
||||
|
||||
@Delete
|
||||
fun delete(playlist: Album)
|
||||
|
||||
@Delete
|
||||
fun delete(songPlaylistMap: SongPlaylistMap)
|
||||
|
||||
@@ -249,7 +254,7 @@ interface Database {
|
||||
views = [
|
||||
SortedSongPlaylistMap::class
|
||||
],
|
||||
version = 12,
|
||||
version = 13,
|
||||
exportSchema = true,
|
||||
autoMigrations = [
|
||||
AutoMigration(from = 1, to = 2),
|
||||
@@ -261,6 +266,7 @@ interface Database {
|
||||
AutoMigration(from = 7, to = 8, spec = DatabaseInitializer.From7To8Migration::class),
|
||||
AutoMigration(from = 9, to = 10),
|
||||
AutoMigration(from = 11, to = 12, spec = DatabaseInitializer.From11To12Migration::class),
|
||||
AutoMigration(from = 12, to = 13),
|
||||
],
|
||||
)
|
||||
@TypeConverters(Converters::class)
|
||||
|
||||
@@ -10,5 +10,6 @@ data class Album(
|
||||
val thumbnailUrl: String? = null,
|
||||
val year: String? = null,
|
||||
val authorsText: String? = null,
|
||||
val shareUrl: String? = null
|
||||
val shareUrl: String? = null,
|
||||
val timestamp: Long?
|
||||
)
|
||||
|
||||
@@ -13,4 +13,5 @@ data class Artist(
|
||||
val shufflePlaylistId: String? = null,
|
||||
val radioVideoId: String? = null,
|
||||
val radioPlaylistId: String? = null,
|
||||
val timestamp: Long?
|
||||
)
|
||||
@@ -45,6 +45,9 @@ import it.vfsfitvnm.youtubemusic.YouTube
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.text.DateFormat
|
||||
import java.util.*
|
||||
|
||||
|
||||
@ExperimentalAnimationApi
|
||||
@@ -56,32 +59,10 @@ fun AlbumScreen(
|
||||
|
||||
val albumResult by remember(browseId) {
|
||||
Database.album(browseId).map { album ->
|
||||
album?.takeIf {
|
||||
album.thumbnailUrl != null
|
||||
}?.let(Result.Companion::success) ?: YouTube.playlistOrAlbum(browseId)
|
||||
?.map { youtubeAlbum ->
|
||||
Album(
|
||||
id = browseId,
|
||||
title = youtubeAlbum.title,
|
||||
thumbnailUrl = youtubeAlbum.thumbnail?.url,
|
||||
year = youtubeAlbum.year,
|
||||
authorsText = youtubeAlbum.authors?.joinToString("") { it.name },
|
||||
shareUrl = youtubeAlbum.url
|
||||
).also(Database::upsert).also {
|
||||
youtubeAlbum.withAudioSources().items?.forEachIndexed { position, albumItem ->
|
||||
albumItem.toMediaItem(browseId, youtubeAlbum)?.let { mediaItem ->
|
||||
Database.insert(mediaItem)
|
||||
Database.upsert(
|
||||
SongAlbumMap(
|
||||
songId = mediaItem.mediaId,
|
||||
albumId = browseId,
|
||||
position = position
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
album
|
||||
?.takeIf { album.timestamp != null }
|
||||
?.let(Result.Companion::success)
|
||||
?: fetchAlbum(browseId)
|
||||
}.distinctUntilChanged()
|
||||
}.collectAsState(initial = null, context = Dispatchers.IO)
|
||||
|
||||
@@ -210,6 +191,25 @@ fun AlbumScreen(
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
MenuEntry(
|
||||
icon = R.drawable.download,
|
||||
text = "Refetch",
|
||||
secondaryText = albumResult?.getOrNull()?.timestamp?.let { timestamp ->
|
||||
"Last updated on ${DateFormat.getDateTimeInstance().format(Date(timestamp))}"
|
||||
},
|
||||
isEnabled = albumResult?.getOrNull() != null,
|
||||
onClick = {
|
||||
menuState.hide()
|
||||
|
||||
query {
|
||||
albumResult?.getOrNull()?.let(Database::delete)
|
||||
runBlocking(Dispatchers.IO) {
|
||||
fetchAlbum(browseId)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -392,3 +392,31 @@ private fun LoadingOrError(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchAlbum(browseId: String): Result<Album>? {
|
||||
return YouTube.playlistOrAlbum(browseId)
|
||||
?.map { youtubeAlbum ->
|
||||
Album(
|
||||
id = browseId,
|
||||
title = youtubeAlbum.title,
|
||||
thumbnailUrl = youtubeAlbum.thumbnail?.url,
|
||||
year = youtubeAlbum.year,
|
||||
authorsText = youtubeAlbum.authors?.joinToString("") { it.name },
|
||||
shareUrl = youtubeAlbum.url,
|
||||
timestamp = System.currentTimeMillis()
|
||||
).also(Database::upsert).also {
|
||||
youtubeAlbum.withAudioSources().items?.forEachIndexed { position, albumItem ->
|
||||
albumItem.toMediaItem(browseId, youtubeAlbum)?.let { mediaItem ->
|
||||
Database.insert(mediaItem)
|
||||
Database.upsert(
|
||||
SongAlbumMap(
|
||||
songId = mediaItem.mediaId,
|
||||
albumId = browseId,
|
||||
position = position
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,21 +82,10 @@ fun ArtistScreen(
|
||||
|
||||
val artistResult by remember(browseId) {
|
||||
Database.artist(browseId).map { artist ->
|
||||
artist?.takeIf {
|
||||
artist.shufflePlaylistId != null
|
||||
}?.let(Result.Companion::success) ?: YouTube.artist(browseId)
|
||||
?.map { youtubeArtist ->
|
||||
Artist(
|
||||
id = browseId,
|
||||
name = youtubeArtist.name,
|
||||
thumbnailUrl = youtubeArtist.thumbnail?.url,
|
||||
info = youtubeArtist.description,
|
||||
shuffleVideoId = youtubeArtist.shuffleEndpoint?.videoId,
|
||||
shufflePlaylistId = youtubeArtist.shuffleEndpoint?.playlistId,
|
||||
radioVideoId = youtubeArtist.radioEndpoint?.videoId,
|
||||
radioPlaylistId = youtubeArtist.radioEndpoint?.playlistId,
|
||||
).also(Database::upsert)
|
||||
}
|
||||
artist
|
||||
?.takeIf { artist.timestamp != null }
|
||||
?.let(Result.Companion::success)
|
||||
?: fetchArtist(browseId)
|
||||
}.distinctUntilChanged()
|
||||
}.collectAsState(initial = null, context = Dispatchers.IO)
|
||||
|
||||
@@ -139,17 +128,6 @@ fun ArtistScreen(
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.clip(CircleShape)
|
||||
.clickable {
|
||||
query {
|
||||
runBlocking {
|
||||
Database
|
||||
.artist(browseId)
|
||||
.first()
|
||||
?.copy(shufflePlaylistId = null)
|
||||
?.let(Database::update)
|
||||
}
|
||||
}
|
||||
}
|
||||
.size(Dimensions.thumbnails.artist)
|
||||
)
|
||||
|
||||
@@ -177,6 +155,12 @@ fun ArtistScreen(
|
||||
playlistId = artist.shufflePlaylistId
|
||||
)
|
||||
)
|
||||
|
||||
query {
|
||||
runBlocking {
|
||||
fetchArtist(browseId)
|
||||
}
|
||||
}
|
||||
}
|
||||
.shadow(elevation = 2.dp, shape = CircleShape)
|
||||
.background(
|
||||
@@ -200,6 +184,12 @@ fun ArtistScreen(
|
||||
playlistId = artist.radioPlaylistId
|
||||
)
|
||||
)
|
||||
|
||||
query {
|
||||
runBlocking {
|
||||
fetchArtist(browseId)
|
||||
}
|
||||
}
|
||||
}
|
||||
.shadow(elevation = 2.dp, shape = CircleShape)
|
||||
.background(
|
||||
@@ -285,7 +275,10 @@ fun ArtistScreen(
|
||||
|
||||
artistResult?.getOrNull()?.info?.let { description ->
|
||||
item {
|
||||
TextCard {
|
||||
TextCard(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Title(text = "Information")
|
||||
Text(text = description)
|
||||
}
|
||||
@@ -329,3 +322,20 @@ private fun LoadingOrError(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchArtist(browseId: String): Result<Artist>? {
|
||||
return YouTube.artist(browseId)
|
||||
?.map { youtubeArtist ->
|
||||
Artist(
|
||||
id = browseId,
|
||||
name = youtubeArtist.name,
|
||||
thumbnailUrl = youtubeArtist.thumbnail?.url,
|
||||
info = youtubeArtist.description,
|
||||
shuffleVideoId = youtubeArtist.shuffleEndpoint?.videoId,
|
||||
shufflePlaylistId = youtubeArtist.shuffleEndpoint?.playlistId,
|
||||
radioVideoId = youtubeArtist.radioEndpoint?.videoId,
|
||||
radioPlaylistId = youtubeArtist.radioEndpoint?.playlistId,
|
||||
timestamp = System.currentTimeMillis()
|
||||
).also(Database::upsert)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user