Minor fixes

This commit is contained in:
tzugen 2021-12-18 14:33:04 +01:00
parent f4554ff29e
commit a0da791b28
No known key found for this signature in database
GPG Key ID: 61E9C34BC10EC930
10 changed files with 57 additions and 74 deletions

View File

@ -221,7 +221,7 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
progress.text = null progress.text = null
} }
DownloadStatus.FAILED, DownloadStatus.FAILED,
DownloadStatus.ABORTED -> { DownloadStatus.CANCELLED -> {
statusImage = imageHelper.errorImage statusImage = imageHelper.errorImage
progress.text = null progress.text = null
} }

View File

@ -167,6 +167,10 @@ class SettingsFragment :
update() update()
} }
/**
* This function will be called when we return from the file picker
* with a new custom cache location
*/
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
if ( if (
requestCode != SELECT_CACHE_ACTIVITY || requestCode != SELECT_CACHE_ACTIVITY ||

View File

@ -167,9 +167,9 @@ class DownloadFile(
fun delete() { fun delete() {
cancelDownload() cancelDownload()
FileUtil.delete(partialFile) Storage.delete(partialFile)
FileUtil.delete(completeFile) Storage.delete(completeFile)
FileUtil.delete(saveFile) Storage.delete(saveFile)
status.postValue(DownloadStatus.IDLE) status.postValue(DownloadStatus.IDLE)
@ -185,11 +185,11 @@ class DownloadFile(
fun cleanup(): Boolean { fun cleanup(): Boolean {
var ok = true var ok = true
if (Storage.isPathExists(completeFile) || Storage.isPathExists(saveFile)) { if (Storage.isPathExists(completeFile) || Storage.isPathExists(saveFile)) {
ok = FileUtil.delete(partialFile) ok = Storage.delete(partialFile)
} }
if (Storage.isPathExists(saveFile)) { if (Storage.isPathExists(saveFile)) {
ok = ok and FileUtil.delete(completeFile) ok = ok and Storage.delete(completeFile)
} }
return ok return ok
@ -204,14 +204,14 @@ class DownloadFile(
private fun doPendingRename() { private fun doPendingRename() {
try { try {
if (saveWhenDone) { if (saveWhenDone) {
FileUtil.renameFile(completeFile, saveFile) Storage.rename(completeFile, saveFile)
saveWhenDone = false saveWhenDone = false
} else if (completeWhenDone) { } else if (completeWhenDone) {
if (shouldSave) { if (shouldSave) {
FileUtil.renameFile(partialFile, saveFile) Storage.rename(partialFile, saveFile)
Util.scanMedia(saveFile) Util.scanMedia(saveFile)
} else { } else {
FileUtil.renameFile(partialFile, completeFile) Storage.rename(partialFile, completeFile)
} }
completeWhenDone = false completeWhenDone = false
} }
@ -245,7 +245,7 @@ class DownloadFile(
if (isPlaying) { if (isPlaying) {
saveWhenDone = true saveWhenDone = true
} else { } else {
FileUtil.renameFile(completeFile, saveFile) Storage.rename(completeFile, saveFile)
newStatus = DownloadStatus.PINNED newStatus = DownloadStatus.PINNED
} }
} else { } else {
@ -292,7 +292,7 @@ class DownloadFile(
outputStream.close() outputStream.close()
if (isCancelled) { if (isCancelled) {
status.postValue(DownloadStatus.ABORTED) status.postValue(DownloadStatus.CANCELLED)
throw Exception(String.format("Download of '%s' was cancelled", song)) throw Exception(String.format("Download of '%s' was cancelled", song))
} }
@ -307,18 +307,18 @@ class DownloadFile(
completeWhenDone = true completeWhenDone = true
} else { } else {
if (shouldSave) { if (shouldSave) {
FileUtil.renameFile(partialFile, saveFile) Storage.rename(partialFile, saveFile)
status.postValue(DownloadStatus.PINNED) status.postValue(DownloadStatus.PINNED)
Util.scanMedia(saveFile) Util.scanMedia(saveFile)
} else { } else {
FileUtil.renameFile(partialFile, completeFile) Storage.rename(partialFile, completeFile)
status.postValue(DownloadStatus.DONE) status.postValue(DownloadStatus.DONE)
} }
} }
} catch (all: Exception) { } catch (all: Exception) {
outputStream.safeClose() outputStream.safeClose()
FileUtil.delete(completeFile) Storage.delete(completeFile)
FileUtil.delete(saveFile) Storage.delete(saveFile)
if (!isCancelled) { if (!isCancelled) {
isFailed = true isFailed = true
if (retryCount > 1) { if (retryCount > 1) {
@ -412,5 +412,5 @@ class DownloadFile(
} }
enum class DownloadStatus { enum class DownloadStatus {
IDLE, DOWNLOADING, RETRYING, FAILED, ABORTED, DONE, PINNED, UNKNOWN IDLE, DOWNLOADING, RETRYING, FAILED, CANCELLED, DONE, PINNED, UNKNOWN
} }

View File

@ -356,7 +356,7 @@ class LocalMediaPlayer : KoinComponent {
setAudioAttributes(mediaPlayer) setAudioAttributes(mediaPlayer)
var dataSource: String? = null var streamUrl: String? = null
if (partial) { if (partial) {
if (proxy == null) { if (proxy == null) {
proxy = StreamProxy(object : Supplier<DownloadFile?>() { proxy = StreamProxy(object : Supplier<DownloadFile?>() {
@ -366,11 +366,11 @@ class LocalMediaPlayer : KoinComponent {
}) })
proxy!!.start() proxy!!.start()
} }
dataSource = String.format( streamUrl = String.format(
Locale.getDefault(), "http://127.0.0.1:%d/%s", Locale.getDefault(), "http://127.0.0.1:%d/%s",
proxy!!.port, URLEncoder.encode(file!!.path, Constants.UTF_8) proxy!!.port, URLEncoder.encode(file!!.path, Constants.UTF_8)
) )
Timber.i("Data Source: %s", dataSource) Timber.i("Data Source: %s", streamUrl)
} else if (proxy != null) { } else if (proxy != null) {
proxy?.stop() proxy?.stop()
proxy = null proxy = null
@ -378,12 +378,12 @@ class LocalMediaPlayer : KoinComponent {
Timber.i("Preparing media player") Timber.i("Preparing media player")
if (dataSource != null) { if (streamUrl != null) {
Timber.v("LocalMediaPlayer doPlay dataSource: %s", dataSource) Timber.v("LocalMediaPlayer doPlay dataSource: %s", streamUrl)
mediaPlayer.setDataSource(dataSource) mediaPlayer.setDataSource(streamUrl)
} else { } else {
Timber.v("LocalMediaPlayer doPlay Path: %s", file!!.path) Timber.v("LocalMediaPlayer doPlay Path: %s", file!!.path)
val descriptor = file!!.getDocumentFileDescriptor("r")!! val descriptor = file.getDocumentFileDescriptor("r")!!
mediaPlayer.setDataSource(descriptor.fileDescriptor) mediaPlayer.setDataSource(descriptor.fileDescriptor)
descriptor.close() descriptor.close()
} }
@ -468,7 +468,7 @@ class LocalMediaPlayer : KoinComponent {
} }
Timber.v("LocalMediaPlayer setupNext Path: %s", file!!.path) Timber.v("LocalMediaPlayer setupNext Path: %s", file!!.path)
val descriptor = file!!.getDocumentFileDescriptor("r")!! val descriptor = file.getDocumentFileDescriptor("r")!!
nextMediaPlayer!!.setDataSource(descriptor.fileDescriptor) nextMediaPlayer!!.setDataSource(descriptor.fileDescriptor)
descriptor.close() descriptor.close()

View File

@ -11,13 +11,13 @@ import org.koin.java.KoinJavaComponent.inject
import org.moire.ultrasonic.data.ActiveServerProvider import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.domain.Playlist import org.moire.ultrasonic.domain.Playlist
import org.moire.ultrasonic.service.Downloader import org.moire.ultrasonic.service.Downloader
import org.moire.ultrasonic.util.FileUtil.delete
import org.moire.ultrasonic.util.FileUtil.getAlbumArtFile import org.moire.ultrasonic.util.FileUtil.getAlbumArtFile
import org.moire.ultrasonic.util.FileUtil.getPlaylistDirectory import org.moire.ultrasonic.util.FileUtil.getPlaylistDirectory
import org.moire.ultrasonic.util.FileUtil.getPlaylistFile import org.moire.ultrasonic.util.FileUtil.getPlaylistFile
import org.moire.ultrasonic.util.FileUtil.listFiles import org.moire.ultrasonic.util.FileUtil.listFiles
import org.moire.ultrasonic.util.FileUtil.musicDirectory import org.moire.ultrasonic.util.FileUtil.musicDirectory
import org.moire.ultrasonic.util.Settings.cacheSizeMB import org.moire.ultrasonic.util.Settings.cacheSizeMB
import org.moire.ultrasonic.util.Storage.delete
import org.moire.ultrasonic.util.Util.formatBytes import org.moire.ultrasonic.util.Util.formatBytes
import timber.log.Timber import timber.log.Timber
@ -144,7 +144,7 @@ class CacheCleaner : CoroutineScope by CoroutineScope(Dispatchers.IO) {
// No songs left in the folder // No songs left in the folder
if (children.size == 1 && children[0].path == getAlbumArtFile(dir.path)) { if (children.size == 1 && children[0].path == getAlbumArtFile(dir.path)) {
// Delete Artwork files // Delete Artwork files
delete(getAlbumArtFile(dir.path)) delete(children[0].path)
children = dir.listFiles() children = dir.listFiles()
} }
@ -166,17 +166,14 @@ class CacheCleaner : CoroutineScope by CoroutineScope(Dispatchers.IO) {
} }
// Ensure that file system is not more than 95% full. // Ensure that file system is not more than 95% full.
val bytesUsedFs: Long
val minFsAvailability: Long
val bytesTotalFs: Long
val bytesAvailableFs: Long
val descriptor = files[0].getDocumentFileDescriptor("r")!! val descriptor = files[0].getDocumentFileDescriptor("r")!!
val stat = Os.fstatvfs(descriptor.fileDescriptor) val stat = Os.fstatvfs(descriptor.fileDescriptor)
bytesTotalFs = stat.f_blocks * stat.f_bsize
bytesAvailableFs = stat.f_bfree * stat.f_bsize val bytesTotalFs: Long = stat.f_blocks * stat.f_bsize
bytesUsedFs = bytesTotalFs - bytesAvailableFs val bytesAvailableFs: Long = stat.f_bfree * stat.f_bsize
minFsAvailability = bytesTotalFs - MIN_FREE_SPACE val bytesUsedFs: Long = bytesTotalFs - bytesAvailableFs
val minFsAvailability: Long = bytesTotalFs - MIN_FREE_SPACE
descriptor.close() descriptor.close()
val bytesToDeleteCacheLimit = (bytesUsedBySubsonic - cacheSizeBytes).coerceAtLeast(0L) val bytesToDeleteCacheLimit = (bytesUsedBySubsonic - cacheSizeBytes).coerceAtLeast(0L)

View File

@ -30,6 +30,10 @@ import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.util.Util.safeClose import org.moire.ultrasonic.util.Util.safeClose
import timber.log.Timber import timber.log.Timber
/**
* Provides Ultrasonic specific functions for managing the library files.
* Base storage functions like rename, create, delete should be handled in Storage.kt
*/
@Suppress("TooManyFunctions") @Suppress("TooManyFunctions")
object FileUtil { object FileUtil {
@ -89,6 +93,10 @@ object FileUtil {
return playlistDir return playlistDir
} }
/**
* Get the directory where we store local copies of the playlists.
* It is always inside Ultrasonic base directory.
*/
@JvmStatic @JvmStatic
fun getPlaylistDirectory(server: String? = null): File { fun getPlaylistDirectory(server: String? = null): File {
val playlistDir: File = if (server != null) { val playlistDir: File = if (server != null) {
@ -186,7 +194,7 @@ object FileUtil {
return albumArtDir return albumArtDir
} }
fun getAlbumDirectory(entry: MusicDirectory.Child): String { private fun getAlbumDirectory(entry: MusicDirectory.Child): String {
val dir: String val dir: String
if (!TextUtils.isEmpty(entry.path) && getParentPath(entry.path!!) != null) { if (!TextUtils.isEmpty(entry.path) && getParentPath(entry.path!!) != null) {
val f = fileSystemSafeDir(entry.path) val f = fileSystemSafeDir(entry.path)
@ -318,10 +326,6 @@ object FileUtil {
@JvmStatic @JvmStatic
fun listFiles(dir: AbstractFile): SortedSet<AbstractFile> { fun listFiles(dir: AbstractFile): SortedSet<AbstractFile> {
val files = dir.listFiles() val files = dir.listFiles()
if (files == null) {
Timber.w("Failed to list children for %s", dir.path)
return TreeSet()
}
return TreeSet(files.asList()) return TreeSet(files.asList())
} }
@ -486,35 +490,4 @@ object FileUtil {
fw.safeClose() fw.safeClose()
} }
} }
@JvmStatic
@Throws(IOException::class)
fun renameFile(from: String, to: String) {
Storage.rename(from, to)
}
@JvmStatic
fun delete(file: File?): Boolean {
if (file != null && file.exists()) {
if (!file.delete()) {
Timber.w("Failed to delete file %s", file)
return false
}
Timber.i("Deleted file %s", file)
}
return true
}
@JvmStatic
fun delete(file: String?): Boolean {
if (file != null) {
val storageFile = Storage.getFromPath(file)
if (storageFile != null && !storageFile.delete()) {
Timber.w("Failed to delete file %s", file)
return false
}
Timber.i("Deleted file %s", file)
}
return true
}
} }

View File

@ -60,6 +60,15 @@ object Storage {
mediaRoot.value.rename(pathFrom, pathTo) mediaRoot.value.rename(pathFrom, pathTo)
} }
fun delete(path: String): Boolean {
val storageFile = getFromPath(path)
if (storageFile != null && !storageFile.delete()) {
Timber.w("Failed to delete file %s", path)
return false
}
return true
}
private fun getRoot(): AbstractFile? { private fun getRoot(): AbstractFile? {
return if (Settings.cacheLocation.isUri()) { return if (Settings.cacheLocation.isUri()) {
val documentFile = DocumentFile.fromTreeUri( val documentFile = DocumentFile.fromTreeUri(

View File

@ -16,7 +16,7 @@
a:layout_width="wrap_content" a:layout_width="wrap_content"
a:layout_height="wrap_content" a:layout_height="wrap_content"
a:layout_gravity="center_vertical" a:layout_gravity="center_vertical"
a:contentDescription="@null" a:importantForAccessibility="no"
a:src="?attr/select_folder" /> a:src="?attr/select_folder" />
<LinearLayout <LinearLayout

View File

@ -190,7 +190,7 @@
<string name="settings.directory_cache_time_60">1 ora</string> <string name="settings.directory_cache_time_60">1 ora</string>
<string name="settings.disc_sort">Ordina Canzoni secondo Disco</string> <string name="settings.disc_sort">Ordina Canzoni secondo Disco</string>
<string name="settings.disc_sort_summary">Ordina lista canzoni secondo il numero disco e traccia</string> <string name="settings.disc_sort_summary">Ordina lista canzoni secondo il numero disco e traccia</string>
<string name="settings.display_bitrate">Visualizza Bitrate Ed Estensione FileAdapter</string> <string name="settings.display_bitrate">Visualizza Bitrate Ed Estensione File</string>
<string name="settings.display_bitrate_summary">Aggiungi nome artista con bitrare ed estensione file</string> <string name="settings.display_bitrate_summary">Aggiungi nome artista con bitrare ed estensione file</string>
<string name="settings.download_transition">Visualizza Download Durante Riproduzione</string> <string name="settings.download_transition">Visualizza Download Durante Riproduzione</string>
<string name="settings.download_transition_summary">Passa al download quando inizia riproduzione</string> <string name="settings.download_transition_summary">Passa al download quando inizia riproduzione</string>

View File

@ -216,7 +216,7 @@
<string name="settings.directory_cache_time_60">1 hour</string> <string name="settings.directory_cache_time_60">1 hour</string>
<string name="settings.disc_sort">Sort Songs By Disc</string> <string name="settings.disc_sort">Sort Songs By Disc</string>
<string name="settings.disc_sort_summary">Sort song list by disc number and track number</string> <string name="settings.disc_sort_summary">Sort song list by disc number and track number</string>
<string name="settings.display_bitrate">Display Bitrate and FileAdapter Suffix</string> <string name="settings.display_bitrate">Display Bitrate and File Suffix</string>
<string name="settings.display_bitrate_summary">Append artist name with bitrate and file suffix</string> <string name="settings.display_bitrate_summary">Append artist name with bitrate and file suffix</string>
<string name="settings.download_transition">Show Downloads on Play</string> <string name="settings.download_transition">Show Downloads on Play</string>
<string name="settings.download_transition_summary">Transition to download activity when starting playback</string> <string name="settings.download_transition_summary">Transition to download activity when starting playback</string>