mirror of
https://gitlab.com/ultrasonic/ultrasonic.git
synced 2025-04-14 16:37:16 +03:00
Fix two exceptions
This commit is contained in:
parent
297f71a8c8
commit
c70fcd7447
@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" ?>
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues>
|
||||
<ID>TooManyFunctions:PlaybackService.kt$PlaybackService : MediaLibraryServiceKoinComponentCoroutineScope</ID>
|
||||
<ID>UnusedPrivateMember:UApp.kt$private fun VmPolicy.Builder.detectAllExceptSocket(): VmPolicy.Builder</ID>
|
||||
<ID>ImplicitDefaultLocale:EditServerFragment.kt$EditServerFragment.<no name provided>$String.format( "%s %s", resources.getString(R.string.settings_connection_failure), getErrorMessage(error) )</ID>
|
||||
<ID>ImplicitDefaultLocale:EditServerFragment.kt$EditServerFragment.<no name provided>$String.format( "%s %s", resources.getString(R.string.settings_connection_failure), getErrorMessage(error) )</ID>
|
||||
<ID>ImplicitDefaultLocale:FileLoggerTree.kt$FileLoggerTree$String.format("Failed to write log to %s", file)</ID>
|
||||
<ID>ImplicitDefaultLocale:FileLoggerTree.kt$FileLoggerTree$String.format("Log file rotated, logging into file %s", file?.name)</ID>
|
||||
<ID>ImplicitDefaultLocale:FileLoggerTree.kt$FileLoggerTree$String.format("Logging into file %s", file?.name)</ID>
|
||||
@ -13,7 +13,7 @@
|
||||
<ID>LongMethod:PlaylistsFragment.kt$PlaylistsFragment$override fun onContextItemSelected(menuItem: MenuItem): Boolean</ID>
|
||||
<ID>LongMethod:ShareHandler.kt$ShareHandler$private fun showDialog( fragment: Fragment, shareDetails: ShareDetails, swipe: SwipeRefreshLayout?, cancellationToken: CancellationToken, additionalId: String? )</ID>
|
||||
<ID>LongMethod:SharesFragment.kt$SharesFragment$override fun onContextItemSelected(menuItem: MenuItem): Boolean</ID>
|
||||
<ID>LongParameterList:ServerRowAdapter.kt$ServerRowAdapter$( private var context: Context, passedData: Array<ServerSetting>, private val model: ServerSettingsModel, private val activeServerProvider: ActiveServerProvider, private val manageMode: Boolean, private val serverDeletedCallback: ((Int) -> Unit), private val serverEditRequestedCallback: ((Int) -> Unit) )</ID>
|
||||
<ID>LongParameterList:ServerRowAdapter.kt$ServerRowAdapter$( private var context: Context, passedData: Array<ServerSetting>, private val model: ServerSettingsModel, private val activeServerProvider: ActiveServerProvider, private val manageMode: Boolean, private val serverDeletedCallback: ((Int) -> Unit), private val serverEditRequestedCallback: ((Int) -> Unit) )</ID>
|
||||
<ID>MagicNumber:ActiveServerProvider.kt$ActiveServerProvider$8192</ID>
|
||||
<ID>MagicNumber:JukeboxMediaPlayer.kt$JukeboxMediaPlayer$0.05f</ID>
|
||||
<ID>MagicNumber:JukeboxMediaPlayer.kt$JukeboxMediaPlayer$50</ID>
|
||||
@ -25,5 +25,5 @@
|
||||
<ID>TooManyFunctions:RESTMusicService.kt$RESTMusicService : MusicService</ID>
|
||||
<ID>UtilityClassWithPublicConstructor:FragmentTitle.kt$FragmentTitle</ID>
|
||||
</ManuallySuppressedIssues>
|
||||
<CurrentIssues></CurrentIssues>
|
||||
<CurrentIssues/>
|
||||
</SmellBaseline>
|
||||
|
@ -1,158 +0,0 @@
|
||||
/*
|
||||
This file is part of Subsonic.
|
||||
|
||||
Subsonic is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Subsonic is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.moire.ultrasonic.util;
|
||||
|
||||
import android.app.Activity;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import org.moire.ultrasonic.R;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public abstract class ModalBackgroundTask<T> extends BackgroundTask<T>
|
||||
{
|
||||
private final AlertDialog progressDialog;
|
||||
private Thread thread;
|
||||
private final boolean finishActivityOnCancel;
|
||||
private boolean cancelled;
|
||||
|
||||
public ModalBackgroundTask(Activity activity, boolean finishActivityOnCancel)
|
||||
{
|
||||
super(activity);
|
||||
this.finishActivityOnCancel = finishActivityOnCancel;
|
||||
progressDialog = createProgressDialog();
|
||||
}
|
||||
|
||||
public ModalBackgroundTask(Activity activity)
|
||||
{
|
||||
this(activity, true);
|
||||
}
|
||||
|
||||
private androidx.appcompat.app.AlertDialog createProgressDialog()
|
||||
{
|
||||
InfoDialog.Builder builder = new InfoDialog.Builder(getActivity().getApplicationContext());
|
||||
builder.setTitle(R.string.background_task_wait);
|
||||
builder.setMessage(R.string.background_task_loading);
|
||||
builder.setOnCancelListener(dialogInterface -> cancel());
|
||||
builder.setPositiveButton(R.string.common_cancel, (dialogInterface, i) -> cancel());
|
||||
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute()
|
||||
{
|
||||
cancelled = false;
|
||||
progressDialog.show();
|
||||
|
||||
thread = new Thread()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
final T result = doInBackground();
|
||||
if (cancelled)
|
||||
{
|
||||
progressDialog.dismiss();
|
||||
return;
|
||||
}
|
||||
|
||||
getHandler().post(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
progressDialog.dismiss();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
done(result);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
catch (final Throwable t)
|
||||
{
|
||||
if (cancelled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
getHandler().post(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
progressDialog.dismiss();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
error(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
thread.start();
|
||||
}
|
||||
|
||||
protected void cancel()
|
||||
{
|
||||
cancelled = true;
|
||||
if (thread != null)
|
||||
{
|
||||
thread.interrupt();
|
||||
}
|
||||
|
||||
if (finishActivityOnCancel)
|
||||
{
|
||||
getActivity().finish();
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isCancelled()
|
||||
{
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void error(Throwable error)
|
||||
{
|
||||
Timber.w(error);
|
||||
new ErrorDialog(getActivity(), getErrorMessage(error), getActivity(), finishActivityOnCancel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProgress(final String message)
|
||||
{
|
||||
getHandler().post(() -> progressDialog.setMessage(message));
|
||||
}
|
||||
}
|
@ -7,19 +7,24 @@
|
||||
|
||||
package org.moire.ultrasonic.subsonic
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import java.util.Collections
|
||||
import java.util.LinkedList
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
import org.moire.ultrasonic.service.MediaPlayerController
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
|
||||
import org.moire.ultrasonic.util.CommunicationError
|
||||
import org.moire.ultrasonic.util.EntryByDiscAndTrackComparator
|
||||
import org.moire.ultrasonic.util.ModalBackgroundTask
|
||||
import org.moire.ultrasonic.util.InfoDialog
|
||||
import org.moire.ultrasonic.util.Settings
|
||||
import org.moire.ultrasonic.util.Util
|
||||
|
||||
@ -30,7 +35,7 @@ import org.moire.ultrasonic.util.Util
|
||||
class DownloadHandler(
|
||||
val mediaPlayerController: MediaPlayerController,
|
||||
val networkAndStorageChecker: NetworkAndStorageChecker
|
||||
) {
|
||||
) : CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
||||
private val maxSongs = 500
|
||||
|
||||
fun download(
|
||||
@ -203,137 +208,176 @@ class DownloadHandler(
|
||||
unpin: Boolean,
|
||||
isArtist: Boolean
|
||||
) {
|
||||
val activity = fragment.activity as Activity
|
||||
val task = object : ModalBackgroundTask<List<Track>>(
|
||||
activity,
|
||||
false
|
||||
) {
|
||||
// Launch the Job
|
||||
val job = launch {
|
||||
val songs: MutableList<Track> =
|
||||
getTracksFromServer(isArtist, id, isDirectory, name, isShare)
|
||||
|
||||
@Throws(Throwable::class)
|
||||
override fun doInBackground(): List<Track> {
|
||||
val musicService = getMusicService()
|
||||
val songs: MutableList<Track> = LinkedList()
|
||||
val root: MusicDirectory
|
||||
if (!isOffline() && isArtist && Settings.shouldUseId3Tags) {
|
||||
getSongsForArtist(id, songs)
|
||||
} else {
|
||||
if (isDirectory) {
|
||||
root = if (!isOffline() && Settings.shouldUseId3Tags)
|
||||
musicService.getAlbumAsDir(id, name, false)
|
||||
else
|
||||
musicService.getMusicDirectory(id, name, false)
|
||||
} else if (isShare) {
|
||||
root = MusicDirectory()
|
||||
val shares = musicService.getShares(true)
|
||||
for (share in shares) {
|
||||
if (share.id == id) {
|
||||
for (entry in share.getEntries()) {
|
||||
root.add(entry)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
root = musicService.getPlaylist(id, name!!)
|
||||
}
|
||||
getSongsRecursively(root, songs)
|
||||
}
|
||||
return songs
|
||||
withContext(Dispatchers.Main) {
|
||||
addTracksToMediaController(
|
||||
songs,
|
||||
background,
|
||||
unpin,
|
||||
append,
|
||||
playNext,
|
||||
save,
|
||||
autoPlay,
|
||||
shuffle,
|
||||
fragment
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DestructuringDeclarationWithTooManyEntries")
|
||||
@Throws(Exception::class)
|
||||
private fun getSongsRecursively(
|
||||
parent: MusicDirectory,
|
||||
songs: MutableList<Track>
|
||||
) {
|
||||
if (songs.size > maxSongs) {
|
||||
return
|
||||
}
|
||||
for (song in parent.getTracks()) {
|
||||
if (!song.isVideo) {
|
||||
songs.add(song)
|
||||
}
|
||||
}
|
||||
val musicService = getMusicService()
|
||||
for ((id1, _, _, title) in parent.getAlbums()) {
|
||||
val root: MusicDirectory = if (
|
||||
!isOffline() &&
|
||||
Settings.shouldUseId3Tags
|
||||
) musicService.getAlbumAsDir(id1, title, false)
|
||||
else musicService.getMusicDirectory(id1, title, false)
|
||||
getSongsRecursively(root, songs)
|
||||
}
|
||||
// Create the dialog
|
||||
val builder = InfoDialog.Builder(fragment.requireContext())
|
||||
builder.setTitle(R.string.background_task_wait)
|
||||
builder.setMessage(R.string.background_task_loading)
|
||||
builder.setOnCancelListener { job.cancel() }
|
||||
builder.setPositiveButton(R.string.common_cancel) { _, i -> job.cancel() }
|
||||
val dialog = builder.create()
|
||||
dialog.show()
|
||||
|
||||
job.invokeOnCompletion {
|
||||
dialog.dismiss()
|
||||
if (it != null && it !is CancellationException) {
|
||||
Util.toast(
|
||||
fragment.requireContext(),
|
||||
CommunicationError.getErrorMessage(it, fragment.requireContext())
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
private fun getSongsForArtist(
|
||||
id: String,
|
||||
songs: MutableCollection<Track>
|
||||
) {
|
||||
if (songs.size > maxSongs) {
|
||||
return
|
||||
private fun addTracksToMediaController(
|
||||
songs: MutableList<Track>,
|
||||
background: Boolean,
|
||||
unpin: Boolean,
|
||||
append: Boolean,
|
||||
playNext: Boolean,
|
||||
save: Boolean,
|
||||
autoPlay: Boolean,
|
||||
shuffle: Boolean,
|
||||
fragment: Fragment
|
||||
) {
|
||||
if (songs.isEmpty()) return
|
||||
if (Settings.shouldSortByDisc) {
|
||||
Collections.sort(songs, EntryByDiscAndTrackComparator())
|
||||
}
|
||||
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
|
||||
if (!background) {
|
||||
if (unpin) {
|
||||
mediaPlayerController.unpin(songs)
|
||||
} else {
|
||||
val insertionMode = when {
|
||||
append -> MediaPlayerController.InsertionMode.APPEND
|
||||
playNext -> MediaPlayerController.InsertionMode.AFTER_CURRENT
|
||||
else -> MediaPlayerController.InsertionMode.CLEAR
|
||||
}
|
||||
val musicService = getMusicService()
|
||||
val artist = musicService.getAlbumsOfArtist(id, "", false)
|
||||
for ((id1) in artist) {
|
||||
val albumDirectory = musicService.getAlbumAsDir(
|
||||
id1,
|
||||
"",
|
||||
false
|
||||
mediaPlayerController.addToPlaylist(
|
||||
songs,
|
||||
save,
|
||||
autoPlay,
|
||||
shuffle,
|
||||
insertionMode
|
||||
)
|
||||
if (
|
||||
!append &&
|
||||
Settings.shouldTransitionOnPlayback
|
||||
) {
|
||||
fragment.findNavController().popBackStack(
|
||||
R.id.playerFragment,
|
||||
true
|
||||
)
|
||||
for (song in albumDirectory.getTracks()) {
|
||||
if (!song.isVideo) {
|
||||
songs.add(song)
|
||||
}
|
||||
}
|
||||
fragment.findNavController().navigate(R.id.playerFragment)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (unpin) {
|
||||
mediaPlayerController.unpin(songs)
|
||||
} else {
|
||||
mediaPlayerController.downloadBackground(songs, save)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Called when we have collected the tracks
|
||||
override fun done(songs: List<Track>) {
|
||||
if (Settings.shouldSortByDisc) {
|
||||
Collections.sort(songs, EntryByDiscAndTrackComparator())
|
||||
}
|
||||
if (songs.isNotEmpty()) {
|
||||
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
|
||||
if (!background) {
|
||||
if (unpin) {
|
||||
mediaPlayerController.unpin(songs)
|
||||
} else {
|
||||
val insertionMode = when {
|
||||
append -> MediaPlayerController.InsertionMode.APPEND
|
||||
playNext -> MediaPlayerController.InsertionMode.AFTER_CURRENT
|
||||
else -> MediaPlayerController.InsertionMode.CLEAR
|
||||
}
|
||||
mediaPlayerController.addToPlaylist(
|
||||
songs,
|
||||
save,
|
||||
autoPlay,
|
||||
shuffle,
|
||||
insertionMode
|
||||
)
|
||||
if (
|
||||
!append &&
|
||||
Settings.shouldTransitionOnPlayback
|
||||
) {
|
||||
fragment.findNavController().popBackStack(
|
||||
R.id.playerFragment,
|
||||
true
|
||||
)
|
||||
fragment.findNavController().navigate(R.id.playerFragment)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (unpin) {
|
||||
mediaPlayerController.unpin(songs)
|
||||
} else {
|
||||
mediaPlayerController.downloadBackground(songs, save)
|
||||
}
|
||||
}
|
||||
private fun getTracksFromServer(
|
||||
isArtist: Boolean,
|
||||
id: String,
|
||||
isDirectory: Boolean,
|
||||
name: String?,
|
||||
isShare: Boolean
|
||||
): MutableList<Track> {
|
||||
val musicService = getMusicService()
|
||||
val songs: MutableList<Track> = LinkedList()
|
||||
val root: MusicDirectory
|
||||
if (!isOffline() && isArtist && Settings.shouldUseId3Tags) {
|
||||
getSongsForArtist(id, songs)
|
||||
} else {
|
||||
if (isDirectory) {
|
||||
root = if (!isOffline() && Settings.shouldUseId3Tags)
|
||||
musicService.getAlbumAsDir(id, name, false)
|
||||
else
|
||||
musicService.getMusicDirectory(id, name, false)
|
||||
} else if (isShare) {
|
||||
root = MusicDirectory()
|
||||
val shares = musicService.getShares(true)
|
||||
// Filter the received shares by the given id, and get their entries
|
||||
val entries = shares.filter { it.id == id }.flatMap { it.getEntries() }
|
||||
root.addAll(entries)
|
||||
} else {
|
||||
root = musicService.getPlaylist(id, name!!)
|
||||
}
|
||||
getSongsRecursively(root, songs)
|
||||
}
|
||||
return songs
|
||||
}
|
||||
|
||||
@Suppress("DestructuringDeclarationWithTooManyEntries")
|
||||
@Throws(Exception::class)
|
||||
private fun getSongsRecursively(
|
||||
parent: MusicDirectory,
|
||||
songs: MutableList<Track>
|
||||
) {
|
||||
if (songs.size > maxSongs) {
|
||||
return
|
||||
}
|
||||
for (song in parent.getTracks()) {
|
||||
if (!song.isVideo) {
|
||||
songs.add(song)
|
||||
}
|
||||
}
|
||||
val musicService = getMusicService()
|
||||
for ((id1, _, _, title) in parent.getAlbums()) {
|
||||
val root: MusicDirectory = if (
|
||||
!isOffline() &&
|
||||
Settings.shouldUseId3Tags
|
||||
) musicService.getAlbumAsDir(id1, title, false)
|
||||
else musicService.getMusicDirectory(id1, title, false)
|
||||
getSongsRecursively(root, songs)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
private fun getSongsForArtist(
|
||||
id: String,
|
||||
songs: MutableCollection<Track>
|
||||
) {
|
||||
if (songs.size > maxSongs) {
|
||||
return
|
||||
}
|
||||
val musicService = getMusicService()
|
||||
val artist = musicService.getAlbumsOfArtist(id, "", false)
|
||||
for ((id1) in artist) {
|
||||
val albumDirectory = musicService.getAlbumAsDir(
|
||||
id1,
|
||||
"",
|
||||
false
|
||||
)
|
||||
for (song in albumDirectory.getTracks()) {
|
||||
if (!song.isVideo) {
|
||||
songs.add(song)
|
||||
}
|
||||
}
|
||||
}
|
||||
task.execute()
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,8 @@ import androidx.media3.common.MediaMetadata
|
||||
import androidx.media3.common.MediaMetadata.FOLDER_TYPE_NONE
|
||||
import androidx.media3.common.StarRating
|
||||
import java.text.DateFormat
|
||||
import java.text.ParseException
|
||||
import java.util.Date
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
import org.moire.ultrasonic.provider.AlbumArtContentProvider
|
||||
|
||||
@ -144,7 +146,7 @@ fun MediaItem.toTrack(): Track {
|
||||
|
||||
// No cache hit, generate it
|
||||
val created = mediaMetadata.extras?.getString("created")
|
||||
val createdDate = if (created != null) DateFormat.getDateInstance().parse(created) else null
|
||||
val createdDate = safeParseDate(created)
|
||||
|
||||
val track = Track(
|
||||
mediaId,
|
||||
@ -194,6 +196,14 @@ fun MediaItem.toTrack(): Track {
|
||||
return track
|
||||
}
|
||||
|
||||
private fun safeParseDate(created: String?): Date? {
|
||||
return if (created != null) try {
|
||||
DateFormat.getDateInstance().parse(created)
|
||||
} catch (_: ParseException) {
|
||||
null
|
||||
} else null
|
||||
}
|
||||
|
||||
fun MediaItem.setPin(pin: Boolean) {
|
||||
this.mediaMetadata.extras?.putBoolean("pin", pin)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user