From e0df24182ea3955952c5aa2243c9d2622a243b8c Mon Sep 17 00:00:00 2001 From: tzugen Date: Thu, 26 Aug 2021 19:47:32 +0200 Subject: [PATCH 1/5] Add nullability annotation --- .../main/java/org/moire/ultrasonic/service/Downloader.java | 4 ++++ .../org/moire/ultrasonic/service/MediaPlayerController.kt | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.java index f768de46..dd4b0d79 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.java @@ -1,5 +1,7 @@ package org.moire.ultrasonic.service; +import androidx.annotation.Nullable; + import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.util.LRUCache; import org.moire.ultrasonic.util.ShufflePlayBuffer; @@ -28,6 +30,8 @@ public class Downloader { public final List downloadList = new ArrayList<>(); public final List backgroundDownloadList = new ArrayList<>(); + + @Nullable public DownloadFile currentDownloading; private final ShufflePlayBuffer shufflePlayBuffer; diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt index 994f06e8..beb71844 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt @@ -409,9 +409,7 @@ class MediaPlayerController( reset() // Cancel current download, if necessary. - if (downloader.currentDownloading != null) { - downloader.currentDownloading.cancelDownload() - } + downloader.currentDownloading?.cancelDownload() } else { jukeboxMediaPlayer.stopJukeboxService() } From b8eddb2d249671b39d9a697103e16135fc28bd5b Mon Sep 17 00:00:00 2001 From: tzugen Date: Fri, 27 Aug 2021 23:53:30 +0200 Subject: [PATCH 2/5] Rename .java to .kt --- .../moire/ultrasonic/service/{Downloader.java => Downloader.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ultrasonic/src/main/java/org/moire/ultrasonic/service/{Downloader.java => Downloader.kt} (100%) diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.kt similarity index 100% rename from ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.java rename to ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.kt From f9aac1ca431fe9b8e614ee22fc90980a7d32aa06 Mon Sep 17 00:00:00 2001 From: tzugen Date: Fri, 27 Aug 2021 23:53:31 +0200 Subject: [PATCH 3/5] Add parallel downloading, better priority handling --- .../fragment/BookmarksFragment.java | 132 +--- .../ultrasonic/fragment/SearchFragment.java | 2 +- .../moire/ultrasonic/service/Downloader.kt | 741 +++++++++--------- .../ultrasonic/fragment/PlayerFragment.kt | 11 +- .../fragment/TrackCollectionFragment.kt | 1 + .../service/AutoMediaBrowserService.kt | 4 +- .../moire/ultrasonic/service/DownloadFile.kt | 8 +- .../service/MediaPlayerController.kt | 61 +- .../service/MediaPlayerLifecycleSupport.kt | 4 +- .../ultrasonic/service/MediaPlayerService.kt | 30 +- .../ultrasonic/subsonic/DownloadHandler.kt | 4 +- .../org/moire/ultrasonic/view/SongView.kt | 5 +- 12 files changed, 470 insertions(+), 533 deletions(-) diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/BookmarksFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/BookmarksFragment.java index 41a4c814..0b350604 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/BookmarksFragment.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/BookmarksFragment.java @@ -4,7 +4,6 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.AdapterView; import android.widget.ImageView; import android.widget.ListView; @@ -25,8 +24,8 @@ import org.moire.ultrasonic.subsonic.NetworkAndStorageChecker; import org.moire.ultrasonic.subsonic.VideoPlayer; import org.moire.ultrasonic.util.CancellationToken; import org.moire.ultrasonic.util.Constants; -import org.moire.ultrasonic.util.Pair; import org.moire.ultrasonic.util.FragmentBackgroundTask; +import org.moire.ultrasonic.util.Pair; import org.moire.ultrasonic.util.Util; import org.moire.ultrasonic.view.EntryAdapter; @@ -78,37 +77,27 @@ public class BookmarksFragment extends Fragment { refreshAlbumListView = view.findViewById(R.id.select_album_entries_refresh); albumListView = view.findViewById(R.id.select_album_entries_list); - refreshAlbumListView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() - { - @Override - public void onRefresh() - { - enableButtons(); - getBookmarks(); - } + refreshAlbumListView.setOnRefreshListener(() -> { + enableButtons(); + getBookmarks(); }); albumListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); - albumListView.setOnItemClickListener(new AdapterView.OnItemClickListener() - { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) + albumListView.setOnItemClickListener((parent, view17, position, id) -> { + if (position >= 0) { - if (position >= 0) - { - MusicDirectory.Entry entry = (MusicDirectory.Entry) parent.getItemAtPosition(position); + MusicDirectory.Entry entry = (MusicDirectory.Entry) parent.getItemAtPosition(position); - if (entry != null) + if (entry != null) + { + if (entry.isVideo()) { - if (entry.isVideo()) - { - VideoPlayer.Companion.playVideo(getContext(), entry); - } - else - { - enableButtons(); - } + VideoPlayer.Companion.playVideo(getContext(), entry); + } + else + { + enableButtons(); } } } @@ -130,58 +119,24 @@ public class BookmarksFragment extends Fragment { playLastButton.setVisibility(View.GONE); oreButton.setVisibility(View.GONE); - playNowButton.setOnClickListener(new View.OnClickListener() - { - @Override - public void onClick(View view) - { - playNow(getSelectedSongs(albumListView)); - } - }); + playNowButton.setOnClickListener(view16 -> playNow(getSelectedSongs(albumListView))); - selectButton.setOnClickListener(new View.OnClickListener() - { - @Override - public void onClick(View view) - { - selectAllOrNone(); - } + selectButton.setOnClickListener(view15 -> selectAllOrNone()); + pinButton.setOnClickListener(view14 -> { + downloadBackground(true); + selectAll(false, false); }); - pinButton.setOnClickListener(new View.OnClickListener() - { - @Override - public void onClick(View view) - { - downloadBackground(true); - selectAll(false, false); - } + unpinButton.setOnClickListener(view13 -> { + unpin(); + selectAll(false, false); }); - unpinButton.setOnClickListener(new View.OnClickListener() - { - @Override - public void onClick(View view) - { - unpin(); - selectAll(false, false); - } + downloadButton.setOnClickListener(view12 -> { + downloadBackground(false); + selectAll(false, false); }); - downloadButton.setOnClickListener(new View.OnClickListener() - { - @Override - public void onClick(View view) - { - downloadBackground(false); - selectAll(false, false); - } - }); - deleteButton.setOnClickListener(new View.OnClickListener() - { - @Override - public void onClick(View view) - { - delete(); - selectAll(false, false); - } + deleteButton.setOnClickListener(view1 -> { + delete(); + selectAll(false, false); }); registerForContextMenu(albumListView); @@ -230,7 +185,8 @@ public class BookmarksFragment extends Fragment { { if (albumListView.isItemChecked(i)) { - songs.add((MusicDirectory.Entry) albumListView.getItemAtPosition(i)); + MusicDirectory.Entry song = (MusicDirectory.Entry) albumListView.getItemAtPosition(i); + if (song != null) songs.add(song); } } } @@ -291,6 +247,7 @@ public class BookmarksFragment extends Fragment { for (MusicDirectory.Entry song : selection) { + if (song == null) continue; DownloadFile downloadFile = mediaPlayerController.getValue().getDownloadFileForSong(song); if (downloadFile.isWorkDone()) { @@ -326,22 +283,17 @@ public class BookmarksFragment extends Fragment { private void downloadBackground(final boolean save, final List songs) { - Runnable onValid = new Runnable() - { - @Override - public void run() - { - networkAndStorageChecker.getValue().warnIfNetworkOrStorageUnavailable(); - mediaPlayerController.getValue().downloadBackground(songs, save); + Runnable onValid = () -> { + networkAndStorageChecker.getValue().warnIfNetworkOrStorageUnavailable(); + mediaPlayerController.getValue().downloadBackground(songs, save); - if (save) - { - Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_pinned, songs.size(), songs.size())); - } - else - { - Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_downloaded, songs.size(), songs.size())); - } + if (save) + { + Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_pinned, songs.size(), songs.size())); + } + else + { + Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_downloaded, songs.size(), songs.size())); } }; diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SearchFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SearchFragment.java index 76761f5b..d99ab93c 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SearchFragment.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SearchFragment.java @@ -562,7 +562,7 @@ public class SearchFragment extends Fragment { mediaPlayerController.clear(); } - mediaPlayerController.download(Collections.singletonList(song), false, false, false, false, false); + mediaPlayerController.addToPlaylist(Collections.singletonList(song), false, false, false, false, false); if (true) { diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.kt b/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.kt index dd4b0d79..ce4fa733 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.kt +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.kt @@ -1,449 +1,428 @@ -package org.moire.ultrasonic.service; +package org.moire.ultrasonic.service -import androidx.annotation.Nullable; - -import org.moire.ultrasonic.domain.MusicDirectory; -import org.moire.ultrasonic.util.LRUCache; -import org.moire.ultrasonic.util.ShufflePlayBuffer; -import org.moire.ultrasonic.util.Util; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -import kotlin.Lazy; -import timber.log.Timber; - -import static org.koin.java.KoinJavaComponent.inject; -import static org.moire.ultrasonic.domain.PlayerState.DOWNLOADING; -import static org.moire.ultrasonic.domain.PlayerState.STARTED; +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.moire.ultrasonic.domain.MusicDirectory +import org.moire.ultrasonic.util.Util.isExternalStoragePresent +import org.moire.ultrasonic.util.Util.isNetworkConnected +import org.moire.ultrasonic.util.Util.getPreloadCount +import org.moire.ultrasonic.util.Util.getMaxSongs +import org.moire.ultrasonic.util.ShufflePlayBuffer +import timber.log.Timber +import org.moire.ultrasonic.domain.PlayerState +import org.moire.ultrasonic.util.LRUCache +import java.util.ArrayList +import java.util.PriorityQueue +import java.util.concurrent.Executors +import java.util.concurrent.ScheduledExecutorService +import java.util.concurrent.TimeUnit /** * This class is responsible for maintaining the playlist and downloading * its items from the network to the filesystem. */ -public class Downloader -{ - public final List downloadList = new ArrayList<>(); - public final List backgroundDownloadList = new ArrayList<>(); +class Downloader( + private val shufflePlayBuffer: ShufflePlayBuffer, + private val externalStorageMonitor: ExternalStorageMonitor, + private val localMediaPlayer: LocalMediaPlayer +): KoinComponent { + val playList: MutableList = ArrayList() + private val downloadQueue: PriorityQueue = PriorityQueue() + private val activelyDownloading: MutableList = ArrayList() - @Nullable - public DownloadFile currentDownloading; + private val jukeboxMediaPlayer: JukeboxMediaPlayer by inject() + + private val downloadFileCache = LRUCache(100) - private final ShufflePlayBuffer shufflePlayBuffer; - private final ExternalStorageMonitor externalStorageMonitor; - private final LocalMediaPlayer localMediaPlayer; + private var executorService: ScheduledExecutorService? = null + var downloadListUpdateRevision: Long = 0 + private set - // TODO: This is a circular reference, try to remove - private final Lazy jukeboxMediaPlayer = inject(JukeboxMediaPlayer.class); - - private final List cleanupCandidates = new ArrayList<>(); - private final LRUCache downloadFileCache = new LRUCache<>(100); - private ScheduledExecutorService executorService; - private long revision; - - public Downloader(ShufflePlayBuffer shufflePlayBuffer, ExternalStorageMonitor externalStorageMonitor, - LocalMediaPlayer localMediaPlayer) - { - this.shufflePlayBuffer = shufflePlayBuffer; - this.externalStorageMonitor = externalStorageMonitor; - this.localMediaPlayer = localMediaPlayer; + val downloadChecker = Runnable { + try { + Timber.w("checking Downloads") + checkDownloadsInternal() + } catch (all: Exception) { + Timber.e(all, "checkDownloads() failed.") + } } - public void onCreate() - { - Runnable downloadChecker = () -> { - try - { - checkDownloads(); + fun onCreate() { + executorService = Executors.newSingleThreadScheduledExecutor() + executorService!!.scheduleWithFixedDelay(downloadChecker, 5, 5, TimeUnit.SECONDS) + Timber.i("Downloader created") + } + + fun onDestroy() { + stop() + clearPlaylist() + clearBackground() + Timber.i("Downloader destroyed") + } + + fun stop() { + if (executorService != null) executorService!!.shutdown() + Timber.i("Downloader stopped") + } + + fun checkDownloads() { + executorService?.execute(downloadChecker) + } + + @Synchronized + fun checkDownloadsInternal() { + if (!isExternalStoragePresent() || !externalStorageMonitor.isExternalStorageAvailable) { + return + } + if (shufflePlayBuffer.isEnabled) { + checkShufflePlay() + } + if (jukeboxMediaPlayer.isEnabled || !isNetworkConnected()) { + return + } + + // Check the active downloads for failures or completions + activelyDownloading.retainAll { + when { + it.isDownloading -> true + it.isFailed && it.shouldRetry() -> { + // Add it back to queue + downloadQueue.add(it) + false + } + else -> { + it.cleanup() + false + } } - catch (Throwable x) - { - Timber.e(x,"checkDownloads() failed."); + } + + + // Check if need to preload more from playlist + val preloadCount = getPreloadCount() + + // Start preloading at the current playing song + var start = if (localMediaPlayer.currentPlaying == null) 0 else currentPlayingIndex + if (start == -1) start = 0 + + var end = (start + preloadCount).coerceAtMost(playList.size) + + // Playlist also contains played songs!!!! + for (i in start until end) { + val download = playList[i] + + // Set correct priority (the lower the number, the higher the priority) + download.priority = i + + // Add file to queue if not in one of the queues already. + if (!download.isWorkDone && !activelyDownloading.contains(download) && !downloadQueue.contains(download)) { + downloadQueue.add(download) } - }; - - executorService = Executors.newSingleThreadScheduledExecutor(); - executorService.scheduleWithFixedDelay(downloadChecker, 5, 5, TimeUnit.SECONDS); - Timber.i("Downloader created"); - } - - public void onDestroy() - { - stop(); - clear(); - clearBackground(); - Timber.i("Downloader destroyed"); - } - - public void stop() - { - if (executorService != null) executorService.shutdown(); - Timber.i("Downloader stopped"); - } - - public synchronized void checkDownloads() - { - if (!Util.isExternalStoragePresent() || !externalStorageMonitor.isExternalStorageAvailable()) - { - return; } - if (shufflePlayBuffer.isEnabled) - { - checkShufflePlay(); - } + // Fill up active List with waiting tasks + while (activelyDownloading.size < PARALLEL_DOWNLOADS && downloadQueue.size > 0 ) { + val task = downloadQueue.remove() + activelyDownloading.add(task) + task.download() - if (jukeboxMediaPlayer.getValue().isEnabled() || !Util.isNetworkConnected()) - { - return; - } - - if (downloadList.isEmpty() && backgroundDownloadList.isEmpty()) - { - return; - } - - // Need to download current playing? - if (localMediaPlayer.currentPlaying != null && localMediaPlayer.currentPlaying != currentDownloading && !localMediaPlayer.currentPlaying.isWorkDone()) - { - // Cancel current download, if necessary. - if (currentDownloading != null) - { - currentDownloading.cancelDownload(); + // The next file on the playlist is currently downloading + // TODO: really necessary? + if (playList.indexOf(task) == 1) { + localMediaPlayer.setNextPlayerState(PlayerState.DOWNLOADING) } - - currentDownloading = localMediaPlayer.currentPlaying; - currentDownloading.download(); - cleanupCandidates.add(currentDownloading); - - // Delete obsolete .partial and .complete files. - cleanup(); - return; } - // Find a suitable target for download. - if (currentDownloading != null && - !currentDownloading.isWorkDone() && - (!currentDownloading.isFailed() || (downloadList.isEmpty() && backgroundDownloadList.isEmpty()))) - { - cleanup(); - return; - } + } - // There is a target to download - currentDownloading = null; - int n = downloadList.size(); - int preloaded = 0; +// fun oldStuff() { +// // Need to download current playing? +// if (localMediaPlayer.currentPlaying != null && localMediaPlayer.currentPlaying != currentDownloading && !localMediaPlayer.currentPlaying!!.isWorkDone) { +// // Cancel current download, if necessary. +// if (currentDownloading != null) { +// currentDownloading!!.cancelDownload() +// } +// currentDownloading = localMediaPlayer.currentPlaying +// currentDownloading!!.download() +// cleanupCandidates.add(currentDownloading) +// +// // Delete obsolete .partial and .complete files. +// cleanup() +// return +// } +// +// // Find a suitable target for download. +// if (currentDownloading != null && +// !currentDownloading!!.isWorkDone && +// (!currentDownloading!!.isFailed || playList.isEmpty() && backgroundDownloadList.isEmpty()) +// ) { +// cleanup() +// return +// } +// +// // There is a target to download +// currentDownloading = null +// val n = playList.size +// var preloaded = 0 +// if (n != 0) { +// var start = if (localMediaPlayer.currentPlaying == null) 0 else currentPlayingIndex +// if (start == -1) start = 0 +// var i = start +// // Check all DownloadFiles on the playlist +// do { +// val downloadFile = playList[i] +// if (!downloadFile.isWorkDone) { +// if (downloadFile.shouldSave() || preloaded < getPreloadCount()) { +// currentDownloading = downloadFile +// currentDownloading!!.download() +// cleanupCandidates.add(currentDownloading) +// if (i == start + 1) { +// // The next file on the playlist is currently downloading +// localMediaPlayer.setNextPlayerState(PlayerState.DOWNLOADING) +// } +// break +// } +// } else if (localMediaPlayer.currentPlaying != downloadFile) { +// preloaded++ +// } +// i = (i + 1) % n +// } while (i != start) +// } +// +// // If the downloadList contains no work, check the backgroundDownloadList +// if ((preloaded + 1 == n || preloaded >= getPreloadCount() || playList.isEmpty()) && backgroundDownloadList.isNotEmpty()) { +// var i = 0 +// while (i < backgroundDownloadList.size) { +// val downloadFile = backgroundDownloadList[i] +// if (downloadFile.isWorkDone && (!downloadFile.shouldSave() || downloadFile.isSaved)) { +// scanMedia(downloadFile.completeFile) +// +// // Don't need to keep list like active song list +// backgroundDownloadList.removeAt(i) +// downloadListUpdateRevision++ +// i-- +// } else if (downloadFile.isFailed && !downloadFile.shouldRetry()) { +// // Don't continue to attempt to download forever +// backgroundDownloadList.removeAt(i) +// downloadListUpdateRevision++ +// i-- +// } else { +// currentDownloading = downloadFile +// currentDownloading!!.download() +// cleanupCandidates.add(currentDownloading) +// break +// } +// i++ +// } +// } +// +// } - if (n != 0) - { - int start = localMediaPlayer.currentPlaying == null ? 0 : getCurrentPlayingIndex(); - if (start == -1) start = 0; + @get:Synchronized + val currentPlayingIndex: Int + get() = playList.indexOf(localMediaPlayer.currentPlaying) - int i = start; - // Check all DownloadFiles on the playlist - do - { - DownloadFile downloadFile = downloadList.get(i); - if (!downloadFile.isWorkDone()) - { - if (downloadFile.shouldSave() || preloaded < Util.getPreloadCount()) - { - currentDownloading = downloadFile; - currentDownloading.download(); - cleanupCandidates.add(currentDownloading); - if (i == (start + 1)) - { - // The next file on the playlist is currently downloading - localMediaPlayer.setNextPlayerState(DOWNLOADING); + @get:Synchronized + val downloadListDuration: Long + get() { + var totalDuration: Long = 0 + for (downloadFile in playList) { + val song = downloadFile.song + if (!song.isDirectory) { + if (song.artist != null) { + if (song.duration != null) { + totalDuration += song.duration!!.toLong() } - break; - } - } - else if (localMediaPlayer.currentPlaying != downloadFile) - { - preloaded++; - } - - i = (i + 1) % n; - } while (i != start); - } - - // If the downloadList contains no work, check the backgroundDownloadList - if ((preloaded + 1 == n || preloaded >= Util.getPreloadCount() || downloadList.isEmpty()) && !backgroundDownloadList.isEmpty()) - { - for (int i = 0; i < backgroundDownloadList.size(); i++) - { - DownloadFile downloadFile = backgroundDownloadList.get(i); - if (downloadFile.isWorkDone() && (!downloadFile.shouldSave() || downloadFile.isSaved())) - { - Util.scanMedia(downloadFile.getCompleteFile()); - - // Don't need to keep list like active song list - backgroundDownloadList.remove(i); - revision++; - i--; - } - else if (downloadFile.isFailed() && !downloadFile.shouldRetry()) { - // Don't continue to attempt to download forever - backgroundDownloadList.remove(i); - revision++; - i--; - } - else - { - currentDownloading = downloadFile; - currentDownloading.download(); - cleanupCandidates.add(currentDownloading); - break; - } - } - } - - // Delete obsolete .partial and .complete files. - cleanup(); - } - - public synchronized int getCurrentPlayingIndex() - { - return downloadList.indexOf(localMediaPlayer.currentPlaying); - } - - public long getDownloadListDuration() - { - long totalDuration = 0; - - for (DownloadFile downloadFile : downloadList) - { - MusicDirectory.Entry entry = downloadFile.getSong(); - - if (!entry.isDirectory()) - { - if (entry.getArtist() != null) - { - Integer duration = entry.getDuration(); - - if (duration != null) - { - totalDuration += duration; } } } + return totalDuration } - return totalDuration; + @get:Synchronized + val downloads: List + get() { + val temp: MutableList = ArrayList() + temp.addAll(playList) + temp.addAll(activelyDownloading) + temp.addAll(downloadQueue) + return temp.distinct() + } + + @Synchronized + fun clearPlaylist() { + playList.clear() + + // Cancel all active downloads with a high priority + for (download in activelyDownloading) { + if (download.priority < 100) + download.cancelDownload() + } + + downloadListUpdateRevision++ } - public synchronized List getDownloads() - { - List temp = new ArrayList<>(); - temp.addAll(downloadList); - temp.addAll(backgroundDownloadList); - return temp; + @Synchronized + private fun clearBackground() { + // Clear the pending queue + downloadQueue.clear() + + // Cancel all active downloads with a low priority + for (download in activelyDownloading) { + if (download.priority >= 100) + download.cancelDownload() + } + + downloadListUpdateRevision++ } - public long getDownloadListUpdateRevision() - { - return revision; - } - - public synchronized void clear() - { - downloadList.clear(); - revision++; - if (currentDownloading != null) - { - currentDownloading.cancelDownload(); - currentDownloading = null; + @Synchronized + fun clearActiveDownloads() { + // Cancel all active downloads with a low priority + for (download in activelyDownloading) { + download.cancelDownload() } } - private void clearBackground() - { - if (currentDownloading != null && backgroundDownloadList.contains(currentDownloading)) - { - currentDownloading.cancelDownload(); - currentDownloading = null; + @Synchronized + fun removeFromPlaylist(downloadFile: DownloadFile) { + if (activelyDownloading.contains(downloadFile)) { + downloadFile.cancelDownload() } - backgroundDownloadList.clear(); + playList.remove(downloadFile) + downloadListUpdateRevision++ } - public synchronized void removeDownloadFile(DownloadFile downloadFile) - { - if (downloadFile == currentDownloading) - { - currentDownloading.cancelDownload(); - currentDownloading = null; + @Synchronized + fun addToPlaylist( + songs: List, + save: Boolean, + autoPlay: Boolean, + playNext: Boolean, + newPlaylist: Boolean + ) { + shufflePlayBuffer.isEnabled = false + var offset = 1 + if (songs.isEmpty()) { + return } - - downloadList.remove(downloadFile); - backgroundDownloadList.remove(downloadFile); - revision++; - } - - public synchronized void download(List songs, boolean save, boolean autoPlay, boolean playNext, boolean newPlaylist) - { - shufflePlayBuffer.isEnabled = false; - int offset = 1; - - if (songs.isEmpty()) - { - return; + if (newPlaylist) { + playList.clear() } - - if (newPlaylist) - { - downloadList.clear(); - } - - if (playNext) - { - if (autoPlay && getCurrentPlayingIndex() >= 0) - { - offset = 0; + if (playNext) { + if (autoPlay && currentPlayingIndex >= 0) { + offset = 0 } - - for (MusicDirectory.Entry song : songs) - { - DownloadFile downloadFile = new DownloadFile(song, save); - downloadList.add(getCurrentPlayingIndex() + offset, downloadFile); - offset++; + for (song in songs) { + val downloadFile = DownloadFile(song!!, save) + playList.add(currentPlayingIndex + offset, downloadFile) + offset++ + } + } else { + for (song in songs) { + val downloadFile = DownloadFile(song!!, save) + playList.add(downloadFile) } } - else - { - for (MusicDirectory.Entry song : songs) - { - DownloadFile downloadFile = new DownloadFile(song, save); - downloadList.add(downloadFile); + downloadListUpdateRevision++ + //checkDownloads() + } + + @Synchronized + fun downloadBackground(songs: List, save: Boolean) { + + // Because of the priority handling we add the songs in the reverse order they + // were requested, then it is correct in the end. + for (song in songs.asReversed()) { + downloadQueue.add(DownloadFile(song, save)) + } + + downloadListUpdateRevision++ + //checkDownloads() + } + + @Synchronized + fun shuffle() { + playList.shuffle() + + // Move the current song to the top.. + if (localMediaPlayer.currentPlaying != null) { + playList.remove(localMediaPlayer.currentPlaying) + playList.add(0, localMediaPlayer.currentPlaying!!) + } + + downloadListUpdateRevision++ + } + + @Synchronized + fun getDownloadFileForSong(song: MusicDirectory.Entry): DownloadFile { + for (downloadFile in playList) { + if (downloadFile.song == song) { + return downloadFile } } - revision++; - } - - public synchronized void downloadBackground(List songs, boolean save) - { - for (MusicDirectory.Entry song : songs) - { - DownloadFile downloadFile = new DownloadFile(song, save); - backgroundDownloadList.add(downloadFile); - } - - revision++; - - checkDownloads(); - } - - public synchronized void shuffle() - { - Collections.shuffle(downloadList); - if (localMediaPlayer.currentPlaying != null) - { - downloadList.remove(localMediaPlayer.currentPlaying); - downloadList.add(0, localMediaPlayer.currentPlaying); - } - revision++; - } - - public synchronized DownloadFile getDownloadFileForSong(MusicDirectory.Entry song) - { - for (DownloadFile downloadFile : downloadList) - { - if (downloadFile.getSong().equals(song) && ((downloadFile.isDownloading() && !downloadFile.isDownloadCancelled() && downloadFile.getPartialFile().exists()) || downloadFile.isWorkDone())) - { - return downloadFile; + for (downloadFile in activelyDownloading) { + if (downloadFile.song == song) { + return downloadFile } } - for (DownloadFile downloadFile : backgroundDownloadList) - { - if (downloadFile.getSong().equals(song)) - { - return downloadFile; + for (downloadFile in downloadQueue) { + if (downloadFile.song == song) { + return downloadFile } } - - DownloadFile downloadFile = downloadFileCache.get(song); - if (downloadFile == null) - { - downloadFile = new DownloadFile(song, false); - downloadFileCache.put(song, downloadFile); + var downloadFile = downloadFileCache[song] + if (downloadFile == null) { + downloadFile = DownloadFile(song, false) + downloadFileCache.put(song, downloadFile) } - return downloadFile; + return downloadFile } - private synchronized void cleanup() - { - Iterator iterator = cleanupCandidates.iterator(); - while (iterator.hasNext()) - { - DownloadFile downloadFile = iterator.next(); - if (downloadFile != localMediaPlayer.currentPlaying && downloadFile != currentDownloading) - { - if (downloadFile.cleanup()) - { - iterator.remove(); - } - } - } - } - private synchronized void checkShufflePlay() - { + @Synchronized + private fun checkShufflePlay() { // Get users desired random playlist size - int listSize = Util.getMaxSongs(); - boolean wasEmpty = downloadList.isEmpty(); - - long revisionBefore = revision; + val listSize = getMaxSongs() + val wasEmpty = playList.isEmpty() + val revisionBefore = downloadListUpdateRevision // First, ensure that list is at least 20 songs long. - int size = downloadList.size(); - if (size < listSize) - { - for (MusicDirectory.Entry song : shufflePlayBuffer.get(listSize - size)) - { - DownloadFile downloadFile = new DownloadFile(song, false); - downloadList.add(downloadFile); - revision++; + val size = playList.size + if (size < listSize) { + for (song in shufflePlayBuffer[listSize - size]) { + val downloadFile = DownloadFile(song, false) + playList.add(downloadFile) + downloadListUpdateRevision++ } } - - int currIndex = localMediaPlayer.currentPlaying == null ? 0 : getCurrentPlayingIndex(); + + val currIndex = if (localMediaPlayer.currentPlaying == null) 0 else currentPlayingIndex // Only shift playlist if playing song #5 or later. - if (currIndex > 4) - { - int songsToShift = currIndex - 2; - for (MusicDirectory.Entry song : shufflePlayBuffer.get(songsToShift)) - { - downloadList.add(new DownloadFile(song, false)); - downloadList.get(0).cancelDownload(); - downloadList.remove(0); - revision++; + if (currIndex > 4) { + val songsToShift = currIndex - 2 + for (song in shufflePlayBuffer[songsToShift]) { + playList.add(DownloadFile(song, false)) + playList[0].cancelDownload() + playList.removeAt(0) + downloadListUpdateRevision++ } } - - if (revisionBefore != revision) - { - jukeboxMediaPlayer.getValue().updatePlaylist(); + if (revisionBefore != downloadListUpdateRevision) { + jukeboxMediaPlayer.updatePlaylist() } - - if (wasEmpty && !downloadList.isEmpty()) - { - if (jukeboxMediaPlayer.getValue().isEnabled()) - { - jukeboxMediaPlayer.getValue().skip(0, 0); - localMediaPlayer.setPlayerState(STARTED); - } - else - { - localMediaPlayer.play(downloadList.get(0)); + if (wasEmpty && playList.isNotEmpty()) { + if (jukeboxMediaPlayer.isEnabled) { + jukeboxMediaPlayer.skip(0, 0) + localMediaPlayer.setPlayerState(PlayerState.STARTED) + } else { + localMediaPlayer.play(playList[0]) } } } + companion object { + const val PARALLEL_DOWNLOADS = 3 + } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt index 718f34a1..4dca6c5f 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt @@ -456,7 +456,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon requireActivity().invalidateOptionsMenu() } - // Scroll to current playing/downloading. + // Scroll to current playing. private fun scrollToCurrent() { val adapter = playlistView.adapter if (adapter != null) { @@ -467,13 +467,6 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon return } } - val currentDownloading = mediaPlayerController.currentDownloading - for (i in 0 until count) { - if (currentDownloading == playlistView.getItemAtPosition(i)) { - playlistView.smoothScrollToPositionFromTop(i, 40) - return - } - } } } @@ -643,7 +636,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon return true } R.id.menu_remove -> { - mediaPlayerController.remove(song!!) + mediaPlayerController.removeFromPlaylist(song!!) onDownloadListChanged() return true } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/TrackCollectionFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/TrackCollectionFragment.kt index f370ab9f..35aefe19 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/TrackCollectionFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/TrackCollectionFragment.kt @@ -517,6 +517,7 @@ class TrackCollectionFragment : Fragment() { var pinnedCount = 0 for (song in selection) { + if (song == null) continue val downloadFile = mediaPlayerController.getDownloadFileForSong(song) if (downloadFile.isWorkDone) { deleteEnabled = true diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/AutoMediaBrowserService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/AutoMediaBrowserService.kt index 5bdfe45b..c3fc3b95 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/AutoMediaBrowserService.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/AutoMediaBrowserService.kt @@ -1066,7 +1066,7 @@ class AutoMediaBrowserService : MediaBrowserServiceCompat() { } private fun playSongs(songs: List?) { - mediaPlayerController.download( + mediaPlayerController.addToPlaylist( songs, save = false, autoPlay = true, @@ -1077,7 +1077,7 @@ class AutoMediaBrowserService : MediaBrowserServiceCompat() { } private fun playSong(song: MusicDirectory.Entry) { - mediaPlayerController.download( + mediaPlayerController.addToPlaylist( listOf(song), save = false, autoPlay = false, diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadFile.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadFile.kt index f8e88257..6b4c03b9 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadFile.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadFile.kt @@ -40,7 +40,7 @@ import timber.log.Timber class DownloadFile( val song: MusicDirectory.Entry, private val save: Boolean -) : KoinComponent { +) : KoinComponent, Comparable { val partialFile: File val completeFile: File private val saveFile: File = FileUtil.getSongFile(song) @@ -50,6 +50,8 @@ class DownloadFile( private val desiredBitRate: Int = Util.getMaxBitRate() + var priority = 100 + @Volatile private var isPlaying = false @@ -387,6 +389,10 @@ class DownloadFile( } } + override fun compareTo(other: DownloadFile): Int { + return priority.compareTo(other.priority) + } + companion object { const val MAX_RETRIES = 5 } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt index beb71844..61324184 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt @@ -74,7 +74,7 @@ class MediaPlayerController( autoPlay: Boolean, newPlaylist: Boolean ) { - download( + addToPlaylist( songs, save = false, autoPlay = false, @@ -167,7 +167,7 @@ class MediaPlayerController( @Synchronized @Suppress("LongParameterList") - fun download( + fun addToPlaylist( songs: List?, save: Boolean, autoPlay: Boolean, @@ -175,10 +175,12 @@ class MediaPlayerController( shuffle: Boolean, newPlaylist: Boolean ) { - downloader.download(songs, save, autoPlay, playNext, newPlaylist) + if (songs == null) return + val filteredSongs = songs.filterNotNull() + downloader.addToPlaylist(filteredSongs, save, autoPlay, playNext, newPlaylist) jukeboxMediaPlayer.updatePlaylist() if (shuffle) shuffle() - val isLastTrack = (downloader.downloadList.size - 1 == downloader.currentPlayingIndex) + val isLastTrack = (downloader.playList.size - 1 == downloader.currentPlayingIndex) if (!playNext && !autoPlay && isLastTrack) { val mediaPlayerService = runningInstance @@ -188,15 +190,15 @@ class MediaPlayerController( if (autoPlay) { play(0) } else { - if (localMediaPlayer.currentPlaying == null && downloader.downloadList.size > 0) { - localMediaPlayer.currentPlaying = downloader.downloadList[0] - downloader.downloadList[0].setPlaying(true) + if (localMediaPlayer.currentPlaying == null && downloader.playList.size > 0) { + localMediaPlayer.currentPlaying = downloader.playList[0] + downloader.playList[0].setPlaying(true) } downloader.checkDownloads() } downloadQueueSerializer.serializeDownloadQueue( - downloader.downloadList, + downloader.playList, downloader.currentPlayingIndex, playerPosition ) @@ -204,9 +206,11 @@ class MediaPlayerController( @Synchronized fun downloadBackground(songs: List?, save: Boolean) { - downloader.downloadBackground(songs, save) + if (songs == null) return + val filteredSongs = songs.filterNotNull() + downloader.downloadBackground(filteredSongs, save) downloadQueueSerializer.serializeDownloadQueue( - downloader.downloadList, + downloader.playList, downloader.currentPlayingIndex, playerPosition ) @@ -237,7 +241,7 @@ class MediaPlayerController( fun shuffle() { downloader.shuffle() downloadQueueSerializer.serializeDownloadQueue( - downloader.downloadList, + downloader.playList, downloader.currentPlayingIndex, playerPosition ) @@ -267,10 +271,10 @@ class MediaPlayerController( mediaPlayerService.clear(serialize) } else { // If no MediaPlayerService is available, just empty the playlist - downloader.clear() + downloader.clearPlaylist() if (serialize) { downloadQueueSerializer.serializeDownloadQueue( - downloader.downloadList, + downloader.playList, downloader.currentPlayingIndex, playerPosition ) } @@ -281,7 +285,7 @@ class MediaPlayerController( @Synchronized fun clearIncomplete() { reset() - val iterator = downloader.downloadList.iterator() + val iterator = downloader.playList.iterator() while (iterator.hasNext()) { val downloadFile = iterator.next() if (!downloadFile.isCompleteFileAvailable) { @@ -290,7 +294,7 @@ class MediaPlayerController( } downloadQueueSerializer.serializeDownloadQueue( - downloader.downloadList, + downloader.playList, downloader.currentPlayingIndex, playerPosition ) @@ -299,15 +303,15 @@ class MediaPlayerController( } @Synchronized - fun remove(downloadFile: DownloadFile) { + fun removeFromPlaylist(downloadFile: DownloadFile) { if (downloadFile == localMediaPlayer.currentPlaying) { reset() currentPlaying = null } - downloader.removeDownloadFile(downloadFile) + downloader.removeFromPlaylist(downloadFile) downloadQueueSerializer.serializeDownloadQueue( - downloader.downloadList, + downloader.playList, downloader.currentPlayingIndex, playerPosition ) @@ -321,15 +325,17 @@ class MediaPlayerController( } @Synchronized + // TODO: Make it require not null fun delete(songs: List) { - for (song in songs) { + for (song in songs.filterNotNull()) { downloader.getDownloadFileForSong(song).delete() } } @Synchronized + // TODO: Make it require not null fun unpin(songs: List) { - for (song in songs) { + for (song in songs.filterNotNull()) { downloader.getDownloadFileForSong(song).unpin() } } @@ -357,12 +363,12 @@ class MediaPlayerController( when (repeatMode) { RepeatMode.SINGLE, RepeatMode.OFF -> { // Play next if exists - if (index + 1 >= 0 && index + 1 < downloader.downloadList.size) { + if (index + 1 >= 0 && index + 1 < downloader.playList.size) { play(index + 1) } } RepeatMode.ALL -> { - play((index + 1) % downloader.downloadList.size) + play((index + 1) % downloader.playList.size) } else -> { } @@ -409,7 +415,7 @@ class MediaPlayerController( reset() // Cancel current download, if necessary. - downloader.currentDownloading?.cancelDownload() + downloader.clearActiveDownloads() } else { jukeboxMediaPlayer.stopJukeboxService() } @@ -489,16 +495,13 @@ class MediaPlayerController( } val playlistSize: Int - get() = downloader.downloadList.size + get() = downloader.playList.size val currentPlayingNumberOnPlaylist: Int get() = downloader.currentPlayingIndex - val currentDownloading: DownloadFile? - get() = downloader.currentDownloading - val playList: List - get() = downloader.downloadList + get() = downloader.playList val playListUpdateRevision: Long get() = downloader.downloadListUpdateRevision @@ -506,7 +509,7 @@ class MediaPlayerController( val playListDuration: Long get() = downloader.downloadListDuration - fun getDownloadFileForSong(song: MusicDirectory.Entry?): DownloadFile { + fun getDownloadFileForSong(song: MusicDirectory.Entry): DownloadFile { return downloader.getDownloadFileForSong(song) } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.kt index 3b9a1800..19572a2d 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.kt @@ -76,7 +76,7 @@ class MediaPlayerLifecycleSupport : KoinComponent { // Work-around: Serialize again, as the restore() method creates a // serialization without current playing info. downloadQueueSerializer.serializeDownloadQueue( - downloader.downloadList, + downloader.playList, downloader.currentPlayingIndex, mediaPlayerController.playerPosition ) @@ -94,7 +94,7 @@ class MediaPlayerLifecycleSupport : KoinComponent { if (!created) return downloadQueueSerializer.serializeDownloadQueueNow( - downloader.downloadList, + downloader.playList, downloader.currentPlayingIndex, mediaPlayerController.playerPosition ) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt index 4e6c14d7..318e7b08 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt @@ -88,7 +88,7 @@ class MediaPlayerService : Service() { localMediaPlayer.onPrepared = { downloadQueueSerializer.serializeDownloadQueue( - downloader.downloadList, + downloader.playList, downloader.currentPlayingIndex, playerPosition ) @@ -189,7 +189,7 @@ class MediaPlayerService : Service() { @Synchronized fun setCurrentPlaying(currentPlayingIndex: Int) { try { - localMediaPlayer.setCurrentPlaying(downloader.downloadList[currentPlayingIndex]) + localMediaPlayer.setCurrentPlaying(downloader.playList[currentPlayingIndex]) } catch (ignored: IndexOutOfBoundsException) { } } @@ -208,7 +208,7 @@ class MediaPlayerService : Service() { if (index != -1) { when (repeatMode) { RepeatMode.OFF -> index += 1 - RepeatMode.ALL -> index = (index + 1) % downloader.downloadList.size + RepeatMode.ALL -> index = (index + 1) % downloader.playList.size RepeatMode.SINGLE -> { } else -> { @@ -217,8 +217,8 @@ class MediaPlayerService : Service() { } localMediaPlayer.clearNextPlaying(false) - if (index < downloader.downloadList.size && index != -1) { - localMediaPlayer.setNextPlaying(downloader.downloadList[index]) + if (index < downloader.playList.size && index != -1) { + localMediaPlayer.setNextPlaying(downloader.playList[index]) } else { localMediaPlayer.clearNextPlaying(true) } @@ -271,7 +271,7 @@ class MediaPlayerService : Service() { @Synchronized fun play(index: Int, start: Boolean) { Timber.v("play requested for %d", index) - if (index < 0 || index >= downloader.downloadList.size) { + if (index < 0 || index >= downloader.playList.size) { resetPlayback() } else { setCurrentPlaying(index) @@ -280,7 +280,7 @@ class MediaPlayerService : Service() { jukeboxMediaPlayer.skip(index, 0) localMediaPlayer.setPlayerState(PlayerState.STARTED) } else { - localMediaPlayer.play(downloader.downloadList[index]) + localMediaPlayer.play(downloader.playList[index]) } } downloader.checkDownloads() @@ -293,7 +293,7 @@ class MediaPlayerService : Service() { localMediaPlayer.reset() localMediaPlayer.setCurrentPlaying(null) downloadQueueSerializer.serializeDownloadQueue( - downloader.downloadList, + downloader.playList, downloader.currentPlayingIndex, playerPosition ) } @@ -395,7 +395,7 @@ class MediaPlayerService : Service() { if (playerState === PlayerState.PAUSED) { downloadQueueSerializer.serializeDownloadQueue( - downloader.downloadList, downloader.currentPlayingIndex, playerPosition + downloader.playList, downloader.currentPlayingIndex, playerPosition ) } @@ -408,8 +408,8 @@ class MediaPlayerService : Service() { Util.broadcastPlaybackStatusChange(context, playerState) Util.broadcastA2dpPlayStatusChange( context, playerState, song, - downloader.downloadList.size + downloader.backgroundDownloadList.size, - downloader.downloadList.indexOf(currentPlaying) + 1, playerPosition + downloader.playList.size, + downloader.playList.indexOf(currentPlaying) + 1, playerPosition ) // Update widget @@ -455,7 +455,7 @@ class MediaPlayerService : Service() { if (index != -1) { when (repeatMode) { RepeatMode.OFF -> { - if (index + 1 < 0 || index + 1 >= downloader.downloadList.size) { + if (index + 1 < 0 || index + 1 >= downloader.playList.size) { if (Util.getShouldClearPlaylist()) { clear(true) jukeboxMediaPlayer.updatePlaylist() @@ -466,7 +466,7 @@ class MediaPlayerService : Service() { } } RepeatMode.ALL -> { - play((index + 1) % downloader.downloadList.size) + play((index + 1) % downloader.playList.size) } RepeatMode.SINGLE -> play(index) else -> { @@ -480,12 +480,12 @@ class MediaPlayerService : Service() { @Synchronized fun clear(serialize: Boolean) { localMediaPlayer.reset() - downloader.clear() + downloader.clearPlaylist() localMediaPlayer.setCurrentPlaying(null) setNextPlaying() if (serialize) { downloadQueueSerializer.serializeDownloadQueue( - downloader.downloadList, + downloader.playList, downloader.currentPlayingIndex, playerPosition ) } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/DownloadHandler.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/DownloadHandler.kt index b30490ac..a0f21f2c 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/DownloadHandler.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/DownloadHandler.kt @@ -39,7 +39,7 @@ class DownloadHandler( mediaPlayerController.clear() } networkAndStorageChecker.warnIfNetworkOrStorageUnavailable() - mediaPlayerController.download( + mediaPlayerController.addToPlaylist( songs, save, autoPlay, @@ -297,7 +297,7 @@ class DownloadHandler( if (unpin) { mediaPlayerController.unpin(songs) } else { - mediaPlayerController.download( + mediaPlayerController.addToPlaylist( songs, save, autoPlay, diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/view/SongView.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/view/SongView.kt index 54fcf3a1..16dd3a3a 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/view/SongView.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/view/SongView.kt @@ -218,10 +218,13 @@ class SongView(context: Context) : UpdateView(context), Checkable, KoinComponent override fun updateBackground() {} + @Synchronized public override fun update() { updateBackground() - downloadFile = mediaPlayerController.getDownloadFileForSong(entry) + val song = entry ?: return + + downloadFile = mediaPlayerController.getDownloadFileForSong(song) updateDownloadStatus(downloadFile!!) From 594e94eea77df94f6ca8b1daf75ccfad24a56cbb Mon Sep 17 00:00:00 2001 From: tzugen Date: Sat, 28 Aug 2021 00:02:50 +0200 Subject: [PATCH 4/5] Finish --- .../moire/ultrasonic/service/Downloader.kt | 269 +++++++----------- .../service/MediaPlayerController.kt | 32 +-- .../service/MediaPlayerLifecycleSupport.kt | 4 +- .../ultrasonic/service/MediaPlayerService.kt | 28 +- 4 files changed, 127 insertions(+), 206 deletions(-) diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.kt b/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.kt index ce4fa733..e5f101f4 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.kt +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.kt @@ -1,21 +1,21 @@ package org.moire.ultrasonic.service -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject -import org.moire.ultrasonic.domain.MusicDirectory -import org.moire.ultrasonic.util.Util.isExternalStoragePresent -import org.moire.ultrasonic.util.Util.isNetworkConnected -import org.moire.ultrasonic.util.Util.getPreloadCount -import org.moire.ultrasonic.util.Util.getMaxSongs -import org.moire.ultrasonic.util.ShufflePlayBuffer -import timber.log.Timber -import org.moire.ultrasonic.domain.PlayerState -import org.moire.ultrasonic.util.LRUCache import java.util.ArrayList import java.util.PriorityQueue import java.util.concurrent.Executors import java.util.concurrent.ScheduledExecutorService import java.util.concurrent.TimeUnit +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.moire.ultrasonic.domain.MusicDirectory +import org.moire.ultrasonic.domain.PlayerState +import org.moire.ultrasonic.util.LRUCache +import org.moire.ultrasonic.util.ShufflePlayBuffer +import org.moire.ultrasonic.util.Util.getMaxSongs +import org.moire.ultrasonic.util.Util.getPreloadCount +import org.moire.ultrasonic.util.Util.isExternalStoragePresent +import org.moire.ultrasonic.util.Util.isNetworkConnected +import timber.log.Timber /** * This class is responsible for maintaining the playlist and downloading @@ -25,17 +25,18 @@ class Downloader( private val shufflePlayBuffer: ShufflePlayBuffer, private val externalStorageMonitor: ExternalStorageMonitor, private val localMediaPlayer: LocalMediaPlayer -): KoinComponent { - val playList: MutableList = ArrayList() +) : KoinComponent { + val playlist: MutableList = ArrayList() private val downloadQueue: PriorityQueue = PriorityQueue() private val activelyDownloading: MutableList = ArrayList() private val jukeboxMediaPlayer: JukeboxMediaPlayer by inject() - + private val downloadFileCache = LRUCache(100) private var executorService: ScheduledExecutorService? = null - var downloadListUpdateRevision: Long = 0 + + var playlistUpdateRevision: Long = 0 private set val downloadChecker = Runnable { @@ -49,7 +50,9 @@ class Downloader( fun onCreate() { executorService = Executors.newSingleThreadScheduledExecutor() - executorService!!.scheduleWithFixedDelay(downloadChecker, 5, 5, TimeUnit.SECONDS) + executorService!!.scheduleWithFixedDelay( + downloadChecker, CHECK_INTERVAL, CHECK_INTERVAL, TimeUnit.SECONDS + ) Timber.i("Downloader created") } @@ -81,7 +84,47 @@ class Downloader( return } - // Check the active downloads for failures or completions + // Check the active downloads for failures or completions and remove them + cleanupActiveDownloads() + + // Check if need to preload more from playlist + val preloadCount = getPreloadCount() + + // Start preloading at the current playing song + var start = currentPlayingIndex + if (start == -1) start = 0 + + val end = (start + preloadCount).coerceAtMost(playlist.size) + + for (i in start until end) { + val download = playlist[i] + + // Set correct priority (the lower the number, the higher the priority) + download.priority = i + + // Add file to queue if not in one of the queues already. + if (!download.isWorkDone && + !activelyDownloading.contains(download) && + !downloadQueue.contains(download) + ) { + downloadQueue.add(download) + } + } + + // Fill up active List with waiting tasks + while (activelyDownloading.size < PARALLEL_DOWNLOADS && downloadQueue.size > 0) { + val task = downloadQueue.remove() + activelyDownloading.add(task) + task.download() + + // The next file on the playlist is currently downloading + if (playlist.indexOf(task) == 1) { + localMediaPlayer.setNextPlayerState(PlayerState.DOWNLOADING) + } + } + } + + private fun cleanupActiveDownloads() { activelyDownloading.retainAll { when { it.isDownloading -> true @@ -96,138 +139,17 @@ class Downloader( } } } - - - // Check if need to preload more from playlist - val preloadCount = getPreloadCount() - - // Start preloading at the current playing song - var start = if (localMediaPlayer.currentPlaying == null) 0 else currentPlayingIndex - if (start == -1) start = 0 - - var end = (start + preloadCount).coerceAtMost(playList.size) - - // Playlist also contains played songs!!!! - for (i in start until end) { - val download = playList[i] - - // Set correct priority (the lower the number, the higher the priority) - download.priority = i - - // Add file to queue if not in one of the queues already. - if (!download.isWorkDone && !activelyDownloading.contains(download) && !downloadQueue.contains(download)) { - downloadQueue.add(download) - } - } - - // Fill up active List with waiting tasks - while (activelyDownloading.size < PARALLEL_DOWNLOADS && downloadQueue.size > 0 ) { - val task = downloadQueue.remove() - activelyDownloading.add(task) - task.download() - - // The next file on the playlist is currently downloading - // TODO: really necessary? - if (playList.indexOf(task) == 1) { - localMediaPlayer.setNextPlayerState(PlayerState.DOWNLOADING) - } - } - } - -// fun oldStuff() { -// // Need to download current playing? -// if (localMediaPlayer.currentPlaying != null && localMediaPlayer.currentPlaying != currentDownloading && !localMediaPlayer.currentPlaying!!.isWorkDone) { -// // Cancel current download, if necessary. -// if (currentDownloading != null) { -// currentDownloading!!.cancelDownload() -// } -// currentDownloading = localMediaPlayer.currentPlaying -// currentDownloading!!.download() -// cleanupCandidates.add(currentDownloading) -// -// // Delete obsolete .partial and .complete files. -// cleanup() -// return -// } -// -// // Find a suitable target for download. -// if (currentDownloading != null && -// !currentDownloading!!.isWorkDone && -// (!currentDownloading!!.isFailed || playList.isEmpty() && backgroundDownloadList.isEmpty()) -// ) { -// cleanup() -// return -// } -// -// // There is a target to download -// currentDownloading = null -// val n = playList.size -// var preloaded = 0 -// if (n != 0) { -// var start = if (localMediaPlayer.currentPlaying == null) 0 else currentPlayingIndex -// if (start == -1) start = 0 -// var i = start -// // Check all DownloadFiles on the playlist -// do { -// val downloadFile = playList[i] -// if (!downloadFile.isWorkDone) { -// if (downloadFile.shouldSave() || preloaded < getPreloadCount()) { -// currentDownloading = downloadFile -// currentDownloading!!.download() -// cleanupCandidates.add(currentDownloading) -// if (i == start + 1) { -// // The next file on the playlist is currently downloading -// localMediaPlayer.setNextPlayerState(PlayerState.DOWNLOADING) -// } -// break -// } -// } else if (localMediaPlayer.currentPlaying != downloadFile) { -// preloaded++ -// } -// i = (i + 1) % n -// } while (i != start) -// } -// -// // If the downloadList contains no work, check the backgroundDownloadList -// if ((preloaded + 1 == n || preloaded >= getPreloadCount() || playList.isEmpty()) && backgroundDownloadList.isNotEmpty()) { -// var i = 0 -// while (i < backgroundDownloadList.size) { -// val downloadFile = backgroundDownloadList[i] -// if (downloadFile.isWorkDone && (!downloadFile.shouldSave() || downloadFile.isSaved)) { -// scanMedia(downloadFile.completeFile) -// -// // Don't need to keep list like active song list -// backgroundDownloadList.removeAt(i) -// downloadListUpdateRevision++ -// i-- -// } else if (downloadFile.isFailed && !downloadFile.shouldRetry()) { -// // Don't continue to attempt to download forever -// backgroundDownloadList.removeAt(i) -// downloadListUpdateRevision++ -// i-- -// } else { -// currentDownloading = downloadFile -// currentDownloading!!.download() -// cleanupCandidates.add(currentDownloading) -// break -// } -// i++ -// } -// } -// -// } - @get:Synchronized val currentPlayingIndex: Int - get() = playList.indexOf(localMediaPlayer.currentPlaying) + get() = playlist.indexOf(localMediaPlayer.currentPlaying) @get:Synchronized val downloadListDuration: Long get() { var totalDuration: Long = 0 - for (downloadFile in playList) { + for (downloadFile in playlist) { val song = downloadFile.song if (!song.isDirectory) { if (song.artist != null) { @@ -244,7 +166,7 @@ class Downloader( val downloads: List get() { val temp: MutableList = ArrayList() - temp.addAll(playList) + temp.addAll(playlist) temp.addAll(activelyDownloading) temp.addAll(downloadQueue) return temp.distinct() @@ -252,7 +174,7 @@ class Downloader( @Synchronized fun clearPlaylist() { - playList.clear() + playlist.clear() // Cancel all active downloads with a high priority for (download in activelyDownloading) { @@ -260,7 +182,7 @@ class Downloader( download.cancelDownload() } - downloadListUpdateRevision++ + playlistUpdateRevision++ } @Synchronized @@ -273,8 +195,6 @@ class Downloader( if (download.priority >= 100) download.cancelDownload() } - - downloadListUpdateRevision++ } @Synchronized @@ -290,8 +210,8 @@ class Downloader( if (activelyDownloading.contains(downloadFile)) { downloadFile.cancelDownload() } - playList.remove(downloadFile) - downloadListUpdateRevision++ + playlist.remove(downloadFile) + playlistUpdateRevision++ } @Synchronized @@ -308,7 +228,7 @@ class Downloader( return } if (newPlaylist) { - playList.clear() + playlist.clear() } if (playNext) { if (autoPlay && currentPlayingIndex >= 0) { @@ -316,17 +236,17 @@ class Downloader( } for (song in songs) { val downloadFile = DownloadFile(song!!, save) - playList.add(currentPlayingIndex + offset, downloadFile) + playlist.add(currentPlayingIndex + offset, downloadFile) offset++ } } else { for (song in songs) { val downloadFile = DownloadFile(song!!, save) - playList.add(downloadFile) + playlist.add(downloadFile) } } - downloadListUpdateRevision++ - //checkDownloads() + playlistUpdateRevision++ + checkDownloads() } @Synchronized @@ -338,26 +258,26 @@ class Downloader( downloadQueue.add(DownloadFile(song, save)) } - downloadListUpdateRevision++ - //checkDownloads() + checkDownloads() } @Synchronized fun shuffle() { - playList.shuffle() + playlist.shuffle() // Move the current song to the top.. if (localMediaPlayer.currentPlaying != null) { - playList.remove(localMediaPlayer.currentPlaying) - playList.add(0, localMediaPlayer.currentPlaying!!) + playlist.remove(localMediaPlayer.currentPlaying) + playlist.add(0, localMediaPlayer.currentPlaying!!) } - downloadListUpdateRevision++ + playlistUpdateRevision++ } @Synchronized + @Suppress("ReturnCount") fun getDownloadFileForSong(song: MusicDirectory.Entry): DownloadFile { - for (downloadFile in playList) { + for (downloadFile in playlist) { if (downloadFile.song == song) { return downloadFile } @@ -380,49 +300,50 @@ class Downloader( return downloadFile } - @Synchronized private fun checkShufflePlay() { // Get users desired random playlist size val listSize = getMaxSongs() - val wasEmpty = playList.isEmpty() - val revisionBefore = downloadListUpdateRevision + val wasEmpty = playlist.isEmpty() + val revisionBefore = playlistUpdateRevision // First, ensure that list is at least 20 songs long. - val size = playList.size + val size = playlist.size if (size < listSize) { for (song in shufflePlayBuffer[listSize - size]) { val downloadFile = DownloadFile(song, false) - playList.add(downloadFile) - downloadListUpdateRevision++ + playlist.add(downloadFile) + playlistUpdateRevision++ } } - + val currIndex = if (localMediaPlayer.currentPlaying == null) 0 else currentPlayingIndex // Only shift playlist if playing song #5 or later. - if (currIndex > 4) { + if (currIndex > SHUFFLE_BUFFER_LIMIT) { val songsToShift = currIndex - 2 for (song in shufflePlayBuffer[songsToShift]) { - playList.add(DownloadFile(song, false)) - playList[0].cancelDownload() - playList.removeAt(0) - downloadListUpdateRevision++ + playlist.add(DownloadFile(song, false)) + playlist[0].cancelDownload() + playlist.removeAt(0) + playlistUpdateRevision++ } } - if (revisionBefore != downloadListUpdateRevision) { + if (revisionBefore != playlistUpdateRevision) { jukeboxMediaPlayer.updatePlaylist() } - if (wasEmpty && playList.isNotEmpty()) { + if (wasEmpty && playlist.isNotEmpty()) { if (jukeboxMediaPlayer.isEnabled) { jukeboxMediaPlayer.skip(0, 0) localMediaPlayer.setPlayerState(PlayerState.STARTED) } else { - localMediaPlayer.play(playList[0]) + localMediaPlayer.play(playlist[0]) } } } companion object { const val PARALLEL_DOWNLOADS = 3 + const val CHECK_INTERVAL = 5L + const val SHUFFLE_BUFFER_LIMIT = 4 } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt index 61324184..c3b400b1 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt @@ -180,7 +180,7 @@ class MediaPlayerController( downloader.addToPlaylist(filteredSongs, save, autoPlay, playNext, newPlaylist) jukeboxMediaPlayer.updatePlaylist() if (shuffle) shuffle() - val isLastTrack = (downloader.playList.size - 1 == downloader.currentPlayingIndex) + val isLastTrack = (downloader.playlist.size - 1 == downloader.currentPlayingIndex) if (!playNext && !autoPlay && isLastTrack) { val mediaPlayerService = runningInstance @@ -190,15 +190,15 @@ class MediaPlayerController( if (autoPlay) { play(0) } else { - if (localMediaPlayer.currentPlaying == null && downloader.playList.size > 0) { - localMediaPlayer.currentPlaying = downloader.playList[0] - downloader.playList[0].setPlaying(true) + if (localMediaPlayer.currentPlaying == null && downloader.playlist.size > 0) { + localMediaPlayer.currentPlaying = downloader.playlist[0] + downloader.playlist[0].setPlaying(true) } downloader.checkDownloads() } downloadQueueSerializer.serializeDownloadQueue( - downloader.playList, + downloader.playlist, downloader.currentPlayingIndex, playerPosition ) @@ -210,7 +210,7 @@ class MediaPlayerController( val filteredSongs = songs.filterNotNull() downloader.downloadBackground(filteredSongs, save) downloadQueueSerializer.serializeDownloadQueue( - downloader.playList, + downloader.playlist, downloader.currentPlayingIndex, playerPosition ) @@ -241,7 +241,7 @@ class MediaPlayerController( fun shuffle() { downloader.shuffle() downloadQueueSerializer.serializeDownloadQueue( - downloader.playList, + downloader.playlist, downloader.currentPlayingIndex, playerPosition ) @@ -274,7 +274,7 @@ class MediaPlayerController( downloader.clearPlaylist() if (serialize) { downloadQueueSerializer.serializeDownloadQueue( - downloader.playList, + downloader.playlist, downloader.currentPlayingIndex, playerPosition ) } @@ -285,7 +285,7 @@ class MediaPlayerController( @Synchronized fun clearIncomplete() { reset() - val iterator = downloader.playList.iterator() + val iterator = downloader.playlist.iterator() while (iterator.hasNext()) { val downloadFile = iterator.next() if (!downloadFile.isCompleteFileAvailable) { @@ -294,7 +294,7 @@ class MediaPlayerController( } downloadQueueSerializer.serializeDownloadQueue( - downloader.playList, + downloader.playlist, downloader.currentPlayingIndex, playerPosition ) @@ -311,7 +311,7 @@ class MediaPlayerController( downloader.removeFromPlaylist(downloadFile) downloadQueueSerializer.serializeDownloadQueue( - downloader.playList, + downloader.playlist, downloader.currentPlayingIndex, playerPosition ) @@ -363,12 +363,12 @@ class MediaPlayerController( when (repeatMode) { RepeatMode.SINGLE, RepeatMode.OFF -> { // Play next if exists - if (index + 1 >= 0 && index + 1 < downloader.playList.size) { + if (index + 1 >= 0 && index + 1 < downloader.playlist.size) { play(index + 1) } } RepeatMode.ALL -> { - play((index + 1) % downloader.playList.size) + play((index + 1) % downloader.playlist.size) } else -> { } @@ -495,16 +495,16 @@ class MediaPlayerController( } val playlistSize: Int - get() = downloader.playList.size + get() = downloader.playlist.size val currentPlayingNumberOnPlaylist: Int get() = downloader.currentPlayingIndex val playList: List - get() = downloader.playList + get() = downloader.playlist val playListUpdateRevision: Long - get() = downloader.downloadListUpdateRevision + get() = downloader.playlistUpdateRevision val playListDuration: Long get() = downloader.downloadListDuration diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.kt index 19572a2d..31c7df38 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.kt @@ -76,7 +76,7 @@ class MediaPlayerLifecycleSupport : KoinComponent { // Work-around: Serialize again, as the restore() method creates a // serialization without current playing info. downloadQueueSerializer.serializeDownloadQueue( - downloader.playList, + downloader.playlist, downloader.currentPlayingIndex, mediaPlayerController.playerPosition ) @@ -94,7 +94,7 @@ class MediaPlayerLifecycleSupport : KoinComponent { if (!created) return downloadQueueSerializer.serializeDownloadQueueNow( - downloader.playList, + downloader.playlist, downloader.currentPlayingIndex, mediaPlayerController.playerPosition ) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt index 318e7b08..7f234a75 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt @@ -88,7 +88,7 @@ class MediaPlayerService : Service() { localMediaPlayer.onPrepared = { downloadQueueSerializer.serializeDownloadQueue( - downloader.playList, + downloader.playlist, downloader.currentPlayingIndex, playerPosition ) @@ -189,7 +189,7 @@ class MediaPlayerService : Service() { @Synchronized fun setCurrentPlaying(currentPlayingIndex: Int) { try { - localMediaPlayer.setCurrentPlaying(downloader.playList[currentPlayingIndex]) + localMediaPlayer.setCurrentPlaying(downloader.playlist[currentPlayingIndex]) } catch (ignored: IndexOutOfBoundsException) { } } @@ -208,7 +208,7 @@ class MediaPlayerService : Service() { if (index != -1) { when (repeatMode) { RepeatMode.OFF -> index += 1 - RepeatMode.ALL -> index = (index + 1) % downloader.playList.size + RepeatMode.ALL -> index = (index + 1) % downloader.playlist.size RepeatMode.SINGLE -> { } else -> { @@ -217,8 +217,8 @@ class MediaPlayerService : Service() { } localMediaPlayer.clearNextPlaying(false) - if (index < downloader.playList.size && index != -1) { - localMediaPlayer.setNextPlaying(downloader.playList[index]) + if (index < downloader.playlist.size && index != -1) { + localMediaPlayer.setNextPlaying(downloader.playlist[index]) } else { localMediaPlayer.clearNextPlaying(true) } @@ -271,7 +271,7 @@ class MediaPlayerService : Service() { @Synchronized fun play(index: Int, start: Boolean) { Timber.v("play requested for %d", index) - if (index < 0 || index >= downloader.playList.size) { + if (index < 0 || index >= downloader.playlist.size) { resetPlayback() } else { setCurrentPlaying(index) @@ -280,7 +280,7 @@ class MediaPlayerService : Service() { jukeboxMediaPlayer.skip(index, 0) localMediaPlayer.setPlayerState(PlayerState.STARTED) } else { - localMediaPlayer.play(downloader.playList[index]) + localMediaPlayer.play(downloader.playlist[index]) } } downloader.checkDownloads() @@ -293,7 +293,7 @@ class MediaPlayerService : Service() { localMediaPlayer.reset() localMediaPlayer.setCurrentPlaying(null) downloadQueueSerializer.serializeDownloadQueue( - downloader.playList, + downloader.playlist, downloader.currentPlayingIndex, playerPosition ) } @@ -395,7 +395,7 @@ class MediaPlayerService : Service() { if (playerState === PlayerState.PAUSED) { downloadQueueSerializer.serializeDownloadQueue( - downloader.playList, downloader.currentPlayingIndex, playerPosition + downloader.playlist, downloader.currentPlayingIndex, playerPosition ) } @@ -408,8 +408,8 @@ class MediaPlayerService : Service() { Util.broadcastPlaybackStatusChange(context, playerState) Util.broadcastA2dpPlayStatusChange( context, playerState, song, - downloader.playList.size, - downloader.playList.indexOf(currentPlaying) + 1, playerPosition + downloader.playlist.size, + downloader.playlist.indexOf(currentPlaying) + 1, playerPosition ) // Update widget @@ -455,7 +455,7 @@ class MediaPlayerService : Service() { if (index != -1) { when (repeatMode) { RepeatMode.OFF -> { - if (index + 1 < 0 || index + 1 >= downloader.playList.size) { + if (index + 1 < 0 || index + 1 >= downloader.playlist.size) { if (Util.getShouldClearPlaylist()) { clear(true) jukeboxMediaPlayer.updatePlaylist() @@ -466,7 +466,7 @@ class MediaPlayerService : Service() { } } RepeatMode.ALL -> { - play((index + 1) % downloader.playList.size) + play((index + 1) % downloader.playlist.size) } RepeatMode.SINGLE -> play(index) else -> { @@ -485,7 +485,7 @@ class MediaPlayerService : Service() { setNextPlaying() if (serialize) { downloadQueueSerializer.serializeDownloadQueue( - downloader.playList, + downloader.playlist, downloader.currentPlayingIndex, playerPosition ) } From d08a38ea1c6685dcfd4856cc47d75f5acb83d4a0 Mon Sep 17 00:00:00 2001 From: tzugen Date: Sat, 28 Aug 2021 11:29:39 +0200 Subject: [PATCH 5/5] Small fixes --- .../main/java/org/moire/ultrasonic/service/Downloader.kt | 4 ++++ .../kotlin/org/moire/ultrasonic/service/DownloadFile.kt | 9 ++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.kt b/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.kt index e5f101f4..56984123 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.kt +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.kt @@ -20,6 +20,10 @@ import timber.log.Timber /** * This class is responsible for maintaining the playlist and downloading * its items from the network to the filesystem. + * + * TODO: Implement LiveData + * TODO: Move away from managing the queue with scheduled checks, instead use callbacks when + * Downloads are finished */ class Downloader( private val shufflePlayBuffer: ShufflePlayBuffer, diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadFile.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadFile.kt index 6b4c03b9..cc82c25d 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadFile.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadFile.kt @@ -204,7 +204,6 @@ class DownloadFile( return String.format("DownloadFile (%s)", song) } - @Suppress("TooGenericExceptionCaught") private inner class DownloadTask : CancellableTask() { override fun execute() { var inputStream: InputStream? = null @@ -292,7 +291,7 @@ class DownloadFile( Util.renameFile(partialFile, completeFile) } } - } catch (e: Exception) { + } catch (all: Exception) { Util.close(outputStream) Util.delete(completeFile) Util.delete(saveFile) @@ -301,7 +300,7 @@ class DownloadFile( if (retryCount > 0) { --retryCount } - Timber.w(e, "Failed to download '%s'.", song) + Timber.w(all, "Failed to download '%s'.", song) } } finally { Util.close(inputStream) @@ -339,8 +338,8 @@ class DownloadFile( // Download the largest size that we can display in the UI imageLoaderProvider.getImageLoader().cacheCoverArt(song) } - } catch (e: Exception) { - Timber.e(e, "Failed to get cover art.") + } catch (all: Exception) { + Timber.e(all, "Failed to get cover art.") } }