mirror of
https://github.com/antebudimir/tempus.git
synced 2026-04-15 16:27:26 +00:00
fix: instant mix random songs (#354)
* wip: updated instant mix request size * Address broken continuous play * wip: filling queue, getting dupes * fix: deduped the song track list
This commit is contained in:
parent
55265615e6
commit
e77f3bf9b3
6 changed files with 55 additions and 58 deletions
|
|
@ -25,6 +25,7 @@ import retrofit2.Response;
|
||||||
public class SongRepository {
|
public class SongRepository {
|
||||||
|
|
||||||
private static final String TAG = "SongRepository";
|
private static final String TAG = "SongRepository";
|
||||||
|
|
||||||
public interface MediaCallbackInternal {
|
public interface MediaCallbackInternal {
|
||||||
void onSongsAvailable(List<Child> songs);
|
void onSongsAvailable(List<Child> songs);
|
||||||
}
|
}
|
||||||
|
|
@ -64,18 +65,25 @@ public class SongRepository {
|
||||||
*/
|
*/
|
||||||
public MutableLiveData<List<Child>> getInstantMix(String id, SeedType type, int count) {
|
public MutableLiveData<List<Child>> getInstantMix(String id, SeedType type, int count) {
|
||||||
MutableLiveData<List<Child>> instantMix = new MutableLiveData<>(new ArrayList<>());
|
MutableLiveData<List<Child>> instantMix = new MutableLiveData<>(new ArrayList<>());
|
||||||
|
Set<String> trackIds = new HashSet<>();
|
||||||
|
|
||||||
performSmartMix(id, type, count, songs -> {
|
performSmartMix(id, type, count, songs -> {
|
||||||
List<Child> current = instantMix.getValue();
|
List<Child> current = instantMix.getValue();
|
||||||
if (current != null) {
|
if (current != null) {
|
||||||
for (Child s : songs) {
|
for (Child s : songs) {
|
||||||
if (!current.contains(s)) current.add(s);
|
if (!trackIds.contains(s.getId())) {
|
||||||
|
current.add(s);
|
||||||
|
trackIds.add(s.getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current.size() < count / 2) {
|
if (current.size() < count / 2) {
|
||||||
fillWithRandom(count - current.size(), remainder -> {
|
fetchSimilarOnly(id, count, remainder -> {
|
||||||
for (Child r : remainder) {
|
for (Child r : remainder) {
|
||||||
if (!current.contains(r)) current.add(r);
|
if (!trackIds.contains(r.getId())) {
|
||||||
|
current.add(r);
|
||||||
|
trackIds.add(r.getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
instantMix.postValue(current);
|
instantMix.postValue(current);
|
||||||
});
|
});
|
||||||
|
|
@ -130,6 +138,7 @@ public class SongRepository {
|
||||||
isComplete = true;
|
isComplete = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void performSmartMix(final String id, final SeedType type, final int count, final MediaCallbackInternal callback) {
|
private void performSmartMix(final String id, final SeedType type, final int count, final MediaCallbackInternal callback) {
|
||||||
|
|
@ -138,7 +147,7 @@ public class SongRepository {
|
||||||
fetchSimilarByArtist(id, count, callback);
|
fetchSimilarByArtist(id, count, callback);
|
||||||
break;
|
break;
|
||||||
case ALBUM:
|
case ALBUM:
|
||||||
fetchAlbumSongsThenSimilar(id, count, callback);
|
fetchAlbumSongs(id, count, callback);
|
||||||
break;
|
break;
|
||||||
case TRACK:
|
case TRACK:
|
||||||
fetchSingleTrackThenSimilar(id, count, callback);
|
fetchSingleTrackThenSimilar(id, count, callback);
|
||||||
|
|
@ -146,7 +155,7 @@ public class SongRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchAlbumSongsThenSimilar(String albumId, int count, MediaCallbackInternal callback) {
|
private void fetchAlbumSongs(String albumId, int count, MediaCallbackInternal callback) {
|
||||||
App.getSubsonicClientInstance(false).getBrowsingClient().getAlbum(albumId).enqueue(new Callback<ApiResponse>() {
|
App.getSubsonicClientInstance(false).getBrowsingClient().getAlbum(albumId).enqueue(new Callback<ApiResponse>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
|
|
@ -157,25 +166,14 @@ public class SongRepository {
|
||||||
int fromAlbum = Math.min(count, albumSongs.size());
|
int fromAlbum = Math.min(count, albumSongs.size());
|
||||||
List<Child> limitedAlbumSongs = albumSongs.subList(0, fromAlbum);
|
List<Child> limitedAlbumSongs = albumSongs.subList(0, fromAlbum);
|
||||||
callback.onSongsAvailable(new ArrayList<>(limitedAlbumSongs));
|
callback.onSongsAvailable(new ArrayList<>(limitedAlbumSongs));
|
||||||
|
|
||||||
int remaining = count - fromAlbum;
|
|
||||||
if (remaining > 0 && albumSongs.get(0).getArtistId() != null) {
|
|
||||||
fetchSimilarByArtist(albumSongs.get(0).getArtistId(), remaining, callback);
|
|
||||||
} else if (remaining > 0) {
|
|
||||||
Log.d(TAG, "No artistId available, skipping similar artist fetch");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d(TAG, "Album fetch failed or empty, calling fillWithRandom");
|
|
||||||
fillWithRandom(count, callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||||
Log.d(TAG, "Album fetch failed: " + t.getMessage());
|
Log.e(TAG, "fetchAlbumSongsThenSimilar.onFailure()", t);
|
||||||
fillWithRandom(count, callback);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -188,17 +186,17 @@ public class SongRepository {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
List<Child> similar = extractSongs(response, "similarSongs2");
|
List<Child> similar = extractSongs(response, "similarSongs2");
|
||||||
|
Log.d(TAG, "fetchSimilarByArtist.onResponse() - similar songs: " + similar.size());
|
||||||
|
|
||||||
if (!similar.isEmpty()) {
|
if (!similar.isEmpty()) {
|
||||||
List<Child> limitedSimilar = similar.subList(0, Math.min(count, similar.size()));
|
List<Child> limitedSimilar = similar.subList(0, Math.min(count, similar.size()));
|
||||||
callback.onSongsAvailable(limitedSimilar);
|
callback.onSongsAvailable(limitedSimilar);
|
||||||
} else {
|
|
||||||
fillWithRandom(count, callback);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||||
fillWithRandom(count, callback);
|
Log.e(TAG, "fetchSimilarByArtist.onFailure()", t);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -211,17 +209,13 @@ public class SongRepository {
|
||||||
Child song = response.body().getSubsonicResponse().getSong();
|
Child song = response.body().getSubsonicResponse().getSong();
|
||||||
if (song != null) {
|
if (song != null) {
|
||||||
callback.onSongsAvailable(Collections.singletonList(song));
|
callback.onSongsAvailable(Collections.singletonList(song));
|
||||||
int remaining = count - 1;
|
|
||||||
if (remaining > 0) {
|
|
||||||
fetchSimilarOnly(trackId, remaining, callback);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fillWithRandom(count, callback);
|
|
||||||
}
|
}
|
||||||
@Override public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
|
||||||
fillWithRandom(count, callback);
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||||
|
Log.e(TAG, "fetchSingleTrackThenSimilar.onFailure()", t);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -232,38 +226,40 @@ public class SongRepository {
|
||||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
List<Child> songs = extractSongs(response, "similarSongs");
|
List<Child> songs = extractSongs(response, "similarSongs");
|
||||||
if (!songs.isEmpty()) {
|
if (!songs.isEmpty()) {
|
||||||
List<Child> limitedSongs = songs.subList(0, Math.min(count, songs.size()));
|
int limit = Math.min(count, songs.size());
|
||||||
callback.onSongsAvailable(limitedSongs);
|
callback.onSongsAvailable(songs.subList(0, limit));
|
||||||
} else {
|
|
||||||
fillWithRandom(count, callback);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@Override public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
|
||||||
fillWithRandom(count, callback);
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||||
|
Log.e(TAG, "fetchSimilarOnly.onFailure()", t);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void fillWithRandom(int target, final MediaCallbackInternal callback) {
|
public MutableLiveData<List<Child>> getContinuousMix(String id, int count) {
|
||||||
|
MutableLiveData<List<Child>> instantMix = new MutableLiveData<>();
|
||||||
|
|
||||||
App.getSubsonicClientInstance(false)
|
App.getSubsonicClientInstance(false)
|
||||||
.getAlbumSongListClient()
|
.getBrowsingClient()
|
||||||
.getRandomSongs(target, null, null)
|
.getSimilarSongs(id, count)
|
||||||
.enqueue(new Callback<ApiResponse>() {
|
.enqueue(new Callback<ApiResponse>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
List<Child> random = extractSongs(response, "randomSongs");
|
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getSimilarSongs() != null) {
|
||||||
if (!random.isEmpty()) {
|
instantMix.setValue(response.body().getSubsonicResponse().getSimilarSongs().getSongs());
|
||||||
List<Child> limitedRandom = random.subList(0, Math.min(target, random.size()));
|
|
||||||
callback.onSongsAvailable(limitedRandom);
|
|
||||||
} else {
|
|
||||||
callback.onSongsAvailable(new ArrayList<>());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@Override public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
|
||||||
callback.onSongsAvailable(new ArrayList<>());
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||||
|
instantMix.setValue(null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return instantMix;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Child> extractSongs(Response<ApiResponse> response, String type) {
|
private List<Child> extractSongs(Response<ApiResponse> response, String type) {
|
||||||
|
|
@ -274,11 +270,10 @@ public class SongRepository {
|
||||||
list = res.getSimilarSongs().getSongs();
|
list = res.getSimilarSongs().getSongs();
|
||||||
} else if (type.equals("similarSongs2") && res.getSimilarSongs2() != null) {
|
} else if (type.equals("similarSongs2") && res.getSimilarSongs2() != null) {
|
||||||
list = res.getSimilarSongs2().getSongs();
|
list = res.getSimilarSongs2().getSongs();
|
||||||
} else if (type.equals("randomSongs") && res.getRandomSongs() != null) {
|
|
||||||
list = res.getRandomSongs().getSongs();
|
|
||||||
}
|
}
|
||||||
return (list != null) ? list : new ArrayList<>();
|
return (list != null) ? list : new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -299,6 +294,7 @@ public class SongRepository {
|
||||||
|
|
||||||
public MutableLiveData<List<Child>> getRandomSampleWithGenre(int number, Integer fromYear, Integer toYear, String genre) {
|
public MutableLiveData<List<Child>> getRandomSampleWithGenre(int number, Integer fromYear, Integer toYear, String genre) {
|
||||||
MutableLiveData<List<Child>> randomSongsSample = new MutableLiveData<>();
|
MutableLiveData<List<Child>> randomSongsSample = new MutableLiveData<>();
|
||||||
|
|
||||||
App.getSubsonicClientInstance(false).getAlbumSongListClient().getRandomSongs(number, fromYear, toYear, genre).enqueue(new Callback<ApiResponse>() {
|
App.getSubsonicClientInstance(false).getAlbumSongListClient().getRandomSongs(number, fromYear, toYear, genre).enqueue(new Callback<ApiResponse>() {
|
||||||
@Override public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
@Override public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
List<Child> songs = new ArrayList<>();
|
List<Child> songs = new ArrayList<>();
|
||||||
|
|
|
||||||
|
|
@ -448,7 +448,8 @@ public class MediaManager {
|
||||||
if (mediaItem != null && Preferences.isContinuousPlayEnabled() && Preferences.isInstantMixUsable()) {
|
if (mediaItem != null && Preferences.isContinuousPlayEnabled() && Preferences.isInstantMixUsable()) {
|
||||||
Preferences.setLastInstantMix();
|
Preferences.setLastInstantMix();
|
||||||
|
|
||||||
LiveData<List<Child>> instantMix = getSongRepository().getInstantMix(mediaItem.mediaId, SeedType.TRACK, 10);
|
LiveData<List<Child>> instantMix = getSongRepository().getContinuousMix(mediaItem.mediaId,25);
|
||||||
|
|
||||||
instantMix.observeForever(new Observer<List<Child>>() {
|
instantMix.observeForever(new Observer<List<Child>>() {
|
||||||
@Override
|
@Override
|
||||||
public void onChanged(List<Child> media) {
|
public void onChanged(List<Child> media) {
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,7 @@ public class AlbumBottomSheetViewModel extends AndroidViewModel {
|
||||||
public LiveData<List<Child>> getAlbumInstantMix(LifecycleOwner owner, AlbumID3 album) {
|
public LiveData<List<Child>> getAlbumInstantMix(LifecycleOwner owner, AlbumID3 album) {
|
||||||
instantMix.setValue(Collections.emptyList());
|
instantMix.setValue(Collections.emptyList());
|
||||||
|
|
||||||
albumRepository.getInstantMix(album, 20).observe(owner, instantMix::postValue);
|
albumRepository.getInstantMix(album, 30).observe(owner, instantMix::postValue);
|
||||||
|
|
||||||
return instantMix;
|
return instantMix;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,7 @@ public class ArtistBottomSheetViewModel extends AndroidViewModel {
|
||||||
public LiveData<List<Child>> getArtistInstantMix(LifecycleOwner owner, ArtistID3 artist) {
|
public LiveData<List<Child>> getArtistInstantMix(LifecycleOwner owner, ArtistID3 artist) {
|
||||||
instantMix.setValue(Collections.emptyList());
|
instantMix.setValue(Collections.emptyList());
|
||||||
|
|
||||||
artistRepository.getInstantMix(artist, 20).observe(owner, instantMix::postValue);
|
artistRepository.getInstantMix(artist, 30).observe(owner, instantMix::postValue);
|
||||||
|
|
||||||
return instantMix;
|
return instantMix;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ public class ArtistPageViewModel extends AndroidViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
public LiveData<List<Child>> getArtistInstantMix() {
|
public LiveData<List<Child>> getArtistInstantMix() {
|
||||||
return artistRepository.getInstantMix(artist, 20);
|
return artistRepository.getInstantMix(artist, 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArtistID3 getArtist() {
|
public ArtistID3 getArtist() {
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ public class SongBottomSheetViewModel extends AndroidViewModel {
|
||||||
public LiveData<List<Child>> getInstantMix(LifecycleOwner owner, Child media) {
|
public LiveData<List<Child>> getInstantMix(LifecycleOwner owner, Child media) {
|
||||||
instantMix.setValue(Collections.emptyList());
|
instantMix.setValue(Collections.emptyList());
|
||||||
|
|
||||||
songRepository.getInstantMix(media.getId(), SeedType.TRACK, 20).observe(owner, instantMix::postValue);
|
songRepository.getInstantMix(media.getId(), SeedType.TRACK, 30).observe(owner, instantMix::postValue);
|
||||||
|
|
||||||
return instantMix;
|
return instantMix;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue