Skip to content

Commit

Permalink
Implement download menu for remaining buttons, fix startup crash
Browse files Browse the repository at this point in the history
Use download selection menu for all appropriate buttons
Fix startup crash when partial downloads are present
  • Loading branch information
toasterofbread committed Nov 25, 2023
1 parent 5edf002 commit 15a59e5
Show file tree
Hide file tree
Showing 12 changed files with 85 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,11 @@ class PlayerDownloadService: PlatformServiceImpl() {
}
}

fun isFileDownloadInProgress(file: PlatformFile): Boolean =
file.name.endsWith(FILE_DOWNLOADING_SUFFIX)

fun isFileDownloadInProgressForSong(file: PlatformFile, song: Song): Boolean =
file.name.endsWith(FILE_DOWNLOADING_SUFFIX) && file.name.startsWith("${song.id}.")
isFileDownloadInProgress(file) && file.name.startsWith("${song.id}.")

fun getSongIdOfInProgressDownload(file: PlatformFile): String? =
if (file.name.endsWith(FILE_DOWNLOADING_SUFFIX)) file.name.split('.', limit = 2).first() else null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,13 @@ object LocalSongMetadataProcessor {
}

suspend fun readLocalSongMetadata(file: PlatformFile, match_id: String? = null, load_data: Boolean = true): SongData? = withContext(Dispatchers.IO) {
val tag: Tag = AudioFileIO.read(File(file.absolute_path)).tag
val tag: Tag
try {
tag = AudioFileIO.read(File(file.absolute_path)).tag
}
catch (e: Throwable) {
return@withContext null
}

val custom_metadata: CustomMetadata = Json.decodeFromString(tag.getFirst(CUSTOM_METADATA_KEY))
if (custom_metadata.song_id == null || (match_id != null && custom_metadata.song_id != match_id)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,11 @@ actual class PlayerDownloadManager actual constructor(val context: AppContext) {

for (file in getSongDownloadDir(context).listFiles() ?: emptyList()) {
val in_progress: Boolean
if (PlayerDownloadService.isFileDownloadInProgressForSong(file, song)) {
if (PlayerDownloadService.isFileDownloadInProgress(file)) {
if (!PlayerDownloadService.isFileDownloadInProgressForSong(file, song)) {
continue
}

in_progress = true
}
else if (LocalSongMetadataProcessor.readLocalSongMetadata(file, match_id = song.id, load_data = false) != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import com.toasterofbread.spmp.resources.getString
import com.toasterofbread.spmp.ui.component.mediaitempreview.MediaItemPreviewLong

@Composable
fun DownloadMethodSelectionDialog(onCancelled: () -> Unit, onSelected: (DownloadMethod) -> Unit, modifier: Modifier = Modifier, song: Song? = null) {
fun DownloadMethodSelectionDialog(onCancelled: () -> Unit, onSelected: (DownloadMethod) -> Unit, modifier: Modifier = Modifier, songs: List<Song>? = null) {
var download_method: DownloadMethod by StreamingSettings.Key.DOWNLOAD_METHOD.rememberMutableEnumState()
var skip_confirmation: Boolean by StreamingSettings.Key.SKIP_DOWNLOAD_METHOD_CONFIRMATION.rememberMutableState()
var show: Boolean by remember { mutableStateOf(false) }
Expand Down Expand Up @@ -86,8 +86,9 @@ fun DownloadMethodSelectionDialog(onCancelled: () -> Unit, onSelected: (Download
},
text = {
Column(verticalArrangement = Arrangement.spacedBy(20.dp)) {
if (song != null) {
MediaItemPreviewLong(song, Modifier.fillMaxWidth())
val first_song: Song? = songs?.firstOrNull()
if (first_song != null) {
MediaItemPreviewLong(first_song, Modifier.fillMaxWidth())
}

Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import com.toasterofbread.composekit.platform.PlatformFile
import com.toasterofbread.spmp.model.mediaitem.enums.MediaItemType
import com.toasterofbread.spmp.model.mediaitem.song.Song
import com.toasterofbread.spmp.model.mediaitem.song.SongAudioQuality
import com.toasterofbread.spmp.platform.AppContext
Expand All @@ -31,22 +32,58 @@ enum class DownloadMethod {
CUSTOM -> getString("download_method_desc_custom")
}

fun execute(context: AppContext, song: Song, callback: DownloadRequestCallback?) =
fun execute(context: AppContext, songs: List<Song>, callback: DownloadRequestCallback?) =
when (this) {
LIBRARY -> context.download_manager.startDownload(song, callback = callback)
LIBRARY -> {
for (song in songs) {
context.download_manager.startDownload(song, callback = callback)
}
}
CUSTOM -> {
context.promptUserForFileCreation(
// TODO | Remove hardcoded MIME type
"audio/mp4",
song.getActiveTitle(context.database),
false
) { uri ->
if (uri == null) {
callback?.invoke(null)
return@promptUserForFileCreation
if (songs.size == 1) {
context.promptUserForFileCreation(
// TODO | Remove hardcoded MIME type
"audio/mp4",
songs.single().getActiveTitle(context.database),
false
) { uri ->
if (uri == null) {
callback?.invoke(null)
return@promptUserForFileCreation
}

context.download_manager.startDownload(songs.single(), file_uri = uri, callback = callback)
}
}
else {
context.promptUserForDirectory(persist = true) { uri ->
if (uri == null) {
callback?.invoke(null)
return@promptUserForDirectory
}

context.download_manager.startDownload(song, file_uri = uri, callback = callback)
val directory: PlatformFile = context.getUserDirectoryFile(uri)

for (song in songs) {
var file: PlatformFile
val name: String = song.getActiveTitle(context.database) ?: MediaItemType.SONG.getReadable(false)

var i: Int = 0
do {
// TODO | Remove hardcoded file type
var file_name = name + ".m4a"
if (i++ >= 1) {
file_name += " (${i + 1})"
}
file = directory.resolve(file_name)
}
while (file.exists)

file.createFile()

context.download_manager.startDownload(song, file_uri = file.uri, callback = callback)
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -405,11 +405,8 @@ class MediaItemMultiSelectContext(
// Download
AnimatedVisibility(any_are_downloadable) {
IconButton({
for (item in getUniqueSelectedItems()) {
if (item is Song) {
player.context.download_manager.startDownload(item)
}
}
val songs: List<Song> = getUniqueSelectedItems().filterIsInstance<Song>()
player.onSongDownloadRequested(songs)
onActionPerformed()
}) {
Icon(Icons.Default.Download, null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,5 +307,6 @@ open class PlayerState protected constructor(

open fun hideLongPressMenu() { upstream!!.hideLongPressMenu() }

open fun onSongDownloadRequested(song: Song, onCompleted: DownloadRequestCallback? = null) { upstream!!.onSongDownloadRequested(song, onCompleted) }
fun onSongDownloadRequested(song: Song, onCompleted: DownloadRequestCallback? = null) { onSongDownloadRequested(listOf(song), onCompleted) }
open fun onSongDownloadRequested(songs: List<Song>, onCompleted: DownloadRequestCallback? = null) { upstream!!.onSongDownloadRequested(songs, onCompleted) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ class PlayerStateImpl(override val context: AppContext, private val coroutine_sc
)
private var np_swipe_anchors: Map<Float, Int>? by mutableStateOf(null)

private var download_request_song: Song? by mutableStateOf(null)
private var download_request_songs: List<Song>? by mutableStateOf(null)
private var download_request_callback: DownloadRequestCallback? by mutableStateOf(null)

override val expansion = NowPlayingExpansionState(this, np_swipe_state, coroutine_scope)
Expand Down Expand Up @@ -384,17 +384,17 @@ class PlayerStateImpl(override val context: AppContext, private val coroutine_sc
)
}

download_request_song?.also { song ->
download_request_songs?.also { songs ->
DownloadMethodSelectionDialog(
onCancelled = {
download_request_song = null
download_request_songs = null
download_request_callback?.invoke(null)
},
onSelected = { method ->
method.execute(context, song, download_request_callback)
download_request_song = null
method.execute(context, songs, download_request_callback)
download_request_songs = null
},
song = song
songs = songs
)
}
}
Expand Down Expand Up @@ -483,8 +483,8 @@ class PlayerStateImpl(override val context: AppContext, private val coroutine_sc
return controller?.has_focus == true
}

override fun onSongDownloadRequested(song: Song, callback: DownloadRequestCallback?) {
download_request_song = song
override fun onSongDownloadRequested(songs: List<Song>, callback: DownloadRequestCallback?) {
download_request_songs = songs
download_request_callback = callback
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ fun LargeThumbnailRow(
}
PlayerOverlayMenuAction.DOWNLOAD -> {
current_song?.also { song ->
player.context.download_manager.startDownload(song)
player.onSongDownloadRequested(song)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ fun SmallThumbnailRow(
}
PlayerOverlayMenuAction.DOWNLOAD -> {
current_song?.also { song ->
player.context.download_manager.startDownload(song)
player.onSongDownloadRequested(song)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ class MainPlayerOverlayMenu(
.fillMaxSize()
.clickable {
if (download_status?.status != DownloadStatus.Status.FINISHED && download_status?.status != DownloadStatus.Status.ALREADY_FINISHED) {
download_manager.startDownload(getSong())
player.onSongDownloadRequested(getSong())
}
},
contentAlignment = Alignment.Center
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
<string name="playlist_select_image">プレイリスト画像の選択</string>
<string name="playlist_info_local">ローカルプレイリスト</string>
<string name="playlist_info_account">アカウントプレイリスト</string>

<string name="feed_row_artists">おすすめのアーティスト</string>
<string name="feed_row_playlists">プレイリスト</string>

Expand Down

0 comments on commit 15a59e5

Please sign in to comment.