mirror of
https://gitlab.com/ultrasonic/ultrasonic.git
synced 2025-04-18 02:02:23 +03:00
Merge pull request #722 from ultrasonic/ready/OnlyRename
Cleanup terminology surrounding entry vs track vs song.
This commit is contained in:
commit
287169649a
@ -0,0 +1,27 @@
|
||||
package org.moire.ultrasonic.domain
|
||||
|
||||
import androidx.room.PrimaryKey
|
||||
import java.util.Date
|
||||
|
||||
data class Album(
|
||||
@PrimaryKey override var id: String,
|
||||
override var parent: String? = null,
|
||||
override var album: String? = null,
|
||||
override var title: String? = null,
|
||||
override val name: String? = null,
|
||||
override var discNumber: Int? = 0,
|
||||
override var coverArt: String? = null,
|
||||
override var songCount: Long? = null,
|
||||
override var created: Date? = null,
|
||||
override var artist: String? = null,
|
||||
override var artistId: String? = null,
|
||||
override var duration: Int? = 0,
|
||||
override var year: Int? = 0,
|
||||
override var genre: String? = null,
|
||||
override var starred: Boolean = false,
|
||||
override var path: String? = null,
|
||||
override var closeness: Int = 0,
|
||||
) : MusicDirectory.Child() {
|
||||
override var isDirectory = true
|
||||
override var isVideo = false
|
||||
}
|
@ -2,7 +2,6 @@ package org.moire.ultrasonic.domain
|
||||
|
||||
import java.io.Serializable
|
||||
import java.util.Date
|
||||
import org.moire.ultrasonic.domain.MusicDirectory.Entry
|
||||
|
||||
data class Bookmark(
|
||||
val position: Int = 0,
|
||||
@ -10,7 +9,7 @@ data class Bookmark(
|
||||
val comment: String,
|
||||
val created: Date? = null,
|
||||
val changed: Date? = null,
|
||||
val entry: Entry
|
||||
val track: Track
|
||||
) : Serializable {
|
||||
companion object {
|
||||
private const val serialVersionUID = 8988990025189807803L
|
||||
|
@ -1,8 +1,5 @@
|
||||
package org.moire.ultrasonic.domain
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import java.io.Serializable
|
||||
import java.util.Date
|
||||
|
||||
class MusicDirectory : ArrayList<MusicDirectory.Child>() {
|
||||
@ -20,9 +17,9 @@ class MusicDirectory : ArrayList<MusicDirectory.Child>() {
|
||||
return filter { it.isDirectory && includeDirs || !it.isDirectory && includeFiles }
|
||||
}
|
||||
|
||||
fun getTracks(): List<Entry> {
|
||||
fun getTracks(): List<Track> {
|
||||
return mapNotNull {
|
||||
it as? Entry
|
||||
it as? Track
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,87 +50,4 @@ class MusicDirectory : ArrayList<MusicDirectory.Child>() {
|
||||
abstract var closeness: Int
|
||||
abstract var isVideo: Boolean
|
||||
}
|
||||
|
||||
// TODO: Rename to Track
|
||||
@Entity
|
||||
data class Entry(
|
||||
@PrimaryKey override var id: String,
|
||||
override var parent: String? = null,
|
||||
override var isDirectory: Boolean = false,
|
||||
override var title: String? = null,
|
||||
override var album: String? = null,
|
||||
var albumId: String? = null,
|
||||
override var artist: String? = null,
|
||||
override var artistId: String? = null,
|
||||
var track: Int? = null,
|
||||
override var year: Int? = null,
|
||||
override var genre: String? = null,
|
||||
var contentType: String? = null,
|
||||
var suffix: String? = null,
|
||||
var transcodedContentType: String? = null,
|
||||
var transcodedSuffix: String? = null,
|
||||
override var coverArt: String? = null,
|
||||
var size: Long? = null,
|
||||
override var songCount: Long? = null,
|
||||
override var duration: Int? = null,
|
||||
var bitRate: Int? = null,
|
||||
override var path: String? = null,
|
||||
override var isVideo: Boolean = false,
|
||||
override var starred: Boolean = false,
|
||||
override var discNumber: Int? = null,
|
||||
var type: String? = null,
|
||||
override var created: Date? = null,
|
||||
override var closeness: Int = 0,
|
||||
var bookmarkPosition: Int = 0,
|
||||
var userRating: Int? = null,
|
||||
var averageRating: Float? = null,
|
||||
override var name: String? = null
|
||||
) : Serializable, Child() {
|
||||
fun setDuration(duration: Long) {
|
||||
this.duration = duration.toInt()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val serialVersionUID = -3339106650010798108L
|
||||
}
|
||||
|
||||
fun compareTo(other: Entry): Int {
|
||||
when {
|
||||
this.closeness == other.closeness -> {
|
||||
return 0
|
||||
}
|
||||
this.closeness > other.closeness -> {
|
||||
return -1
|
||||
}
|
||||
else -> {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun compareTo(other: Identifiable) = compareTo(other as Entry)
|
||||
}
|
||||
|
||||
data class Album(
|
||||
@PrimaryKey override var id: String,
|
||||
override var parent: String? = null,
|
||||
override var album: String? = null,
|
||||
override var title: String? = null,
|
||||
override val name: String? = null,
|
||||
override var discNumber: Int? = 0,
|
||||
override var coverArt: String? = null,
|
||||
override var songCount: Long? = null,
|
||||
override var created: Date? = null,
|
||||
override var artist: String? = null,
|
||||
override var artistId: String? = null,
|
||||
override var duration: Int? = 0,
|
||||
override var year: Int? = 0,
|
||||
override var genre: String? = null,
|
||||
override var starred: Boolean = false,
|
||||
override var path: String? = null,
|
||||
override var closeness: Int = 0,
|
||||
) : Child() {
|
||||
override var isDirectory = true
|
||||
override var isVideo = false
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,10 @@
|
||||
package org.moire.ultrasonic.domain
|
||||
|
||||
import org.moire.ultrasonic.domain.MusicDirectory.Album
|
||||
import org.moire.ultrasonic.domain.MusicDirectory.Entry
|
||||
|
||||
/**
|
||||
* The result of a search. Contains matching artists, albums and songs.
|
||||
*/
|
||||
data class SearchResult(
|
||||
val artists: List<ArtistOrIndex> = listOf(),
|
||||
val albums: List<Album> = listOf(),
|
||||
val songs: List<Entry> = listOf()
|
||||
val songs: List<Track> = listOf()
|
||||
)
|
||||
|
@ -1,7 +1,6 @@
|
||||
package org.moire.ultrasonic.domain
|
||||
|
||||
import java.io.Serializable
|
||||
import org.moire.ultrasonic.domain.MusicDirectory.Entry
|
||||
|
||||
data class Share(
|
||||
override var id: String,
|
||||
@ -12,7 +11,7 @@ data class Share(
|
||||
var lastVisited: String? = null,
|
||||
var expires: String? = null,
|
||||
var visitCount: Long? = null,
|
||||
private val entries: MutableList<Entry> = mutableListOf()
|
||||
private val tracks: MutableList<Track> = mutableListOf()
|
||||
) : Serializable, GenericEntry() {
|
||||
override val name: String?
|
||||
get() {
|
||||
@ -22,12 +21,12 @@ data class Share(
|
||||
return null
|
||||
}
|
||||
|
||||
fun getEntries(): List<Entry> {
|
||||
return entries.toList()
|
||||
fun getEntries(): List<Track> {
|
||||
return tracks.toList()
|
||||
}
|
||||
|
||||
fun addEntry(entry: Entry) {
|
||||
entries.add(entry)
|
||||
fun addEntry(track: Track) {
|
||||
tracks.add(track)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -0,0 +1,65 @@
|
||||
package org.moire.ultrasonic.domain
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import java.io.Serializable
|
||||
import java.util.Date
|
||||
|
||||
@Entity
|
||||
data class Track(
|
||||
@PrimaryKey override var id: String,
|
||||
override var parent: String? = null,
|
||||
override var isDirectory: Boolean = false,
|
||||
override var title: String? = null,
|
||||
override var album: String? = null,
|
||||
var albumId: String? = null,
|
||||
override var artist: String? = null,
|
||||
override var artistId: String? = null,
|
||||
var track: Int? = null,
|
||||
override var year: Int? = null,
|
||||
override var genre: String? = null,
|
||||
var contentType: String? = null,
|
||||
var suffix: String? = null,
|
||||
var transcodedContentType: String? = null,
|
||||
var transcodedSuffix: String? = null,
|
||||
override var coverArt: String? = null,
|
||||
var size: Long? = null,
|
||||
override var songCount: Long? = null,
|
||||
override var duration: Int? = null,
|
||||
var bitRate: Int? = null,
|
||||
override var path: String? = null,
|
||||
override var isVideo: Boolean = false,
|
||||
override var starred: Boolean = false,
|
||||
override var discNumber: Int? = null,
|
||||
var type: String? = null,
|
||||
override var created: Date? = null,
|
||||
override var closeness: Int = 0,
|
||||
var bookmarkPosition: Int = 0,
|
||||
var userRating: Int? = null,
|
||||
var averageRating: Float? = null,
|
||||
override var name: String? = null
|
||||
) : Serializable, MusicDirectory.Child() {
|
||||
fun setDuration(duration: Long) {
|
||||
this.duration = duration.toInt()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val serialVersionUID = -3339106650010798108L
|
||||
}
|
||||
|
||||
fun compareTo(other: Track): Int {
|
||||
when {
|
||||
this.closeness == other.closeness -> {
|
||||
return 0
|
||||
}
|
||||
this.closeness > other.closeness -> {
|
||||
return -1
|
||||
}
|
||||
else -> {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun compareTo(other: Identifiable) = compareTo(other as Track)
|
||||
}
|
@ -1,41 +1,24 @@
|
||||
<?xml version="1.0" ?>
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues></ManuallySuppressedIssues>
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues>
|
||||
<ID>ComplexCondition:DownloadHandler.kt$DownloadHandler.<no name provided>$!append && !playNext && !unpin && !background</ID>
|
||||
<ID>ComplexCondition:FilePickerAdapter.kt$FilePickerAdapter$currentDirectory.absolutePath == "/" || currentDirectory.absolutePath == "/storage" || currentDirectory.absolutePath == "/storage/emulated" || currentDirectory.absolutePath == "/mnt"</ID>
|
||||
<ID>ComplexCondition:DownloadHandler.kt$DownloadHandler.<no name provided>$!append && !playNext && !unpin && !background</ID>
|
||||
<ID>ComplexMethod:DownloadFile.kt$DownloadFile.DownloadTask$override fun execute()</ID>
|
||||
<ID>ComplexMethod:FilePickerAdapter.kt$FilePickerAdapter$private fun fileLister(currentDirectory: File)</ID>
|
||||
<ID>ComplexMethod:SongView.kt$SongView$fun setSong(song: MusicDirectory.Entry, checkable: Boolean, draggable: Boolean)</ID>
|
||||
<ID>ComplexMethod:TrackCollectionFragment.kt$TrackCollectionFragment$private fun enableButtons()</ID>
|
||||
<ID>ComplexMethod:TrackCollectionFragment.kt$TrackCollectionFragment$private fun updateInterfaceWithEntries(musicDirectory: MusicDirectory)</ID>
|
||||
<ID>ImplicitDefaultLocale:DownloadFile.kt$DownloadFile$String.format("DownloadFile (%s)", song)</ID>
|
||||
<ID>ImplicitDefaultLocale:DownloadFile.kt$DownloadFile.DownloadTask$String.format("Download of '%s' was cancelled", song)</ID>
|
||||
<ID>ImplicitDefaultLocale:DownloadFile.kt$DownloadFile.DownloadTask$String.format("DownloadTask (%s)", song)</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>
|
||||
<ID>ImplicitDefaultLocale:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$String.format("BufferTask (%s)", downloadFile)</ID>
|
||||
<ID>ImplicitDefaultLocale:LocalMediaPlayer.kt$LocalMediaPlayer.CheckCompletionTask$String.format("CheckCompletionTask (%s)", downloadFile)</ID>
|
||||
<ID>ImplicitDefaultLocale:ShareHandler.kt$ShareHandler$String.format("%d:%s", timeSpanAmount, timeSpanType)</ID>
|
||||
<ID>ImplicitDefaultLocale:SongView.kt$SongView$String.format("%02d.", trackNumber)</ID>
|
||||
<ID>ImplicitDefaultLocale:SongView.kt$SongView$String.format("%s ", bitRate)</ID>
|
||||
<ID>ImplicitDefaultLocale:SongView.kt$SongView$String.format("%s > %s", suffix, transcodedSuffix)</ID>
|
||||
<ID>LargeClass:TrackCollectionFragment.kt$TrackCollectionFragment : Fragment</ID>
|
||||
<ID>LongMethod:DownloadFile.kt$DownloadFile.DownloadTask$override fun execute()</ID>
|
||||
<ID>LongMethod:EditServerFragment.kt$EditServerFragment$override fun onViewCreated(view: View, savedInstanceState: Bundle?)</ID>
|
||||
<ID>LongMethod:LocalMediaPlayer.kt$LocalMediaPlayer$@Synchronized private fun doPlay(downloadFile: DownloadFile, position: Int, start: Boolean)</ID>
|
||||
<ID>LongMethod:NavigationActivity.kt$NavigationActivity$override fun onCreate(savedInstanceState: Bundle?)</ID>
|
||||
<ID>LongMethod:ShareHandler.kt$ShareHandler$private fun showDialog( fragment: Fragment, shareDetails: ShareDetails, swipe: SwipeRefreshLayout?, cancellationToken: CancellationToken )</ID>
|
||||
<ID>LongMethod:SongView.kt$SongView$fun setSong(song: MusicDirectory.Entry, checkable: Boolean, draggable: Boolean)</ID>
|
||||
<ID>LongMethod:TrackCollectionFragment.kt$TrackCollectionFragment$override fun onContextItemSelected(menuItem: MenuItem): Boolean</ID>
|
||||
<ID>LongMethod:TrackCollectionFragment.kt$TrackCollectionFragment$override fun onViewCreated(view: View, savedInstanceState: Bundle?)</ID>
|
||||
<ID>LongMethod:TrackCollectionFragment.kt$TrackCollectionFragment$private fun updateDisplay(refresh: Boolean)</ID>
|
||||
<ID>LongMethod:TrackCollectionFragment.kt$TrackCollectionFragment$private fun updateInterfaceWithEntries(musicDirectory: MusicDirectory)</ID>
|
||||
<ID>LongParameterList:ServerRowAdapter.kt$ServerRowAdapter$( private var context: Context, private var data: 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, private var data: 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:LocalMediaPlayer.kt$LocalMediaPlayer.<no name provided>$60000</ID>
|
||||
<ID>MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.<no name provided>$60000</ID>
|
||||
<ID>MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$100000</ID>
|
||||
<ID>MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$8</ID>
|
||||
<ID>MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$86400L</ID>
|
||||
@ -44,25 +27,17 @@
|
||||
<ID>MagicNumber:MediaPlayerService.kt$MediaPlayerService$3</ID>
|
||||
<ID>MagicNumber:MediaPlayerService.kt$MediaPlayerService$4</ID>
|
||||
<ID>MagicNumber:RESTMusicService.kt$RESTMusicService$206</ID>
|
||||
<ID>MagicNumber:SongView.kt$SongView$3</ID>
|
||||
<ID>MagicNumber:SongView.kt$SongView$4</ID>
|
||||
<ID>MagicNumber:SongView.kt$SongView$60</ID>
|
||||
<ID>NestedBlockDepth:DownloadFile.kt$DownloadFile.DownloadTask$override fun execute()</ID>
|
||||
<ID>NestedBlockDepth:DownloadHandler.kt$DownloadHandler$private fun downloadRecursively( fragment: Fragment, id: String, name: String?, isShare: Boolean, isDirectory: Boolean, save: Boolean, append: Boolean, autoPlay: Boolean, shuffle: Boolean, background: Boolean, playNext: Boolean, unpin: Boolean, isArtist: Boolean )</ID>
|
||||
<ID>NestedBlockDepth:MediaPlayerService.kt$MediaPlayerService$private fun setupOnSongCompletedHandler()</ID>
|
||||
<ID>ReturnCount:ServerRowAdapter.kt$ServerRowAdapter$ private fun popupMenuItemClick(menuItem: MenuItem, position: Int): Boolean</ID>
|
||||
<ID>ReturnCount:TrackCollectionFragment.kt$TrackCollectionFragment$override fun onContextItemSelected(menuItem: MenuItem): Boolean</ID>
|
||||
<ID>TooGenericExceptionCaught:DownloadFile.kt$DownloadFile$e: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:FileLoggerTree.kt$FileLoggerTree$x: Throwable</ID>
|
||||
<ID>TooGenericExceptionCaught:LocalMediaPlayer.kt$LocalMediaPlayer$ex: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:LocalMediaPlayer.kt$LocalMediaPlayer$exception: Throwable</ID>
|
||||
<ID>TooGenericExceptionCaught:LocalMediaPlayer.kt$LocalMediaPlayer$x: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:LocalMediaPlayer.kt$LocalMediaPlayer.PositionCache$e: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:SongView.kt$SongView$e: Exception</ID>
|
||||
<ID>TooGenericExceptionThrown:DownloadFile.kt$DownloadFile.DownloadTask$throw Exception(String.format("Download of '%s' was cancelled", song))</ID>
|
||||
<ID>TooGenericExceptionThrown:DownloadFile.kt$DownloadFile.DownloadTask$throw RuntimeException( String.format(Locale.ROOT, "Download of '%s' was cancelled", track) )</ID>
|
||||
<ID>TooManyFunctions:MediaPlayerService.kt$MediaPlayerService : Service</ID>
|
||||
<ID>TooManyFunctions:RESTMusicService.kt$RESTMusicService : MusicService</ID>
|
||||
<ID>TooManyFunctions:TrackCollectionFragment.kt$TrackCollectionFragment : Fragment</ID>
|
||||
<ID>UtilityClassWithPublicConstructor:FragmentTitle.kt$FragmentTitle</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
|
@ -14,7 +14,7 @@ import android.widget.RemoteViews;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.activity.NavigationActivity;
|
||||
import org.moire.ultrasonic.domain.MusicDirectory;
|
||||
import org.moire.ultrasonic.domain.Track;
|
||||
import org.moire.ultrasonic.imageloader.BitmapUtils;
|
||||
import org.moire.ultrasonic.receiver.MediaButtonIntentReceiver;
|
||||
import org.moire.ultrasonic.service.MediaPlayerController;
|
||||
@ -73,7 +73,7 @@ public class UltrasonicAppWidgetProvider extends AppWidgetProvider
|
||||
/**
|
||||
* Handle a change notification coming over from {@link MediaPlayerController}
|
||||
*/
|
||||
public void notifyChange(Context context, MusicDirectory.Entry currentSong, boolean playing, boolean setAlbum)
|
||||
public void notifyChange(Context context, Track currentSong, boolean playing, boolean setAlbum)
|
||||
{
|
||||
if (hasInstances(context))
|
||||
{
|
||||
@ -100,7 +100,7 @@ public class UltrasonicAppWidgetProvider extends AppWidgetProvider
|
||||
/**
|
||||
* Update all active widget instances by pushing changes
|
||||
*/
|
||||
private void performUpdate(Context context, MusicDirectory.Entry currentSong, boolean playing, boolean setAlbum)
|
||||
private void performUpdate(Context context, Track currentSong, boolean playing, boolean setAlbum)
|
||||
{
|
||||
final Resources res = context.getResources();
|
||||
final RemoteViews views = new RemoteViews(context.getPackageName(), this.layoutId);
|
||||
|
@ -1,16 +1,16 @@
|
||||
package org.moire.ultrasonic.receiver;
|
||||
|
||||
import static org.koin.java.KoinJavaComponent.inject;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import org.moire.ultrasonic.domain.MusicDirectory.Entry;
|
||||
import org.moire.ultrasonic.domain.Track;
|
||||
import org.moire.ultrasonic.service.MediaPlayerController;
|
||||
|
||||
import kotlin.Lazy;
|
||||
|
||||
import static org.koin.java.KoinJavaComponent.inject;
|
||||
|
||||
public class A2dpIntentReceiver extends BroadcastReceiver
|
||||
{
|
||||
private static final String PLAYSTATUS_RESPONSE = "com.android.music.playstatusresponse";
|
||||
@ -21,7 +21,7 @@ public class A2dpIntentReceiver extends BroadcastReceiver
|
||||
{
|
||||
if (mediaPlayerControllerLazy.getValue().getCurrentPlaying() == null) return;
|
||||
|
||||
Entry song = mediaPlayerControllerLazy.getValue().getCurrentPlaying().getSong();
|
||||
Track song = mediaPlayerControllerLazy.getValue().getCurrentPlaying().getTrack();
|
||||
if (song == null) return;
|
||||
|
||||
Intent avrcpIntent = new Intent(PLAYSTATUS_RESPONSE);
|
||||
|
@ -234,7 +234,7 @@ public class JukeboxMediaPlayer
|
||||
List<String> ids = new ArrayList<>();
|
||||
for (DownloadFile file : downloader.getAll())
|
||||
{
|
||||
ids.add(file.getSong().getId());
|
||||
ids.add(file.getTrack().getId());
|
||||
}
|
||||
|
||||
tasks.add(new SetPlaylist(ids));
|
||||
|
@ -18,7 +18,7 @@ public class Scrobbler
|
||||
{
|
||||
if (song == null || !ActiveServerProvider.Companion.isScrobblingEnabled()) return;
|
||||
|
||||
final String id = song.getSong().getId();
|
||||
final String id = song.getTrack().getId();
|
||||
if (id == null) return;
|
||||
|
||||
// Avoid duplicate registrations.
|
||||
|
@ -1,6 +1,6 @@
|
||||
package org.moire.ultrasonic.service;
|
||||
|
||||
import org.moire.ultrasonic.domain.MusicDirectory;
|
||||
import org.moire.ultrasonic.domain.Track;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
@ -13,7 +13,7 @@ public class State implements Serializable
|
||||
{
|
||||
public static final long serialVersionUID = -6346438781062572270L;
|
||||
|
||||
public List<MusicDirectory.Entry> songs = new ArrayList<>();
|
||||
public List<Track> songs = new ArrayList<>();
|
||||
public int currentPlayingIndex;
|
||||
public int currentPlayingPosition;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package org.moire.ultrasonic.util;
|
||||
|
||||
import org.moire.ultrasonic.domain.MusicDirectory;
|
||||
import org.moire.ultrasonic.domain.Track;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -12,5 +12,5 @@ public class ShareDetails
|
||||
public String Description;
|
||||
public boolean ShareOnServer;
|
||||
public long Expiration;
|
||||
public List<MusicDirectory.Entry> Entries;
|
||||
public List<Track> Entries;
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ package org.moire.ultrasonic.util;
|
||||
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider;
|
||||
import org.moire.ultrasonic.domain.MusicDirectory;
|
||||
import org.moire.ultrasonic.domain.Track;
|
||||
import org.moire.ultrasonic.service.MusicService;
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory;
|
||||
|
||||
@ -40,7 +41,7 @@ public class ShufflePlayBuffer
|
||||
private static final int CAPACITY = 50;
|
||||
private static final int REFILL_THRESHOLD = 40;
|
||||
|
||||
private final List<MusicDirectory.Entry> buffer = new ArrayList<>();
|
||||
private final List<Track> buffer = new ArrayList<>();
|
||||
private ScheduledExecutorService executorService;
|
||||
private int currentServer;
|
||||
|
||||
@ -64,11 +65,11 @@ public class ShufflePlayBuffer
|
||||
Timber.i("ShufflePlayBuffer destroyed");
|
||||
}
|
||||
|
||||
public List<MusicDirectory.Entry> get(int size)
|
||||
public List<Track> get(int size)
|
||||
{
|
||||
clearBufferIfNecessary();
|
||||
|
||||
List<MusicDirectory.Entry> result = new ArrayList<>(size);
|
||||
List<Track> result = new ArrayList<>(size);
|
||||
synchronized (buffer)
|
||||
{
|
||||
while (!buffer.isEmpty() && result.size() < size)
|
||||
|
@ -1,6 +1,6 @@
|
||||
package org.moire.ultrasonic.util;
|
||||
|
||||
import org.moire.ultrasonic.domain.MusicDirectory;
|
||||
import org.moire.ultrasonic.domain.Track;
|
||||
import org.moire.ultrasonic.service.DownloadFile;
|
||||
import org.moire.ultrasonic.service.Supplier;
|
||||
|
||||
@ -180,7 +180,7 @@ public class StreamProxy implements Runnable
|
||||
{
|
||||
Timber.i("Streaming song in background");
|
||||
DownloadFile downloadFile = currentPlaying == null? null : currentPlaying.get();
|
||||
MusicDirectory.Entry song = downloadFile.getSong();
|
||||
Track song = downloadFile.getTrack();
|
||||
long fileSize = downloadFile.getBitRate() * ((song.getDuration() != null) ? song.getDuration() : 0) * 1000 / 8;
|
||||
Timber.i("Streaming fileSize: %d", fileSize);
|
||||
|
||||
|
@ -20,7 +20,7 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import com.drakeet.multitype.ItemViewBinder
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.domain.Album
|
||||
import org.moire.ultrasonic.imageloader.ImageLoader
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
|
||||
import org.moire.ultrasonic.util.Settings.shouldUseId3Tags
|
||||
@ -31,11 +31,11 @@ import timber.log.Timber
|
||||
* Creates a Row in a RecyclerView which contains the details of an Album
|
||||
*/
|
||||
class AlbumRowBinder(
|
||||
val onItemClick: (MusicDirectory.Album) -> Unit,
|
||||
val onContextMenuClick: (MenuItem, MusicDirectory.Album) -> Boolean,
|
||||
val onItemClick: (Album) -> Unit,
|
||||
val onContextMenuClick: (MenuItem, Album) -> Boolean,
|
||||
private val imageLoader: ImageLoader,
|
||||
context: Context
|
||||
) : ItemViewBinder<MusicDirectory.Album, AlbumRowBinder.ViewHolder>(), KoinComponent {
|
||||
) : ItemViewBinder<Album, AlbumRowBinder.ViewHolder>(), KoinComponent {
|
||||
|
||||
private val starDrawable: Drawable =
|
||||
Util.getDrawableFromAttribute(context, R.attr.star_full)
|
||||
@ -46,7 +46,7 @@ class AlbumRowBinder(
|
||||
val layout = R.layout.list_item_album
|
||||
val contextMenuLayout = R.menu.context_menu_artist
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, item: MusicDirectory.Album) {
|
||||
override fun onBindViewHolder(holder: ViewHolder, item: Album) {
|
||||
holder.album.text = item.title
|
||||
holder.artist.text = item.artist
|
||||
holder.details.setOnClickListener { onItemClick(item) }
|
||||
@ -86,7 +86,7 @@ class AlbumRowBinder(
|
||||
/**
|
||||
* Handles the star / unstar action for an album
|
||||
*/
|
||||
private fun onStarClick(entry: MusicDirectory.Album, star: ImageView) {
|
||||
private fun onStarClick(entry: Album, star: ImageView) {
|
||||
entry.starred = !entry.starred
|
||||
star.setImageDrawable(if (entry.starred) starDrawable else starHollowDrawable)
|
||||
val musicService = getMusicService()
|
||||
|
@ -12,7 +12,7 @@ import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.domain.Identifiable
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
import org.moire.ultrasonic.service.DownloadFile
|
||||
import org.moire.ultrasonic.service.Downloader
|
||||
|
||||
@ -45,7 +45,7 @@ class TrackViewBinder(
|
||||
val diffAdapter = adapter as BaseAdapter<*>
|
||||
|
||||
when (item) {
|
||||
is MusicDirectory.Entry -> {
|
||||
is Track -> {
|
||||
downloadFile = downloader.getDownloadFileForSong(item)
|
||||
}
|
||||
is DownloadFile -> {
|
||||
@ -77,7 +77,7 @@ class TrackViewBinder(
|
||||
}
|
||||
} else {
|
||||
// Minimize or maximize the Text view (if song title is very long)
|
||||
if (!downloadFile.song.isDirectory) {
|
||||
if (!downloadFile.track.isDirectory) {
|
||||
holder.maximizeOrMinimize()
|
||||
}
|
||||
}
|
||||
@ -86,7 +86,7 @@ class TrackViewBinder(
|
||||
}
|
||||
|
||||
holder.itemView.setOnClickListener {
|
||||
if (checkable && !downloadFile.song.isVideo) {
|
||||
if (checkable && !downloadFile.track.isVideo) {
|
||||
val nowChecked = !holder.check.isChecked
|
||||
holder.isChecked = nowChecked
|
||||
} else {
|
||||
|
@ -15,7 +15,7 @@ import io.reactivex.rxjava3.disposables.Disposable
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
import org.moire.ultrasonic.service.DownloadFile
|
||||
import org.moire.ultrasonic.service.DownloadStatus
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory
|
||||
@ -44,7 +44,7 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
|
||||
var duration: TextView = view.findViewById(R.id.song_duration)
|
||||
var progress: TextView = view.findViewById(R.id.song_status)
|
||||
|
||||
var entry: MusicDirectory.Entry? = null
|
||||
var entry: Track? = null
|
||||
private set
|
||||
var downloadFile: DownloadFile? = null
|
||||
private set
|
||||
@ -67,7 +67,7 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
|
||||
isSelected: Boolean = false
|
||||
) {
|
||||
val useFiveStarRating = Settings.useFiveStarRating
|
||||
val song = file.song
|
||||
val song = file.track
|
||||
downloadFile = file
|
||||
entry = song
|
||||
|
||||
@ -131,7 +131,7 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupStarButtons(song: MusicDirectory.Entry, useFiveStarRating: Boolean) {
|
||||
private fun setupStarButtons(song: Track, useFiveStarRating: Boolean) {
|
||||
if (useFiveStarRating) {
|
||||
// Hide single star
|
||||
star.isVisible = false
|
||||
|
@ -4,8 +4,9 @@
|
||||
package org.moire.ultrasonic.domain
|
||||
|
||||
import org.moire.ultrasonic.api.subsonic.models.Album
|
||||
typealias DomainAlbum = org.moire.ultrasonic.domain.Album
|
||||
|
||||
fun Album.toDomainEntity(): MusicDirectory.Album = MusicDirectory.Album(
|
||||
fun Album.toDomainEntity(): DomainAlbum = Album(
|
||||
id = this@toDomainEntity.id,
|
||||
title = this@toDomainEntity.name ?: this@toDomainEntity.title,
|
||||
album = this@toDomainEntity.album,
|
||||
@ -24,4 +25,4 @@ fun Album.toMusicDirectoryDomainEntity(): MusicDirectory = MusicDirectory().appl
|
||||
addAll(this@toMusicDirectoryDomainEntity.songList.map { it.toTrackEntity() })
|
||||
}
|
||||
|
||||
fun List<Album>.toDomainEntityList(): List<MusicDirectory.Album> = this.map { it.toDomainEntity() }
|
||||
fun List<Album>.toDomainEntityList(): List<DomainAlbum> = this.map { it.toDomainEntity() }
|
||||
|
@ -24,6 +24,6 @@ fun APIArtist.toMusicDirectoryDomainEntity(): MusicDirectory = MusicDirectory().
|
||||
addAll(this@toMusicDirectoryDomainEntity.albumsList.map { it.toDomainEntity() })
|
||||
}
|
||||
|
||||
fun APIArtist.toDomainEntityList(): List<MusicDirectory.Album> {
|
||||
fun APIArtist.toDomainEntityList(): List<Album> {
|
||||
return this.albumsList.map { it.toDomainEntity() }
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ fun ApiBookmark.toDomainEntity(): Bookmark = Bookmark(
|
||||
comment = this@toDomainEntity.comment,
|
||||
created = this@toDomainEntity.created?.time,
|
||||
changed = this@toDomainEntity.changed?.time,
|
||||
entry = this@toDomainEntity.entry.toTrackEntity()
|
||||
track = this@toDomainEntity.entry.toTrackEntity()
|
||||
)
|
||||
|
||||
fun List<ApiBookmark>.toDomainEntitiesList(): List<Bookmark> = map { it.toDomainEntity() }
|
||||
|
@ -26,12 +26,12 @@ internal val dateFormat: DateFormat by lazy {
|
||||
SimpleDateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.getDefault())
|
||||
}
|
||||
|
||||
fun MusicDirectoryChild.toTrackEntity(): MusicDirectory.Entry = MusicDirectory.Entry(id).apply {
|
||||
fun MusicDirectoryChild.toTrackEntity(): Track = Track(id).apply {
|
||||
populateCommonProps(this, this@toTrackEntity)
|
||||
populateTrackProps(this, this@toTrackEntity)
|
||||
}
|
||||
|
||||
fun MusicDirectoryChild.toAlbumEntity(): MusicDirectory.Album = MusicDirectory.Album(id).apply {
|
||||
fun MusicDirectoryChild.toAlbumEntity(): Album = Album(id).apply {
|
||||
populateCommonProps(this, this@toAlbumEntity)
|
||||
}
|
||||
|
||||
@ -64,20 +64,20 @@ private fun populateCommonProps(
|
||||
}
|
||||
|
||||
private fun populateTrackProps(
|
||||
entry: MusicDirectory.Entry,
|
||||
track: Track,
|
||||
source: MusicDirectoryChild
|
||||
) {
|
||||
entry.size = source.size
|
||||
entry.contentType = source.contentType
|
||||
entry.suffix = source.suffix
|
||||
entry.transcodedContentType = source.transcodedContentType
|
||||
entry.transcodedSuffix = source.transcodedSuffix
|
||||
entry.track = source.track
|
||||
entry.albumId = source.albumId
|
||||
entry.bitRate = source.bitRate
|
||||
entry.type = source.type
|
||||
entry.userRating = source.userRating
|
||||
entry.averageRating = source.averageRating
|
||||
track.size = source.size
|
||||
track.contentType = source.contentType
|
||||
track.suffix = source.suffix
|
||||
track.transcodedContentType = source.transcodedContentType
|
||||
track.transcodedSuffix = source.transcodedSuffix
|
||||
track.track = source.track
|
||||
track.albumId = source.albumId
|
||||
track.bitRate = source.bitRate
|
||||
track.type = source.type
|
||||
track.userRating = source.userRating
|
||||
track.averageRating = source.averageRating
|
||||
}
|
||||
|
||||
fun List<MusicDirectoryChild>.toDomainEntityList(): List<MusicDirectory.Child> {
|
||||
@ -93,7 +93,7 @@ fun List<MusicDirectoryChild>.toDomainEntityList(): List<MusicDirectory.Child> {
|
||||
return newList
|
||||
}
|
||||
|
||||
fun List<MusicDirectoryChild>.toTrackList(): List<MusicDirectory.Entry> = this.map {
|
||||
fun List<MusicDirectoryChild>.toTrackList(): List<Track> = this.map {
|
||||
it.toTrackEntity()
|
||||
}
|
||||
|
||||
|
@ -22,5 +22,5 @@ fun APIShare.toDomainEntity(): Share = Share(
|
||||
url = this@toDomainEntity.url,
|
||||
username = this@toDomainEntity.username,
|
||||
visitCount = this@toDomainEntity.visitCount.toLong(),
|
||||
entries = this@toDomainEntity.items.toTrackList().toMutableList()
|
||||
tracks = this@toDomainEntity.items.toTrackList().toMutableList()
|
||||
)
|
||||
|
@ -15,14 +15,14 @@ import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.adapters.AlbumRowBinder
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.domain.Album
|
||||
import org.moire.ultrasonic.model.AlbumListModel
|
||||
import org.moire.ultrasonic.util.Constants
|
||||
|
||||
/**
|
||||
* Displays a list of Albums from the media library
|
||||
*/
|
||||
class AlbumListFragment : EntryListFragment<MusicDirectory.Album>() {
|
||||
class AlbumListFragment : EntryListFragment<Album>() {
|
||||
|
||||
/**
|
||||
* The ViewModel to use to get the data
|
||||
@ -45,7 +45,7 @@ class AlbumListFragment : EntryListFragment<MusicDirectory.Album>() {
|
||||
override fun getLiveData(
|
||||
args: Bundle?,
|
||||
refresh: Boolean
|
||||
): LiveData<List<MusicDirectory.Album>> {
|
||||
): LiveData<List<Album>> {
|
||||
if (args == null) throw IllegalArgumentException("Required arguments are missing")
|
||||
|
||||
val refresh2 = args.getBoolean(Constants.INTENT_REFRESH) || refresh
|
||||
@ -83,7 +83,7 @@ class AlbumListFragment : EntryListFragment<MusicDirectory.Album>() {
|
||||
emptyTextView.setText(R.string.select_album_empty)
|
||||
}
|
||||
|
||||
override fun onItemClick(item: MusicDirectory.Album) {
|
||||
override fun onItemClick(item: Album) {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(Constants.INTENT_ID, item.id)
|
||||
bundle.putBoolean(Constants.INTENT_IS_ALBUM, item.isDirectory)
|
||||
|
@ -15,6 +15,7 @@ import kotlinx.coroutines.launch
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.adapters.BaseAdapter
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
|
||||
|
||||
/**
|
||||
@ -61,7 +62,7 @@ class BookmarksFragment : TrackCollectionFragment() {
|
||||
/**
|
||||
* Custom playback function which uses the restore functionality. A bit of a hack..
|
||||
*/
|
||||
private fun playNow(songs: List<MusicDirectory.Entry>) {
|
||||
private fun playNow(songs: List<Track>) {
|
||||
if (songs.isNotEmpty()) {
|
||||
|
||||
val position = songs[0].bookmarkPosition
|
||||
|
@ -105,7 +105,7 @@ class NowPlayingFragment : Fragment() {
|
||||
val file = mediaPlayerController.currentPlaying
|
||||
|
||||
if (file != null) {
|
||||
val song = file.song
|
||||
val song = file.track
|
||||
val title = song.title
|
||||
val artist = song.artist
|
||||
|
||||
|
@ -66,9 +66,9 @@ import org.moire.ultrasonic.audiofx.EqualizerController
|
||||
import org.moire.ultrasonic.audiofx.VisualizerController
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
|
||||
import org.moire.ultrasonic.domain.Identifiable
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.domain.PlayerState
|
||||
import org.moire.ultrasonic.domain.RepeatMode
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
|
||||
import org.moire.ultrasonic.service.DownloadFile
|
||||
import org.moire.ultrasonic.service.LocalMediaPlayer
|
||||
@ -118,7 +118,7 @@ class PlayerFragment :
|
||||
private val imageLoaderProvider: ImageLoaderProvider by inject()
|
||||
private lateinit var executorService: ScheduledExecutorService
|
||||
private var currentPlaying: DownloadFile? = null
|
||||
private var currentSong: MusicDirectory.Entry? = null
|
||||
private var currentSong: Track? = null
|
||||
private lateinit var viewManager: LinearLayoutManager
|
||||
private var rxBusSubscription: Disposable? = null
|
||||
private var ioScope = CoroutineScope(Dispatchers.IO)
|
||||
@ -507,7 +507,7 @@ class PlayerFragment :
|
||||
val downloadFile = mediaPlayerController.currentPlaying
|
||||
|
||||
if (downloadFile != null) {
|
||||
currentSong = downloadFile.song
|
||||
currentSong = downloadFile.track
|
||||
}
|
||||
|
||||
if (useFiveStarRating) starMenuItem.isVisible = false
|
||||
@ -544,9 +544,9 @@ class PlayerFragment :
|
||||
val downloadFile = viewAdapter.getCurrentList()[info!!.position] as DownloadFile
|
||||
val menuInflater = requireActivity().menuInflater
|
||||
menuInflater.inflate(R.menu.nowplaying_context, menu)
|
||||
val song: MusicDirectory.Entry?
|
||||
val song: Track?
|
||||
|
||||
song = downloadFile.song
|
||||
song = downloadFile.track
|
||||
|
||||
if (song.parent == null) {
|
||||
val menuItem = menu.findItem(R.id.menu_show_album)
|
||||
@ -571,21 +571,21 @@ class PlayerFragment :
|
||||
|
||||
@Suppress("ComplexMethod", "LongMethod", "ReturnCount")
|
||||
private fun menuItemSelected(menuItemId: Int, song: DownloadFile?): Boolean {
|
||||
var entry: MusicDirectory.Entry? = null
|
||||
var track: Track? = null
|
||||
val bundle: Bundle
|
||||
if (song != null) {
|
||||
entry = song.song
|
||||
track = song.track
|
||||
}
|
||||
|
||||
when (menuItemId) {
|
||||
R.id.menu_show_artist -> {
|
||||
if (entry == null) return false
|
||||
if (track == null) return false
|
||||
|
||||
if (Settings.shouldUseId3Tags) {
|
||||
bundle = Bundle()
|
||||
bundle.putString(Constants.INTENT_ID, entry.artistId)
|
||||
bundle.putString(Constants.INTENT_NAME, entry.artist)
|
||||
bundle.putString(Constants.INTENT_PARENT_ID, entry.artistId)
|
||||
bundle.putString(Constants.INTENT_ID, track.artistId)
|
||||
bundle.putString(Constants.INTENT_NAME, track.artist)
|
||||
bundle.putString(Constants.INTENT_PARENT_ID, track.artistId)
|
||||
bundle.putBoolean(Constants.INTENT_ARTIST, true)
|
||||
Navigation.findNavController(requireView())
|
||||
.navigate(R.id.playerToSelectAlbum, bundle)
|
||||
@ -593,24 +593,24 @@ class PlayerFragment :
|
||||
return true
|
||||
}
|
||||
R.id.menu_show_album -> {
|
||||
if (entry == null) return false
|
||||
if (track == null) return false
|
||||
|
||||
val albumId = if (Settings.shouldUseId3Tags) entry.albumId else entry.parent
|
||||
val albumId = if (Settings.shouldUseId3Tags) track.albumId else track.parent
|
||||
bundle = Bundle()
|
||||
bundle.putString(Constants.INTENT_ID, albumId)
|
||||
bundle.putString(Constants.INTENT_NAME, entry.album)
|
||||
bundle.putString(Constants.INTENT_PARENT_ID, entry.parent)
|
||||
bundle.putString(Constants.INTENT_NAME, track.album)
|
||||
bundle.putString(Constants.INTENT_PARENT_ID, track.parent)
|
||||
bundle.putBoolean(Constants.INTENT_IS_ALBUM, true)
|
||||
Navigation.findNavController(requireView())
|
||||
.navigate(R.id.playerToSelectAlbum, bundle)
|
||||
return true
|
||||
}
|
||||
R.id.menu_lyrics -> {
|
||||
if (entry == null) return false
|
||||
if (track == null) return false
|
||||
|
||||
bundle = Bundle()
|
||||
bundle.putString(Constants.INTENT_ARTIST, entry.artist)
|
||||
bundle.putString(Constants.INTENT_TITLE, entry.title)
|
||||
bundle.putString(Constants.INTENT_ARTIST, track.artist)
|
||||
bundle.putString(Constants.INTENT_TITLE, track.title)
|
||||
Navigation.findNavController(requireView()).navigate(R.id.playerToLyrics, bundle)
|
||||
return true
|
||||
}
|
||||
@ -746,22 +746,22 @@ class PlayerFragment :
|
||||
}
|
||||
R.id.menu_item_share -> {
|
||||
val mediaPlayerController = mediaPlayerController
|
||||
val entries: MutableList<MusicDirectory.Entry?> = ArrayList()
|
||||
val tracks: MutableList<Track?> = ArrayList()
|
||||
val downloadServiceSongs = mediaPlayerController.playList
|
||||
for (downloadFile in downloadServiceSongs) {
|
||||
val playlistEntry = downloadFile.song
|
||||
entries.add(playlistEntry)
|
||||
val playlistEntry = downloadFile.track
|
||||
tracks.add(playlistEntry)
|
||||
}
|
||||
shareHandler.createShare(this, entries, null, cancellationToken)
|
||||
shareHandler.createShare(this, tracks, null, cancellationToken)
|
||||
return true
|
||||
}
|
||||
R.id.menu_item_share_song -> {
|
||||
if (currentSong == null) return true
|
||||
|
||||
val entries: MutableList<MusicDirectory.Entry?> = ArrayList()
|
||||
entries.add(currentSong)
|
||||
val tracks: MutableList<Track?> = ArrayList()
|
||||
tracks.add(currentSong)
|
||||
|
||||
shareHandler.createShare(this, entries, null, cancellationToken)
|
||||
shareHandler.createShare(this, tracks, null, cancellationToken)
|
||||
return true
|
||||
}
|
||||
else -> return false
|
||||
@ -785,7 +785,7 @@ class PlayerFragment :
|
||||
ioScope.launch {
|
||||
|
||||
val entries = mediaPlayerController.playList.map {
|
||||
it.song
|
||||
it.track
|
||||
}
|
||||
val musicService = getMusicService()
|
||||
musicService.createPlaylist(null, playlistName, entries)
|
||||
@ -903,7 +903,7 @@ class PlayerFragment :
|
||||
|
||||
val songRemoved = String.format(
|
||||
resources.getString(R.string.download_song_removed),
|
||||
file.song.title
|
||||
file.track.title
|
||||
)
|
||||
Util.toast(context, songRemoved)
|
||||
|
||||
@ -980,7 +980,7 @@ class PlayerFragment :
|
||||
val trackFormat =
|
||||
String.format(Locale.getDefault(), "%d / %d", currentSongIndex, totalSongs)
|
||||
if (currentPlaying != null) {
|
||||
currentSong = currentPlaying!!.song
|
||||
currentSong = currentPlaying!!.track
|
||||
songTitleTextView.text = currentSong!!.title
|
||||
artistTextView.text = currentSong!!.artist
|
||||
albumTextView.text = currentSong!!.album
|
||||
|
@ -24,12 +24,13 @@ import org.moire.ultrasonic.adapters.DividerBinder
|
||||
import org.moire.ultrasonic.adapters.MoreButtonBinder
|
||||
import org.moire.ultrasonic.adapters.MoreButtonBinder.MoreButton
|
||||
import org.moire.ultrasonic.adapters.TrackViewBinder
|
||||
import org.moire.ultrasonic.domain.Album
|
||||
import org.moire.ultrasonic.domain.Artist
|
||||
import org.moire.ultrasonic.domain.ArtistOrIndex
|
||||
import org.moire.ultrasonic.domain.Identifiable
|
||||
import org.moire.ultrasonic.domain.Index
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.domain.SearchResult
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
|
||||
import org.moire.ultrasonic.model.SearchListModel
|
||||
import org.moire.ultrasonic.service.DownloadFile
|
||||
@ -199,7 +200,7 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
private fun downloadBackground(save: Boolean, songs: List<MusicDirectory.Entry?>) {
|
||||
private fun downloadBackground(save: Boolean, songs: List<Track?>) {
|
||||
val onValid = Runnable {
|
||||
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
|
||||
mediaPlayerController.downloadBackground(songs, save)
|
||||
@ -287,7 +288,7 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||
}
|
||||
}
|
||||
|
||||
private fun onAlbumSelected(album: MusicDirectory.Album, autoplay: Boolean) {
|
||||
private fun onAlbumSelected(album: Album, autoplay: Boolean) {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(Constants.INTENT_ID, album.id)
|
||||
bundle.putString(Constants.INTENT_NAME, album.title)
|
||||
@ -296,7 +297,7 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||
Navigation.findNavController(requireView()).navigate(R.id.searchToTrackCollection, bundle)
|
||||
}
|
||||
|
||||
private fun onSongSelected(song: MusicDirectory.Entry, append: Boolean) {
|
||||
private fun onSongSelected(song: Track, append: Boolean) {
|
||||
if (!append) {
|
||||
mediaPlayerController.clear()
|
||||
}
|
||||
@ -312,8 +313,8 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||
toast(context, resources.getQuantityString(R.plurals.select_album_n_songs_added, 1, 1))
|
||||
}
|
||||
|
||||
private fun onVideoSelected(entry: MusicDirectory.Entry) {
|
||||
playVideo(requireContext(), entry)
|
||||
private fun onVideoSelected(track: Track) {
|
||||
playVideo(requireContext(), track)
|
||||
}
|
||||
|
||||
private fun autoplay() {
|
||||
@ -329,14 +330,14 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||
is ArtistOrIndex -> {
|
||||
onArtistSelected(item)
|
||||
}
|
||||
is MusicDirectory.Entry -> {
|
||||
is Track -> {
|
||||
if (item.isVideo) {
|
||||
onVideoSelected(item)
|
||||
} else {
|
||||
onSongSelected(item, true)
|
||||
}
|
||||
}
|
||||
is MusicDirectory.Album -> {
|
||||
is Album -> {
|
||||
onAlbumSelected(item, false)
|
||||
}
|
||||
}
|
||||
@ -356,11 +357,11 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||
|
||||
if (found || item !is DownloadFile) return true
|
||||
|
||||
val songs = mutableListOf<MusicDirectory.Entry>()
|
||||
val songs = mutableListOf<Track>()
|
||||
|
||||
when (menuItem.itemId) {
|
||||
R.id.song_menu_play_now -> {
|
||||
songs.add(item.song)
|
||||
songs.add(item.track)
|
||||
downloadHandler.download(
|
||||
fragment = this,
|
||||
append = false,
|
||||
@ -372,7 +373,7 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||
)
|
||||
}
|
||||
R.id.song_menu_play_next -> {
|
||||
songs.add(item.song)
|
||||
songs.add(item.track)
|
||||
downloadHandler.download(
|
||||
fragment = this,
|
||||
append = true,
|
||||
@ -384,7 +385,7 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||
)
|
||||
}
|
||||
R.id.song_menu_play_last -> {
|
||||
songs.add(item.song)
|
||||
songs.add(item.track)
|
||||
downloadHandler.download(
|
||||
fragment = this,
|
||||
append = true,
|
||||
@ -396,7 +397,7 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||
)
|
||||
}
|
||||
R.id.song_menu_pin -> {
|
||||
songs.add(item.song)
|
||||
songs.add(item.track)
|
||||
toast(
|
||||
context,
|
||||
resources.getQuantityString(
|
||||
@ -408,7 +409,7 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||
downloadBackground(true, songs)
|
||||
}
|
||||
R.id.song_menu_download -> {
|
||||
songs.add(item.song)
|
||||
songs.add(item.track)
|
||||
toast(
|
||||
context,
|
||||
resources.getQuantityString(
|
||||
@ -420,7 +421,7 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||
downloadBackground(false, songs)
|
||||
}
|
||||
R.id.song_menu_unpin -> {
|
||||
songs.add(item.song)
|
||||
songs.add(item.track)
|
||||
toast(
|
||||
context,
|
||||
resources.getQuantityString(
|
||||
@ -432,7 +433,7 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||
mediaPlayerController.unpin(songs)
|
||||
}
|
||||
R.id.song_menu_share -> {
|
||||
songs.add(item.song)
|
||||
songs.add(item.track)
|
||||
shareHandler.createShare(this, songs, searchRefresh, cancellationToken!!)
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import org.moire.ultrasonic.adapters.TrackViewBinder
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
|
||||
import org.moire.ultrasonic.domain.Identifiable
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
|
||||
import org.moire.ultrasonic.model.TrackCollectionModel
|
||||
import org.moire.ultrasonic.service.MediaPlayerController
|
||||
@ -46,7 +47,6 @@ import org.moire.ultrasonic.util.Constants
|
||||
import org.moire.ultrasonic.util.EntryByDiscAndTrackComparator
|
||||
import org.moire.ultrasonic.util.Settings
|
||||
import org.moire.ultrasonic.util.Util
|
||||
import org.moire.ultrasonic.util.Util.toast
|
||||
|
||||
/**
|
||||
* Displays a group of tracks, eg. the songs of an album, of a playlist etc.
|
||||
@ -122,8 +122,8 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
|
||||
viewAdapter.register(
|
||||
TrackViewBinder(
|
||||
onItemClick = { onItemClick(it.song) },
|
||||
onContextMenuClick = { menu, id -> onContextMenuItemSelected(menu, id.song) },
|
||||
onItemClick = { onItemClick(it.track) },
|
||||
onContextMenuClick = { menu, id -> onContextMenuItemSelected(menu, id.track) },
|
||||
checkable = true,
|
||||
draggable = false,
|
||||
context = requireContext(),
|
||||
@ -249,7 +249,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
|
||||
private fun playNow(
|
||||
append: Boolean,
|
||||
selectedSongs: List<MusicDirectory.Entry> = getSelectedSongs()
|
||||
selectedSongs: List<Track> = getSelectedSongs()
|
||||
) {
|
||||
if (selectedSongs.isNotEmpty()) {
|
||||
downloadHandler.download(
|
||||
@ -314,10 +314,10 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun getAllSongs(): List<MusicDirectory.Entry> {
|
||||
private fun getAllSongs(): List<Track> {
|
||||
return viewAdapter.getCurrentList().filter {
|
||||
it is MusicDirectory.Entry && !it.isDirectory
|
||||
} as List<MusicDirectory.Entry>
|
||||
it is Track && !it.isDirectory
|
||||
} as List<Track>
|
||||
}
|
||||
|
||||
internal fun selectAllOrNone() {
|
||||
@ -338,7 +338,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
}
|
||||
}
|
||||
|
||||
internal open fun enableButtons(selection: List<MusicDirectory.Entry> = getSelectedSongs()) {
|
||||
internal open fun enableButtons(selection: List<Track> = getSelectedSongs()) {
|
||||
val enabled = selection.isNotEmpty()
|
||||
var unpinEnabled = false
|
||||
var deleteEnabled = false
|
||||
@ -378,7 +378,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
|
||||
private fun downloadBackground(
|
||||
save: Boolean,
|
||||
songs: List<MusicDirectory.Entry?>
|
||||
songs: List<Track?>
|
||||
) {
|
||||
val onValid = Runnable {
|
||||
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
|
||||
@ -403,7 +403,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
onValid.run()
|
||||
}
|
||||
|
||||
internal fun delete(songs: List<MusicDirectory.Entry> = getSelectedSongs()) {
|
||||
internal fun delete(songs: List<Track> = getSelectedSongs()) {
|
||||
Util.toast(
|
||||
context,
|
||||
resources.getQuantityString(
|
||||
@ -414,7 +414,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
mediaPlayerController.delete(songs)
|
||||
}
|
||||
|
||||
internal fun unpin(songs: List<MusicDirectory.Entry> = getSelectedSongs()) {
|
||||
internal fun unpin(songs: List<Track> = getSelectedSongs()) {
|
||||
Util.toast(
|
||||
context,
|
||||
resources.getQuantityString(
|
||||
@ -533,10 +533,10 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
}
|
||||
}
|
||||
|
||||
internal fun getSelectedSongs(): List<MusicDirectory.Entry> {
|
||||
internal fun getSelectedSongs(): List<Track> {
|
||||
// Walk through selected set and get the Entries based on the saved ids.
|
||||
return viewAdapter.getCurrentList().mapNotNull {
|
||||
if (it is MusicDirectory.Entry && viewAdapter.isSelected(it.longId))
|
||||
if (it is Track && viewAdapter.isSelected(it.longId))
|
||||
it
|
||||
else
|
||||
null
|
||||
@ -655,7 +655,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
playAll()
|
||||
}
|
||||
R.id.song_menu_share -> {
|
||||
if (item is MusicDirectory.Entry) {
|
||||
if (item is Track) {
|
||||
shareHandler.createShare(
|
||||
this, listOf(item), refreshListView,
|
||||
cancellationToken!!
|
||||
@ -669,10 +669,10 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
return true
|
||||
}
|
||||
|
||||
internal fun getClickedSong(item: MusicDirectory.Child): List<MusicDirectory.Entry> {
|
||||
internal fun getClickedSong(item: MusicDirectory.Child): List<Track> {
|
||||
// This can probably be done better
|
||||
return viewAdapter.getCurrentList().mapNotNull {
|
||||
if (it is MusicDirectory.Entry && (it.id == item.id))
|
||||
if (it is Track && (it.id == item.id))
|
||||
it
|
||||
else
|
||||
null
|
||||
@ -692,7 +692,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
bundle
|
||||
)
|
||||
}
|
||||
item is MusicDirectory.Entry && item.isVideo -> {
|
||||
item is Track && item.isVideo -> {
|
||||
VideoPlayer.playVideo(requireContext(), item)
|
||||
}
|
||||
else -> {
|
||||
|
@ -4,7 +4,7 @@ import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.os.Build
|
||||
import java.io.File
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
import org.moire.ultrasonic.util.FileUtil
|
||||
import org.moire.ultrasonic.util.Util
|
||||
import timber.log.Timber
|
||||
@ -26,11 +26,11 @@ class BitmapUtils {
|
||||
}
|
||||
|
||||
fun getAlbumArtBitmapFromDisk(
|
||||
entry: MusicDirectory.Entry?,
|
||||
track: Track?,
|
||||
size: Int
|
||||
): Bitmap? {
|
||||
if (entry == null) return null
|
||||
val albumArtFile = FileUtil.getAlbumArtFile(entry)
|
||||
if (track == null) return null
|
||||
val albumArtFile = FileUtil.getAlbumArtFile(track)
|
||||
val bitmap: Bitmap? = null
|
||||
if (albumArtFile != null && File(albumArtFile).exists()) {
|
||||
return getBitmapFromDisk(albumArtFile, size, bitmap)
|
||||
|
@ -20,6 +20,7 @@ import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
|
||||
import org.moire.ultrasonic.api.subsonic.throwOnFailure
|
||||
import org.moire.ultrasonic.api.subsonic.toStreamResponse
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
import org.moire.ultrasonic.util.FileUtil
|
||||
import org.moire.ultrasonic.util.Util.safeClose
|
||||
import timber.log.Timber
|
||||
@ -148,28 +149,28 @@ class ImageLoader(
|
||||
* Download a cover art file and cache it on disk
|
||||
*/
|
||||
fun cacheCoverArt(
|
||||
entry: MusicDirectory.Entry
|
||||
track: Track
|
||||
) {
|
||||
|
||||
// Synchronize on the entry so that we don't download concurrently for
|
||||
// the same song.
|
||||
synchronized(entry) {
|
||||
synchronized(track) {
|
||||
// Always download the large size..
|
||||
val size = config.largeSize
|
||||
|
||||
// Check cache to avoid downloading existing files
|
||||
val file = FileUtil.getAlbumArtFile(entry)
|
||||
val file = FileUtil.getAlbumArtFile(track)
|
||||
|
||||
// Return if have a cache hit
|
||||
if (file != null && File(file).exists()) return
|
||||
File(file!!).createNewFile()
|
||||
|
||||
// Can't load empty string ids
|
||||
val id = entry.coverArt
|
||||
val id = track.coverArt
|
||||
if (TextUtils.isEmpty(id)) return
|
||||
|
||||
// Query the API
|
||||
Timber.d("Loading cover art for: %s", entry)
|
||||
Timber.d("Loading cover art for: %s", track)
|
||||
val response = API.getCoverArt(id!!, size.toLong()).execute().toStreamResponse()
|
||||
response.throwOnFailure()
|
||||
|
||||
|
@ -6,14 +6,14 @@ import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import org.moire.ultrasonic.api.subsonic.models.AlbumListType
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.domain.Album
|
||||
import org.moire.ultrasonic.service.MusicService
|
||||
import org.moire.ultrasonic.util.Constants
|
||||
import org.moire.ultrasonic.util.Settings
|
||||
|
||||
class AlbumListModel(application: Application) : GenericListModel(application) {
|
||||
|
||||
val list: MutableLiveData<List<MusicDirectory.Album>> = MutableLiveData()
|
||||
val list: MutableLiveData<List<Album>> = MutableLiveData()
|
||||
var lastType: String? = null
|
||||
private var loadedUntil: Int = 0
|
||||
|
||||
@ -21,7 +21,7 @@ class AlbumListModel(application: Application) : GenericListModel(application) {
|
||||
refresh: Boolean,
|
||||
swipe: SwipeRefreshLayout,
|
||||
args: Bundle
|
||||
): LiveData<List<MusicDirectory.Album>> {
|
||||
): LiveData<List<Album>> {
|
||||
// Don't reload the data if navigating back to the view that was active before.
|
||||
// This way, we keep the scroll position
|
||||
val albumListType = args.getString(Constants.INTENT_ALBUM_LIST_TYPE)!!
|
||||
@ -56,7 +56,7 @@ class AlbumListModel(application: Application) : GenericListModel(application) {
|
||||
var offset = args.getInt(Constants.INTENT_ALBUM_LIST_OFFSET, 0)
|
||||
val append = args.getBoolean(Constants.INTENT_APPEND, false)
|
||||
|
||||
val musicDirectory: List<MusicDirectory.Album>
|
||||
val musicDirectory: List<Album>
|
||||
val musicFolderId = if (showSelectFolderHeader(args)) {
|
||||
activeServerProvider.getActiveServer().musicFolderId
|
||||
} else {
|
||||
@ -98,7 +98,7 @@ class AlbumListModel(application: Application) : GenericListModel(application) {
|
||||
currentListIsSortable = isCollectionSortable(albumListType)
|
||||
|
||||
if (append && list.value != null) {
|
||||
val newList = ArrayList<MusicDirectory.Album>()
|
||||
val newList = ArrayList<Album>()
|
||||
newList.addAll(list.value!!)
|
||||
newList.addAll(musicDirectory)
|
||||
list.postValue(newList)
|
||||
|
@ -25,6 +25,7 @@ import org.moire.ultrasonic.data.ActiveServerProvider
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.domain.SearchCriteria
|
||||
import org.moire.ultrasonic.domain.SearchResult
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
import org.moire.ultrasonic.util.MediaSessionHandler
|
||||
import org.moire.ultrasonic.util.Settings
|
||||
import org.moire.ultrasonic.util.Util
|
||||
@ -80,10 +81,10 @@ class AutoMediaBrowserService : MediaBrowserServiceCompat() {
|
||||
private val serviceJob = Job()
|
||||
private val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob)
|
||||
|
||||
private var playlistCache: List<MusicDirectory.Entry>? = null
|
||||
private var starredSongsCache: List<MusicDirectory.Entry>? = null
|
||||
private var randomSongsCache: List<MusicDirectory.Entry>? = null
|
||||
private var searchSongsCache: List<MusicDirectory.Entry>? = null
|
||||
private var playlistCache: List<Track>? = null
|
||||
private var starredSongsCache: List<Track>? = null
|
||||
private var randomSongsCache: List<Track>? = null
|
||||
private var searchSongsCache: List<Track>? = null
|
||||
|
||||
private val isOffline get() = ActiveServerProvider.isOffline()
|
||||
private val useId3Tags get() = Settings.shouldUseId3Tags
|
||||
@ -1070,7 +1071,7 @@ class AutoMediaBrowserService : MediaBrowserServiceCompat() {
|
||||
return section.toString()
|
||||
}
|
||||
|
||||
private fun playSongs(songs: List<MusicDirectory.Entry?>?) {
|
||||
private fun playSongs(songs: List<Track?>?) {
|
||||
mediaPlayerController.addToPlaylist(
|
||||
songs,
|
||||
save = false,
|
||||
@ -1081,7 +1082,7 @@ class AutoMediaBrowserService : MediaBrowserServiceCompat() {
|
||||
)
|
||||
}
|
||||
|
||||
private fun playSong(song: MusicDirectory.Entry) {
|
||||
private fun playSong(song: Track) {
|
||||
mediaPlayerController.addToPlaylist(
|
||||
listOf(song),
|
||||
save = false,
|
||||
|
@ -13,6 +13,7 @@ import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||
import org.moire.ultrasonic.data.MetaDatabase
|
||||
import org.moire.ultrasonic.domain.Album
|
||||
import org.moire.ultrasonic.domain.Artist
|
||||
import org.moire.ultrasonic.domain.Bookmark
|
||||
import org.moire.ultrasonic.domain.ChatMessage
|
||||
@ -27,6 +28,7 @@ import org.moire.ultrasonic.domain.PodcastsChannel
|
||||
import org.moire.ultrasonic.domain.SearchCriteria
|
||||
import org.moire.ultrasonic.domain.SearchResult
|
||||
import org.moire.ultrasonic.domain.Share
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
import org.moire.ultrasonic.domain.UserInfo
|
||||
import org.moire.ultrasonic.util.Constants
|
||||
import org.moire.ultrasonic.util.LRUCache
|
||||
@ -41,7 +43,7 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
|
||||
|
||||
// Old style TimeLimitedCache
|
||||
private val cachedMusicDirectories: LRUCache<String, TimeLimitedCache<MusicDirectory?>>
|
||||
private val cachedArtist: LRUCache<String, TimeLimitedCache<List<MusicDirectory.Album>>>
|
||||
private val cachedArtist: LRUCache<String, TimeLimitedCache<List<Album>>>
|
||||
private val cachedAlbum: LRUCache<String, TimeLimitedCache<MusicDirectory?>>
|
||||
private val cachedUserInfo: LRUCache<String, TimeLimitedCache<UserInfo?>>
|
||||
private val cachedLicenseValid = TimeLimitedCache<Boolean>(120, TimeUnit.SECONDS)
|
||||
@ -149,7 +151,7 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
|
||||
|
||||
@Throws(Exception::class)
|
||||
override fun getArtist(id: String, name: String?, refresh: Boolean):
|
||||
List<MusicDirectory.Album> {
|
||||
List<Album> {
|
||||
checkSettingsChanged()
|
||||
var cache = if (refresh) null else cachedArtist[id]
|
||||
var dir = cache?.get()
|
||||
@ -218,9 +220,9 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
override fun createPlaylist(id: String?, name: String?, entries: List<MusicDirectory.Entry>) {
|
||||
override fun createPlaylist(id: String?, name: String?, tracks: List<Track>) {
|
||||
cachedPlaylists.clear()
|
||||
musicService.createPlaylist(id, name, entries)
|
||||
musicService.createPlaylist(id, name, tracks)
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
@ -249,7 +251,7 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
|
||||
size: Int,
|
||||
offset: Int,
|
||||
musicFolderId: String?
|
||||
): List<MusicDirectory.Album> {
|
||||
): List<Album> {
|
||||
return musicService.getAlbumList(type, size, offset, musicFolderId)
|
||||
}
|
||||
|
||||
@ -259,7 +261,7 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
|
||||
size: Int,
|
||||
offset: Int,
|
||||
musicFolderId: String?
|
||||
): List<MusicDirectory.Album> {
|
||||
): List<Album> {
|
||||
return musicService.getAlbumList2(type, size, offset, musicFolderId)
|
||||
}
|
||||
|
||||
@ -276,7 +278,7 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
|
||||
|
||||
@Throws(Exception::class)
|
||||
override fun getDownloadInputStream(
|
||||
song: MusicDirectory.Entry,
|
||||
song: Track,
|
||||
offset: Long,
|
||||
maxBitrate: Int,
|
||||
save: Boolean
|
||||
|
@ -12,12 +12,13 @@ import androidx.lifecycle.MutableLiveData
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.util.Locale
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||
import org.moire.ultrasonic.domain.Artist
|
||||
import org.moire.ultrasonic.domain.Identifiable
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
|
||||
import org.moire.ultrasonic.subsonic.ImageLoaderProvider
|
||||
import org.moire.ultrasonic.util.CacheCleaner
|
||||
@ -38,12 +39,12 @@ import timber.log.Timber
|
||||
*
|
||||
*/
|
||||
class DownloadFile(
|
||||
val song: MusicDirectory.Entry,
|
||||
val track: Track,
|
||||
save: Boolean
|
||||
) : KoinComponent, Identifiable {
|
||||
val partialFile: String
|
||||
lateinit var completeFile: String
|
||||
val saveFile: String = FileUtil.getSongFile(song)
|
||||
val saveFile: String = FileUtil.getSongFile(track)
|
||||
var shouldSave = save
|
||||
private var downloadTask: CancellableTask? = null
|
||||
var isFailed = false
|
||||
@ -104,7 +105,7 @@ class DownloadFile(
|
||||
* Returns the effective bit rate.
|
||||
*/
|
||||
fun getBitRate(): Int {
|
||||
return if (song.bitRate == null) desiredBitRate else song.bitRate!!
|
||||
return if (track.bitRate == null) desiredBitRate else track.bitRate!!
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
@ -221,7 +222,7 @@ class DownloadFile(
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return String.format("DownloadFile (%s)", song)
|
||||
return String.format(Locale.ROOT, "DownloadFile (%s)", track)
|
||||
}
|
||||
|
||||
private inner class DownloadTask : CancellableTask() {
|
||||
@ -259,7 +260,7 @@ class DownloadFile(
|
||||
|
||||
// Some devices seem to throw error on partial file which doesn't exist
|
||||
val needsDownloading: Boolean
|
||||
val duration = song.duration
|
||||
val duration = track.duration
|
||||
val fileLength = Storage.getFromPath(partialFile)?.length ?: 0
|
||||
|
||||
needsDownloading = (
|
||||
@ -269,7 +270,7 @@ class DownloadFile(
|
||||
if (needsDownloading) {
|
||||
// Attempt partial HTTP GET, appending to the file if it exists.
|
||||
val (inStream, isPartial) = musicService.getDownloadInputStream(
|
||||
song, fileLength, desiredBitRate, shouldSave
|
||||
track, fileLength, desiredBitRate, shouldSave
|
||||
)
|
||||
|
||||
inputStream = inStream
|
||||
@ -293,11 +294,13 @@ class DownloadFile(
|
||||
|
||||
if (isCancelled) {
|
||||
status.postValue(DownloadStatus.CANCELLED)
|
||||
throw Exception(String.format("Download of '%s' was cancelled", song))
|
||||
throw RuntimeException(
|
||||
String.format(Locale.ROOT, "Download of '%s' was cancelled", track)
|
||||
)
|
||||
}
|
||||
|
||||
if (song.artistId != null) {
|
||||
cacheMetadata(song.artistId!!)
|
||||
if (track.artistId != null) {
|
||||
cacheMetadata(track.artistId!!)
|
||||
}
|
||||
|
||||
downloadAndSaveCoverArt()
|
||||
@ -328,7 +331,7 @@ class DownloadFile(
|
||||
status.postValue(DownloadStatus.FAILED)
|
||||
--retryCount
|
||||
}
|
||||
Timber.w(all, "Failed to download '%s'.", song)
|
||||
Timber.w(all, "Failed to download '%s'.", track)
|
||||
}
|
||||
} finally {
|
||||
inputStream.safeClose()
|
||||
@ -339,7 +342,7 @@ class DownloadFile(
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return String.format("DownloadTask (%s)", song)
|
||||
return String.format(Locale.ROOT, "DownloadTask (%s)", track)
|
||||
}
|
||||
|
||||
private fun cacheMetadata(artistId: String) {
|
||||
@ -367,9 +370,9 @@ class DownloadFile(
|
||||
|
||||
private fun downloadAndSaveCoverArt() {
|
||||
try {
|
||||
if (!TextUtils.isEmpty(song.coverArt)) {
|
||||
if (!TextUtils.isEmpty(track.coverArt)) {
|
||||
// Download the largest size that we can display in the UI
|
||||
imageLoaderProvider.getImageLoader().cacheCoverArt(song)
|
||||
imageLoaderProvider.getImageLoader().cacheCoverArt(track)
|
||||
}
|
||||
} catch (all: Exception) {
|
||||
Timber.e(all, "Failed to get cover art.")
|
||||
@ -392,8 +395,8 @@ class DownloadFile(
|
||||
}
|
||||
|
||||
private fun setProgress(totalBytesCopied: Long) {
|
||||
if (song.size != null) {
|
||||
progress.postValue((totalBytesCopied * 100 / song.size!!).toInt())
|
||||
if (track.size != null) {
|
||||
progress.postValue((totalBytesCopied * 100 / track.size!!).toInt())
|
||||
}
|
||||
}
|
||||
|
||||
@ -404,7 +407,7 @@ class DownloadFile(
|
||||
}
|
||||
|
||||
override val id: String
|
||||
get() = song.id
|
||||
get() = track.id
|
||||
|
||||
companion object {
|
||||
const val MAX_RETRIES = 5
|
||||
|
@ -10,8 +10,8 @@ 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.domain.Track
|
||||
import org.moire.ultrasonic.util.LRUCache
|
||||
import org.moire.ultrasonic.util.Settings
|
||||
import org.moire.ultrasonic.util.ShufflePlayBuffer
|
||||
@ -45,7 +45,7 @@ class Downloader(
|
||||
private val jukeboxMediaPlayer: JukeboxMediaPlayer by inject()
|
||||
|
||||
// This cache helps us to avoid creating duplicate DownloadFile instances when showing Entries
|
||||
private val downloadFileCache = LRUCache<MusicDirectory.Entry, DownloadFile>(100)
|
||||
private val downloadFileCache = LRUCache<Track, DownloadFile>(100)
|
||||
|
||||
private var executorService: ScheduledExecutorService? = null
|
||||
private var wifiLock: WifiManager.WifiLock? = null
|
||||
@ -234,7 +234,7 @@ class Downloader(
|
||||
get() {
|
||||
var totalDuration: Long = 0
|
||||
for (downloadFile in playlist) {
|
||||
val song = downloadFile.song
|
||||
val song = downloadFile.track
|
||||
if (!song.isDirectory) {
|
||||
if (song.artist != null) {
|
||||
if (song.duration != null) {
|
||||
@ -345,7 +345,7 @@ class Downloader(
|
||||
|
||||
@Synchronized
|
||||
fun addToPlaylist(
|
||||
songs: List<MusicDirectory.Entry>,
|
||||
songs: List<Track>,
|
||||
save: Boolean,
|
||||
autoPlay: Boolean,
|
||||
playNext: Boolean,
|
||||
@ -407,7 +407,7 @@ class Downloader(
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun downloadBackground(songs: List<MusicDirectory.Entry>, save: Boolean) {
|
||||
fun downloadBackground(songs: List<Track>, save: Boolean) {
|
||||
|
||||
// By using the counter we ensure that the songs are added in the correct order
|
||||
for (song in songs) {
|
||||
@ -435,19 +435,19 @@ class Downloader(
|
||||
|
||||
@Synchronized
|
||||
@Suppress("ReturnCount")
|
||||
fun getDownloadFileForSong(song: MusicDirectory.Entry): DownloadFile {
|
||||
fun getDownloadFileForSong(song: Track): DownloadFile {
|
||||
for (downloadFile in playlist) {
|
||||
if (downloadFile.song == song) {
|
||||
if (downloadFile.track == song) {
|
||||
return downloadFile
|
||||
}
|
||||
}
|
||||
for (downloadFile in activelyDownloading) {
|
||||
if (downloadFile.song == song) {
|
||||
if (downloadFile.track == song) {
|
||||
return downloadFile
|
||||
}
|
||||
}
|
||||
for (downloadFile in downloadQueue) {
|
||||
if (downloadFile.song == song) {
|
||||
if (downloadFile.track == song) {
|
||||
return downloadFile
|
||||
}
|
||||
}
|
||||
@ -513,7 +513,7 @@ class Downloader(
|
||||
* Extension function
|
||||
* Gathers the download file for a given song, and modifies shouldSave if provided.
|
||||
*/
|
||||
fun MusicDirectory.Entry.getDownloadFile(save: Boolean? = null): DownloadFile {
|
||||
fun Track.getDownloadFile(save: Boolean? = null): DownloadFile {
|
||||
return getDownloadFileForSong(this).apply {
|
||||
if (save != null) this.shouldSave = save
|
||||
}
|
||||
|
@ -303,7 +303,7 @@ class LocalMediaPlayer : KoinComponent {
|
||||
val playerDuration: Int
|
||||
get() {
|
||||
if (currentPlaying != null) {
|
||||
val duration = currentPlaying!!.song.duration
|
||||
val duration = currentPlaying!!.track.duration
|
||||
if (duration != null) {
|
||||
return duration * 1000
|
||||
}
|
||||
@ -391,7 +391,7 @@ class LocalMediaPlayer : KoinComponent {
|
||||
setPlayerState(PlayerState.PREPARING, downloadFile)
|
||||
|
||||
mediaPlayer.setOnBufferingUpdateListener { mp, percent ->
|
||||
val song = downloadFile.song
|
||||
val song = downloadFile.track
|
||||
|
||||
if (percent == 100) {
|
||||
mp.setOnBufferingUpdateListener(null)
|
||||
@ -512,8 +512,8 @@ class LocalMediaPlayer : KoinComponent {
|
||||
}
|
||||
|
||||
var duration = 0
|
||||
if (downloadFile.song.duration != null) {
|
||||
duration = downloadFile.song.duration!! * 1000
|
||||
if (downloadFile.track.duration != null) {
|
||||
duration = downloadFile.track.duration!! * 1000
|
||||
}
|
||||
|
||||
mediaPlayer.setOnCompletionListener(object : OnCompletionListener {
|
||||
|
@ -11,9 +11,9 @@ import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import org.moire.ultrasonic.app.UApp
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.domain.PlayerState
|
||||
import org.moire.ultrasonic.domain.RepeatMode
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
import org.moire.ultrasonic.service.MediaPlayerService.Companion.executeOnStartedMediaPlayerService
|
||||
import org.moire.ultrasonic.service.MediaPlayerService.Companion.getInstance
|
||||
import org.moire.ultrasonic.service.MediaPlayerService.Companion.runningInstance
|
||||
@ -65,7 +65,7 @@ class MediaPlayerController(
|
||||
|
||||
@Synchronized
|
||||
fun restore(
|
||||
songs: List<MusicDirectory.Entry?>?,
|
||||
songs: List<Track?>?,
|
||||
currentPlayingIndex: Int,
|
||||
currentPlayingPosition: Int,
|
||||
autoPlay: Boolean,
|
||||
@ -165,7 +165,7 @@ class MediaPlayerController(
|
||||
@Synchronized
|
||||
@Suppress("LongParameterList")
|
||||
fun addToPlaylist(
|
||||
songs: List<MusicDirectory.Entry?>?,
|
||||
songs: List<Track?>?,
|
||||
save: Boolean,
|
||||
autoPlay: Boolean,
|
||||
playNext: Boolean,
|
||||
@ -202,7 +202,7 @@ class MediaPlayerController(
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun downloadBackground(songs: List<MusicDirectory.Entry?>?, save: Boolean) {
|
||||
fun downloadBackground(songs: List<Track?>?, save: Boolean) {
|
||||
if (songs == null) return
|
||||
val filteredSongs = songs.filterNotNull()
|
||||
downloader.downloadBackground(filteredSongs, save)
|
||||
@ -325,7 +325,7 @@ class MediaPlayerController(
|
||||
|
||||
@Synchronized
|
||||
// TODO: Make it require not null
|
||||
fun delete(songs: List<MusicDirectory.Entry?>) {
|
||||
fun delete(songs: List<Track?>) {
|
||||
for (song in songs.filterNotNull()) {
|
||||
downloader.getDownloadFileForSong(song).delete()
|
||||
}
|
||||
@ -333,7 +333,7 @@ class MediaPlayerController(
|
||||
|
||||
@Synchronized
|
||||
// TODO: Make it require not null
|
||||
fun unpin(songs: List<MusicDirectory.Entry?>) {
|
||||
fun unpin(songs: List<Track?>) {
|
||||
for (song in songs.filterNotNull()) {
|
||||
downloader.getDownloadFileForSong(song).unpin()
|
||||
}
|
||||
@ -453,7 +453,7 @@ class MediaPlayerController(
|
||||
|
||||
fun toggleSongStarred() {
|
||||
if (localMediaPlayer.currentPlaying == null) return
|
||||
val song = localMediaPlayer.currentPlaying!!.song
|
||||
val song = localMediaPlayer.currentPlaying!!.track
|
||||
|
||||
Thread {
|
||||
val musicService = getMusicService()
|
||||
@ -477,7 +477,7 @@ class MediaPlayerController(
|
||||
fun setSongRating(rating: Int) {
|
||||
if (!Settings.useFiveStarRating) return
|
||||
if (localMediaPlayer.currentPlaying == null) return
|
||||
val song = localMediaPlayer.currentPlaying!!.song
|
||||
val song = localMediaPlayer.currentPlaying!!.track
|
||||
song.userRating = rating
|
||||
Thread {
|
||||
try {
|
||||
@ -509,7 +509,7 @@ class MediaPlayerController(
|
||||
val playListDuration: Long
|
||||
get() = downloader.downloadListDuration
|
||||
|
||||
fun getDownloadFileForSong(song: MusicDirectory.Entry): DownloadFile {
|
||||
fun getDownloadFileForSong(song: Track): DownloadFile {
|
||||
return downloader.getDownloadFileForSong(song)
|
||||
}
|
||||
|
||||
|
@ -28,9 +28,9 @@ import org.koin.android.ext.android.inject
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.activity.NavigationActivity
|
||||
import org.moire.ultrasonic.app.UApp
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.domain.PlayerState
|
||||
import org.moire.ultrasonic.domain.RepeatMode
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
import org.moire.ultrasonic.imageloader.BitmapUtils
|
||||
import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X1
|
||||
import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X2
|
||||
@ -340,7 +340,7 @@ class MediaPlayerService : Service() {
|
||||
localMediaPlayer.setPlayerState(PlayerState.STARTED, localMediaPlayer.currentPlaying)
|
||||
}
|
||||
|
||||
private fun updateWidget(playerState: PlayerState, song: MusicDirectory.Entry?) {
|
||||
private fun updateWidget(playerState: PlayerState, song: Track?) {
|
||||
val started = playerState === PlayerState.STARTED
|
||||
val context = this@MediaPlayerService
|
||||
|
||||
@ -364,7 +364,7 @@ class MediaPlayerService : Service() {
|
||||
Settings.isNotificationAlwaysEnabled
|
||||
|
||||
val show = playerState === PlayerState.STARTED || showWhenPaused
|
||||
val song = currentPlaying?.song
|
||||
val song = currentPlaying?.track
|
||||
|
||||
if (isStateChanged) {
|
||||
when {
|
||||
@ -396,7 +396,7 @@ class MediaPlayerService : Service() {
|
||||
}
|
||||
|
||||
if (isTrackChanged) {
|
||||
Util.broadcastNewTrackInfo(this@MediaPlayerService, currentPlaying?.song)
|
||||
Util.broadcastNewTrackInfo(this@MediaPlayerService, currentPlaying?.track)
|
||||
}
|
||||
|
||||
// Update widget
|
||||
@ -424,7 +424,7 @@ class MediaPlayerService : Service() {
|
||||
val index = downloader.currentPlayingIndex
|
||||
|
||||
if (currentPlaying != null) {
|
||||
val song = currentPlaying.song
|
||||
val song = currentPlaying.track
|
||||
if (song.bookmarkPosition > 0 && Settings.shouldClearBookmark) {
|
||||
val musicService = getMusicService()
|
||||
try {
|
||||
@ -523,7 +523,7 @@ class MediaPlayerService : Service() {
|
||||
|
||||
// Init
|
||||
val context = applicationContext
|
||||
val song = currentPlaying?.song
|
||||
val song = currentPlaying?.track
|
||||
val stopIntent = Util.getPendingIntentForMediaAction(
|
||||
context,
|
||||
KeyEvent.KEYCODE_MEDIA_STOP,
|
||||
@ -589,7 +589,7 @@ class MediaPlayerService : Service() {
|
||||
context: Context,
|
||||
notificationBuilder: NotificationCompat.Builder,
|
||||
playerState: PlayerState,
|
||||
song: MusicDirectory.Entry?
|
||||
song: Track?
|
||||
): IntArray {
|
||||
// Init
|
||||
val compactActionList = ArrayList<Int>()
|
||||
|
@ -7,6 +7,7 @@
|
||||
package org.moire.ultrasonic.service
|
||||
|
||||
import java.io.InputStream
|
||||
import org.moire.ultrasonic.domain.Album
|
||||
import org.moire.ultrasonic.domain.Artist
|
||||
import org.moire.ultrasonic.domain.Bookmark
|
||||
import org.moire.ultrasonic.domain.ChatMessage
|
||||
@ -21,6 +22,7 @@ import org.moire.ultrasonic.domain.PodcastsChannel
|
||||
import org.moire.ultrasonic.domain.SearchCriteria
|
||||
import org.moire.ultrasonic.domain.SearchResult
|
||||
import org.moire.ultrasonic.domain.Share
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
import org.moire.ultrasonic.domain.UserInfo
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
@ -57,7 +59,7 @@ interface MusicService {
|
||||
fun getMusicDirectory(id: String, name: String?, refresh: Boolean): MusicDirectory
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun getArtist(id: String, name: String?, refresh: Boolean): List<MusicDirectory.Album>
|
||||
fun getArtist(id: String, name: String?, refresh: Boolean): List<Album>
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun getAlbum(id: String, name: String?, refresh: Boolean): MusicDirectory
|
||||
@ -75,7 +77,7 @@ interface MusicService {
|
||||
fun getPlaylists(refresh: Boolean): List<Playlist>
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun createPlaylist(id: String?, name: String?, entries: List<MusicDirectory.Entry>)
|
||||
fun createPlaylist(id: String?, name: String?, tracks: List<Track>)
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun deletePlaylist(id: String)
|
||||
@ -95,7 +97,7 @@ interface MusicService {
|
||||
size: Int,
|
||||
offset: Int,
|
||||
musicFolderId: String?
|
||||
): List<MusicDirectory.Album>
|
||||
): List<Album>
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun getAlbumList2(
|
||||
@ -103,7 +105,7 @@ interface MusicService {
|
||||
size: Int,
|
||||
offset: Int,
|
||||
musicFolderId: String?
|
||||
): List<MusicDirectory.Album>
|
||||
): List<Album>
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun getRandomSongs(size: Int): MusicDirectory
|
||||
@ -123,7 +125,7 @@ interface MusicService {
|
||||
*/
|
||||
@Throws(Exception::class)
|
||||
fun getDownloadInputStream(
|
||||
song: MusicDirectory.Entry,
|
||||
song: Track,
|
||||
offset: Long,
|
||||
maxBitrate: Int,
|
||||
save: Boolean
|
||||
|
@ -23,6 +23,7 @@ import java.util.regex.Pattern
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||
import org.moire.ultrasonic.domain.Album
|
||||
import org.moire.ultrasonic.domain.Artist
|
||||
import org.moire.ultrasonic.domain.ArtistOrIndex
|
||||
import org.moire.ultrasonic.domain.Bookmark
|
||||
@ -38,6 +39,7 @@ import org.moire.ultrasonic.domain.PodcastsChannel
|
||||
import org.moire.ultrasonic.domain.SearchCriteria
|
||||
import org.moire.ultrasonic.domain.SearchResult
|
||||
import org.moire.ultrasonic.domain.Share
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
import org.moire.ultrasonic.domain.UserInfo
|
||||
import org.moire.ultrasonic.util.AbstractFile
|
||||
import org.moire.ultrasonic.util.Constants
|
||||
@ -126,8 +128,8 @@ class OfflineMusicService : MusicService, KoinComponent {
|
||||
|
||||
override fun search(criteria: SearchCriteria): SearchResult {
|
||||
val artists: MutableList<ArtistOrIndex> = ArrayList()
|
||||
val albums: MutableList<MusicDirectory.Album> = ArrayList()
|
||||
val songs: MutableList<MusicDirectory.Entry> = ArrayList()
|
||||
val albums: MutableList<Album> = ArrayList()
|
||||
val songs: MutableList<Track> = ArrayList()
|
||||
val root = FileUtil.musicDirectory
|
||||
var closeness: Int
|
||||
for (artistFile in FileUtil.listFiles(root)) {
|
||||
@ -227,14 +229,14 @@ class OfflineMusicService : MusicService, KoinComponent {
|
||||
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
@Throws(Exception::class)
|
||||
override fun createPlaylist(id: String?, name: String?, entries: List<MusicDirectory.Entry>) {
|
||||
override fun createPlaylist(id: String?, name: String?, tracks: List<Track>) {
|
||||
val playlistFile =
|
||||
FileUtil.getPlaylistFile(activeServerProvider.getActiveServer().name, name)
|
||||
val fw = FileWriter(playlistFile)
|
||||
val bw = BufferedWriter(fw)
|
||||
try {
|
||||
fw.write("#EXTM3U\n")
|
||||
for (e in entries) {
|
||||
for (e in tracks) {
|
||||
var filePath = FileUtil.getSongFile(e)
|
||||
if (!Storage.isPathExists(filePath)) {
|
||||
val ext = FileUtil.getExtension(filePath)
|
||||
@ -299,7 +301,7 @@ class OfflineMusicService : MusicService, KoinComponent {
|
||||
size: Int,
|
||||
offset: Int,
|
||||
musicFolderId: String?
|
||||
): List<MusicDirectory.Album> {
|
||||
): List<Album> {
|
||||
throw OfflineException("Album lists not available in offline mode")
|
||||
}
|
||||
|
||||
@ -309,7 +311,7 @@ class OfflineMusicService : MusicService, KoinComponent {
|
||||
size: Int,
|
||||
offset: Int,
|
||||
musicFolderId: String?
|
||||
): List<MusicDirectory.Album> {
|
||||
): List<Album> {
|
||||
throw OfflineException("getAlbumList2 isn't available in offline mode")
|
||||
}
|
||||
|
||||
@ -455,7 +457,7 @@ class OfflineMusicService : MusicService, KoinComponent {
|
||||
|
||||
@Throws(OfflineException::class)
|
||||
override fun getArtist(id: String, name: String?, refresh: Boolean):
|
||||
List<MusicDirectory.Album> {
|
||||
List<Album> {
|
||||
throw OfflineException("getArtist isn't available in offline mode")
|
||||
}
|
||||
|
||||
@ -471,7 +473,7 @@ class OfflineMusicService : MusicService, KoinComponent {
|
||||
|
||||
@Throws(OfflineException::class)
|
||||
override fun getDownloadInputStream(
|
||||
song: MusicDirectory.Entry,
|
||||
song: Track,
|
||||
offset: Long,
|
||||
maxBitrate: Int,
|
||||
save: Boolean
|
||||
@ -502,14 +504,14 @@ class OfflineMusicService : MusicService, KoinComponent {
|
||||
return FileUtil.getBaseName(name)
|
||||
}
|
||||
|
||||
private fun createEntry(file: AbstractFile, name: String?): MusicDirectory.Entry {
|
||||
val entry = MusicDirectory.Entry(file.path)
|
||||
private fun createEntry(file: AbstractFile, name: String?): Track {
|
||||
val entry = Track(file.path)
|
||||
entry.populateWithDataFrom(file, name)
|
||||
return entry
|
||||
}
|
||||
|
||||
private fun createAlbum(file: AbstractFile, name: String?): MusicDirectory.Album {
|
||||
val album = MusicDirectory.Album(file.path)
|
||||
private fun createAlbum(file: AbstractFile, name: String?): Album {
|
||||
val album = Album(file.path)
|
||||
album.populateWithDataFrom(file, name)
|
||||
return album
|
||||
}
|
||||
@ -536,7 +538,7 @@ class OfflineMusicService : MusicService, KoinComponent {
|
||||
* More extensive variant of Child.populateWithDataFrom(), which also parses the ID3 tags of
|
||||
* a given track file.
|
||||
*/
|
||||
private fun MusicDirectory.Entry.populateWithDataFrom(file: AbstractFile, name: String?) {
|
||||
private fun Track.populateWithDataFrom(file: AbstractFile, name: String?) {
|
||||
(this as MusicDirectory.Child).populateWithDataFrom(file, name)
|
||||
|
||||
val meta = RawMetadata(null)
|
||||
@ -609,8 +611,8 @@ class OfflineMusicService : MusicService, KoinComponent {
|
||||
artistName: String,
|
||||
file: AbstractFile,
|
||||
criteria: SearchCriteria,
|
||||
albums: MutableList<MusicDirectory.Album>,
|
||||
songs: MutableList<MusicDirectory.Entry>
|
||||
albums: MutableList<Album>,
|
||||
songs: MutableList<Track>
|
||||
) {
|
||||
var closeness: Int
|
||||
for (albumFile in FileUtil.listMediaFiles(file)) {
|
||||
|
@ -61,7 +61,7 @@ class PlaybackStateSerializer : KoinComponent {
|
||||
val state = State()
|
||||
|
||||
for (downloadFile in songs) {
|
||||
state.songs.add(downloadFile.song)
|
||||
state.songs.add(downloadFile.track)
|
||||
}
|
||||
|
||||
state.currentPlayingIndex = currentPlayingIndex
|
||||
|
@ -19,6 +19,7 @@ import org.moire.ultrasonic.api.subsonic.throwOnFailure
|
||||
import org.moire.ultrasonic.api.subsonic.toStreamResponse
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
|
||||
import org.moire.ultrasonic.domain.Album
|
||||
import org.moire.ultrasonic.domain.Artist
|
||||
import org.moire.ultrasonic.domain.Bookmark
|
||||
import org.moire.ultrasonic.domain.ChatMessage
|
||||
@ -33,6 +34,7 @@ import org.moire.ultrasonic.domain.PodcastsChannel
|
||||
import org.moire.ultrasonic.domain.SearchCriteria
|
||||
import org.moire.ultrasonic.domain.SearchResult
|
||||
import org.moire.ultrasonic.domain.Share
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
import org.moire.ultrasonic.domain.UserInfo
|
||||
import org.moire.ultrasonic.domain.toArtistList
|
||||
import org.moire.ultrasonic.domain.toDomainEntitiesList
|
||||
@ -143,7 +145,7 @@ open class RESTMusicService(
|
||||
id: String,
|
||||
name: String?,
|
||||
refresh: Boolean
|
||||
): List<MusicDirectory.Album> {
|
||||
): List<Album> {
|
||||
val response = API.getArtist(id).execute().throwOnFailure()
|
||||
|
||||
return response.body()!!.artist.toDomainEntityList()
|
||||
@ -262,14 +264,14 @@ open class RESTMusicService(
|
||||
override fun createPlaylist(
|
||||
id: String?,
|
||||
name: String?,
|
||||
entries: List<MusicDirectory.Entry>
|
||||
tracks: List<Track>
|
||||
) {
|
||||
if (id == null && name == null)
|
||||
throw IllegalArgumentException("Either id or name is required.")
|
||||
|
||||
val pSongIds: MutableList<String> = ArrayList(entries.size)
|
||||
val pSongIds: MutableList<String> = ArrayList(tracks.size)
|
||||
|
||||
for ((id1) in entries) {
|
||||
for ((id1) in tracks) {
|
||||
pSongIds.add(id1)
|
||||
}
|
||||
|
||||
@ -350,7 +352,7 @@ open class RESTMusicService(
|
||||
size: Int,
|
||||
offset: Int,
|
||||
musicFolderId: String?
|
||||
): List<MusicDirectory.Album> {
|
||||
): List<Album> {
|
||||
val response = API.getAlbumList(
|
||||
fromName(type),
|
||||
size,
|
||||
@ -370,7 +372,7 @@ open class RESTMusicService(
|
||||
size: Int,
|
||||
offset: Int,
|
||||
musicFolderId: String?
|
||||
): List<MusicDirectory.Album> {
|
||||
): List<Album> {
|
||||
val response = API.getAlbumList2(
|
||||
fromName(type),
|
||||
size,
|
||||
@ -418,7 +420,7 @@ open class RESTMusicService(
|
||||
|
||||
@Throws(Exception::class)
|
||||
override fun getDownloadInputStream(
|
||||
song: MusicDirectory.Entry,
|
||||
song: Track,
|
||||
offset: Long,
|
||||
maxBitrate: Int,
|
||||
save: Boolean
|
||||
|
@ -8,6 +8,7 @@ import java.util.LinkedList
|
||||
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.Constants
|
||||
@ -33,7 +34,7 @@ class DownloadHandler(
|
||||
autoPlay: Boolean,
|
||||
playNext: Boolean,
|
||||
shuffle: Boolean,
|
||||
songs: List<MusicDirectory.Entry?>
|
||||
songs: List<Track?>
|
||||
) {
|
||||
val onValid = Runnable {
|
||||
if (!append && !playNext) {
|
||||
@ -195,15 +196,15 @@ class DownloadHandler(
|
||||
isArtist: Boolean
|
||||
) {
|
||||
val activity = fragment.activity as Activity
|
||||
val task = object : ModalBackgroundTask<List<MusicDirectory.Entry>>(
|
||||
val task = object : ModalBackgroundTask<List<Track>>(
|
||||
activity,
|
||||
false
|
||||
) {
|
||||
|
||||
@Throws(Throwable::class)
|
||||
override fun doInBackground(): List<MusicDirectory.Entry> {
|
||||
override fun doInBackground(): List<Track> {
|
||||
val musicService = getMusicService()
|
||||
val songs: MutableList<MusicDirectory.Entry> = LinkedList()
|
||||
val songs: MutableList<Track> = LinkedList()
|
||||
val root: MusicDirectory
|
||||
if (!isOffline() && isArtist && Settings.shouldUseId3Tags) {
|
||||
getSongsForArtist(id, songs)
|
||||
@ -235,7 +236,7 @@ class DownloadHandler(
|
||||
@Throws(Exception::class)
|
||||
private fun getSongsRecursively(
|
||||
parent: MusicDirectory,
|
||||
songs: MutableList<MusicDirectory.Entry>
|
||||
songs: MutableList<Track>
|
||||
) {
|
||||
if (songs.size > maxSongs) {
|
||||
return
|
||||
@ -259,7 +260,7 @@ class DownloadHandler(
|
||||
@Throws(Exception::class)
|
||||
private fun getSongsForArtist(
|
||||
id: String,
|
||||
songs: MutableCollection<MusicDirectory.Entry>
|
||||
songs: MutableCollection<Track>
|
||||
) {
|
||||
if (songs.size > maxSongs) {
|
||||
return
|
||||
@ -280,7 +281,7 @@ class DownloadHandler(
|
||||
}
|
||||
}
|
||||
|
||||
override fun done(songs: List<MusicDirectory.Entry>) {
|
||||
override fun done(songs: List<Track>) {
|
||||
if (Settings.shouldSortByDisc) {
|
||||
Collections.sort(songs, EntryByDiscAndTrackComparator())
|
||||
}
|
||||
|
@ -15,8 +15,8 @@ import java.util.Locale
|
||||
import java.util.regex.Pattern
|
||||
import kotlin.collections.ArrayList
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.domain.Share
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
|
||||
import org.moire.ultrasonic.util.BackgroundTask
|
||||
import org.moire.ultrasonic.util.CancellationToken
|
||||
@ -44,13 +44,13 @@ class ShareHandler(val context: Context) {
|
||||
|
||||
fun createShare(
|
||||
fragment: Fragment,
|
||||
entries: List<MusicDirectory.Entry?>?,
|
||||
tracks: List<Track?>?,
|
||||
swipe: SwipeRefreshLayout?,
|
||||
cancellationToken: CancellationToken
|
||||
) {
|
||||
val askForDetails = Settings.shouldAskForShareDetails
|
||||
val shareDetails = ShareDetails()
|
||||
shareDetails.Entries = entries
|
||||
shareDetails.Entries = tracks
|
||||
if (askForDetails) {
|
||||
showDialog(fragment, shareDetails, swipe, cancellationToken)
|
||||
} else {
|
||||
|
@ -4,7 +4,7 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory
|
||||
import org.moire.ultrasonic.util.Util
|
||||
|
||||
@ -14,14 +14,14 @@ import org.moire.ultrasonic.util.Util
|
||||
@Suppress("UtilityClassWithPublicConstructor")
|
||||
class VideoPlayer {
|
||||
companion object {
|
||||
fun playVideo(context: Context, entry: MusicDirectory.Entry?) {
|
||||
if (!Util.isNetworkConnected() || entry == null) {
|
||||
fun playVideo(context: Context, track: Track?) {
|
||||
if (!Util.isNetworkConnected() || track == null) {
|
||||
Util.toast(context, R.string.select_album_no_network)
|
||||
return
|
||||
}
|
||||
try {
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
val url = MusicServiceFactory.getMusicService().getVideoUrl(entry.id)
|
||||
val url = MusicServiceFactory.getMusicService().getVideoUrl(track.id)
|
||||
intent.setDataAndType(
|
||||
Uri.parse(url),
|
||||
"video/*"
|
||||
|
@ -2,13 +2,14 @@ package org.moire.ultrasonic.util
|
||||
|
||||
import java.util.Comparator
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
|
||||
class EntryByDiscAndTrackComparator : Comparator<MusicDirectory.Child> {
|
||||
override fun compare(x: MusicDirectory.Child, y: MusicDirectory.Child): Int {
|
||||
val discX = x.discNumber
|
||||
val discY = y.discNumber
|
||||
val trackX = if (x is MusicDirectory.Entry) x.track else null
|
||||
val trackY = if (y is MusicDirectory.Entry) y.track else null
|
||||
val trackX = if (x is Track) x.track else null
|
||||
val trackY = if (y is Track) y.track else null
|
||||
val albumX = x.album
|
||||
val albumY = y.album
|
||||
val pathX = x.path
|
||||
|
@ -27,6 +27,7 @@ import java.util.TreeSet
|
||||
import java.util.regex.Pattern
|
||||
import org.moire.ultrasonic.app.UApp
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
import org.moire.ultrasonic.util.Util.safeClose
|
||||
import timber.log.Timber
|
||||
|
||||
@ -49,7 +50,7 @@ object FileUtil {
|
||||
const val SUFFIX_SMALL = ".jpeg-small"
|
||||
private const val UNNAMED = "unnamed"
|
||||
|
||||
fun getSongFile(song: MusicDirectory.Entry): String {
|
||||
fun getSongFile(song: Track): String {
|
||||
val dir = getAlbumDirectory(song)
|
||||
|
||||
// Do not generate new name for offline files. Offline files will have their Path as their Id.
|
||||
|
@ -188,7 +188,7 @@ class MediaSessionHandler : KoinComponent {
|
||||
val metadata = MediaMetadataCompat.Builder()
|
||||
if (currentPlaying != null) {
|
||||
try {
|
||||
val song = currentPlaying.song
|
||||
val song = currentPlaying.track
|
||||
val cover = BitmapUtils.getAlbumArtBitmapFromDisk(
|
||||
song, Util.getMinDisplayMetric()
|
||||
)
|
||||
@ -278,7 +278,7 @@ class MediaSessionHandler : KoinComponent {
|
||||
|
||||
val queue = playlist.mapIndexed { id, file ->
|
||||
MediaSessionCompat.QueueItem(
|
||||
Util.getMediaDescriptionForEntry(file.song),
|
||||
Util.getMediaDescriptionForEntry(file.track),
|
||||
id.toLong()
|
||||
)
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ import org.moire.ultrasonic.domain.Bookmark
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.domain.PlayerState
|
||||
import org.moire.ultrasonic.domain.SearchResult
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
import org.moire.ultrasonic.service.DownloadFile
|
||||
import timber.log.Timber
|
||||
|
||||
@ -438,9 +439,9 @@ object Util {
|
||||
@JvmStatic
|
||||
fun getSongsFromBookmarks(bookmarks: Iterable<Bookmark>): MusicDirectory {
|
||||
val musicDirectory = MusicDirectory()
|
||||
var song: MusicDirectory.Entry
|
||||
var song: Track
|
||||
for (bookmark in bookmarks) {
|
||||
song = bookmark.entry
|
||||
song = bookmark.track
|
||||
song.bookmarkPosition = bookmark.position
|
||||
musicDirectory.add(song)
|
||||
}
|
||||
@ -450,7 +451,7 @@ object Util {
|
||||
/**
|
||||
* Broadcasts the given song info as the new song being played.
|
||||
*/
|
||||
fun broadcastNewTrackInfo(context: Context, song: MusicDirectory.Entry?) {
|
||||
fun broadcastNewTrackInfo(context: Context, song: Track?) {
|
||||
val intent = Intent(EVENT_META_CHANGED)
|
||||
if (song != null) {
|
||||
intent.putExtra("title", song.title)
|
||||
@ -476,9 +477,9 @@ object Util {
|
||||
) {
|
||||
if (!Settings.shouldSendBluetoothNotifications) return
|
||||
|
||||
var song: MusicDirectory.Entry? = null
|
||||
var song: Track? = null
|
||||
val avrcpIntent = Intent(CM_AVRCP_METADATA_CHANGED)
|
||||
if (currentPlaying != null) song = currentPlaying.song
|
||||
if (currentPlaying != null) song = currentPlaying.track
|
||||
|
||||
fillIntent(avrcpIntent, song, playerPosition, id, listSize)
|
||||
|
||||
@ -489,7 +490,7 @@ object Util {
|
||||
fun broadcastA2dpPlayStatusChange(
|
||||
context: Context,
|
||||
state: PlayerState?,
|
||||
newSong: MusicDirectory.Entry?,
|
||||
newSong: Track?,
|
||||
listSize: Int,
|
||||
id: Int,
|
||||
playerPosition: Int
|
||||
@ -520,7 +521,7 @@ object Util {
|
||||
|
||||
private fun fillIntent(
|
||||
intent: Intent,
|
||||
song: MusicDirectory.Entry?,
|
||||
song: Track?,
|
||||
playerPosition: Int,
|
||||
id: Int,
|
||||
listSize: Int
|
||||
@ -776,7 +777,7 @@ object Util {
|
||||
)
|
||||
|
||||
fun getMediaDescriptionForEntry(
|
||||
song: MusicDirectory.Entry,
|
||||
song: Track,
|
||||
mediaId: String? = null,
|
||||
groupNameId: Int? = null
|
||||
): MediaDescriptionCompat {
|
||||
@ -809,7 +810,7 @@ object Util {
|
||||
}
|
||||
|
||||
@Suppress("ComplexMethod", "LongMethod")
|
||||
fun readableEntryDescription(song: MusicDirectory.Entry): ReadableEntryDescription {
|
||||
fun readableEntryDescription(song: Track): ReadableEntryDescription {
|
||||
val artist = StringBuilder(LINE_LENGTH)
|
||||
var bitRate: String? = null
|
||||
var trackText = ""
|
||||
|
@ -27,7 +27,7 @@ class APIBookmarkConverterTest {
|
||||
comment `should be equal to` entity.comment
|
||||
created `should be equal to` entity.created?.time
|
||||
changed `should be equal to` entity.changed?.time
|
||||
entry `should be equal to` entity.entry.toTrackEntity()
|
||||
track `should be equal to` entity.entry.toTrackEntity()
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user