wip: refactor song repo instant mix to take in a type

This commit is contained in:
eddyizm 2025-12-27 12:27:18 -08:00
parent f59f572e5c
commit 8de9aff1f6
No known key found for this signature in database
GPG key ID: CF5F671829E8158A
5 changed files with 93 additions and 52 deletions

View file

@ -7,6 +7,7 @@ import com.cappielloantonio.tempo.App;
import com.cappielloantonio.tempo.subsonic.base.ApiResponse; import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
import com.cappielloantonio.tempo.subsonic.models.Child; import com.cappielloantonio.tempo.subsonic.models.Child;
import com.cappielloantonio.tempo.subsonic.models.SubsonicResponse; import com.cappielloantonio.tempo.subsonic.models.SubsonicResponse;
import com.cappielloantonio.tempo.util.Constants;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -56,16 +57,26 @@ public class SongRepository {
/** /**
* Used by ViewModels. Updates the LiveData list incrementally as songs are found. * Used by ViewModels. Updates the LiveData list incrementally as songs are found.
*/ */
public MutableLiveData<List<Child>> getInstantMix(String id, int count) { public MutableLiveData<List<Child>> getInstantMix(String id, Constants.SEEDTYPE type, int count) {
MutableLiveData<List<Child>> instantMix = new MutableLiveData<>(new ArrayList<>()); MutableLiveData<List<Child>> instantMix = new MutableLiveData<>(new ArrayList<>());
performSmartMix(id, 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 (!current.contains(s)) current.add(s);
} }
if (current.size() < count / 2) {
fillWithRandom(count - current.size(), remainder -> {
for (Child r : remainder) {
if (!current.contains(r)) current.add(r);
}
instantMix.postValue(current); instantMix.postValue(current);
});
} else {
instantMix.postValue(current);
}
} }
}); });
@ -75,54 +86,76 @@ public class SongRepository {
/** /**
* Overloaded method used by other Repositories * Overloaded method used by other Repositories
*/ */
public void getInstantMix(String id, int count, MediaCallbackInternal callback) { public void getInstantMix(String id, Constants.SEEDTYPE type, int count, MediaCallbackInternal callback) {
performSmartMix(id, count, callback); performSmartMix(id, type, count, callback);
} }
private void performSmartMix(final String id, final int count, final MediaCallbackInternal callback) { private void performSmartMix(final String id, final Constants.SEEDTYPE type, final int count, final MediaCallbackInternal callback) {
App.getSubsonicClientInstance(false) switch (type) {
.getBrowsingClient() case ARTIST:
.getSimilarSongs(id, count) fetchSimilarByArtist(id, count, callback);
.enqueue(new Callback<ApiResponse>() { break;
@Override case ALBUM:
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) { fetchAlbumSongsThenSimilar(id, count, callback);
List<Child> songs = extractSongs(response, "similarSongs"); break;
if (!songs.isEmpty()) { case TRACK:
callback.onSongsAvailable(songs); fetchSingleTrackThenSimilar(id, count, callback);
} break;
if (songs.size() < count / 2) {
fetchContextAndSeed(id, count, callback);
} }
} }
@Override private void fetchAlbumSongsThenSimilar(String albumId, int count, MediaCallbackInternal callback) {
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) { App.getSubsonicClientInstance(false).getBrowsingClient().getAlbum(albumId).enqueue(new Callback<ApiResponse>() {
fetchContextAndSeed(id, count, callback);
}
});
}
private void fetchContextAndSeed(final String id, final int count, final MediaCallbackInternal callback) {
App.getSubsonicClientInstance(false)
.getBrowsingClient()
.getAlbum(id)
.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) {
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbum() != null) { if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbum() != null) {
List<Child> albumSongs = response.body().getSubsonicResponse().getAlbum().getSongs(); List<Child> albumSongs = response.body().getSubsonicResponse().getAlbum().getSongs();
if (albumSongs != null && !albumSongs.isEmpty()) { if (albumSongs != null && !albumSongs.isEmpty()) {
callback.onSongsAvailable(new ArrayList<>(albumSongs)); callback.onSongsAvailable(new ArrayList<>(albumSongs));
String seedArtistId = albumSongs.get(0).getArtistId(); fetchSimilarByArtist(albumSongs.get(0).getArtistId(), count, callback);
fetchSimilarByArtist(seedArtistId, count, callback);
return; return;
} }
} }
fillWithRandom(count, callback); fillWithRandom(count, callback);
} }
@Override public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
fillWithRandom(count, callback);
}
});
}
private void fetchSingleTrackThenSimilar(String trackId, int count, MediaCallbackInternal callback) {
App.getSubsonicClientInstance(false).getBrowsingClient().getSong(trackId).enqueue(new Callback<ApiResponse>() {
@Override @Override
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) { public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
if (response.isSuccessful() && response.body() != null) {
Child song = response.body().getSubsonicResponse().getSong();
if (song != null) {
callback.onSongsAvailable(Collections.singletonList(song));
fetchSimilarOnly(trackId, count, callback);
return;
}
}
fillWithRandom(count, callback);
}
@Override public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
fillWithRandom(count, callback);
}
});
}
private void fetchSimilarOnly(String id, int count, MediaCallbackInternal callback) {
App.getSubsonicClientInstance(false).getBrowsingClient().getSimilarSongs(id, count).enqueue(new Callback<ApiResponse>() {
@Override
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
List<Child> songs = extractSongs(response, "similarSongs");
if (!songs.isEmpty()) {
callback.onSongsAvailable(songs);
} else {
fillWithRandom(count, callback);
}
}
@Override public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
fillWithRandom(count, callback); fillWithRandom(count, callback);
} }
}); });
@ -138,9 +171,14 @@ public class SongRepository {
List<Child> similar = extractSongs(response, "similarSongs2"); List<Child> similar = extractSongs(response, "similarSongs2");
if (!similar.isEmpty()) { if (!similar.isEmpty()) {
callback.onSongsAvailable(similar); callback.onSongsAvailable(similar);
} else {
fillWithRandom(count, callback);
} }
} }
@Override public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {} @Override
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
fillWithRandom(count, callback);
}
}); });
} }

View file

@ -26,6 +26,7 @@ import com.cappielloantonio.tempo.repository.SongRepository;
import com.cappielloantonio.tempo.subsonic.models.Child; import com.cappielloantonio.tempo.subsonic.models.Child;
import com.cappielloantonio.tempo.subsonic.models.InternetRadioStation; import com.cappielloantonio.tempo.subsonic.models.InternetRadioStation;
import com.cappielloantonio.tempo.subsonic.models.PodcastEpisode; import com.cappielloantonio.tempo.subsonic.models.PodcastEpisode;
import com.cappielloantonio.tempo.util.Constants;
import com.cappielloantonio.tempo.util.MappingUtil; import com.cappielloantonio.tempo.util.MappingUtil;
import com.cappielloantonio.tempo.util.Preferences; import com.cappielloantonio.tempo.util.Preferences;
import com.cappielloantonio.tempo.viewmodel.PlaybackViewModel; import com.cappielloantonio.tempo.viewmodel.PlaybackViewModel;
@ -442,7 +443,7 @@ 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, 10); LiveData<List<Child>> instantMix = getSongRepository().getInstantMix(mediaItem.mediaId, Constants.SEEDTYPE.TRACK, 10);
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) {

View file

@ -24,6 +24,7 @@ import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
import com.cappielloantonio.tempo.subsonic.models.Child; import com.cappielloantonio.tempo.subsonic.models.Child;
import com.cappielloantonio.tempo.subsonic.models.Playlist; import com.cappielloantonio.tempo.subsonic.models.Playlist;
import com.cappielloantonio.tempo.subsonic.models.Share; import com.cappielloantonio.tempo.subsonic.models.Share;
import com.cappielloantonio.tempo.util.Constants;
import com.cappielloantonio.tempo.util.Preferences; import com.cappielloantonio.tempo.util.Preferences;
import com.google.common.reflect.TypeToken; import com.google.common.reflect.TypeToken;
import com.google.gson.Gson; import com.google.gson.Gson;
@ -223,7 +224,7 @@ public class HomeViewModel extends AndroidViewModel {
public LiveData<List<Child>> getMediaInstantMix(LifecycleOwner owner, Child media) { public LiveData<List<Child>> getMediaInstantMix(LifecycleOwner owner, Child media) {
mediaInstantMix.setValue(Collections.emptyList()); mediaInstantMix.setValue(Collections.emptyList());
songRepository.getInstantMix(media.getId(), 20).observe(owner, mediaInstantMix::postValue); songRepository.getInstantMix(media.getId(), Constants.SEEDTYPE.TRACK, 20).observe(owner, mediaInstantMix::postValue);
return mediaInstantMix; return mediaInstantMix;
} }

View file

@ -278,7 +278,7 @@ public class PlayerBottomSheetViewModel extends AndroidViewModel {
public LiveData<List<Child>> getMediaInstantMix(LifecycleOwner owner, Child media) { public LiveData<List<Child>> getMediaInstantMix(LifecycleOwner owner, Child media) {
instantMix.setValue(Collections.emptyList()); instantMix.setValue(Collections.emptyList());
songRepository.getInstantMix(media.getId(), 20).observe(owner, instantMix::postValue); songRepository.getInstantMix(media.getId(), Constants.SEEDTYPE.TRACK, 20).observe(owner, instantMix::postValue);
return instantMix; return instantMix;
} }

View file

@ -21,6 +21,7 @@ import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
import com.cappielloantonio.tempo.subsonic.models.ArtistID3; import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
import com.cappielloantonio.tempo.subsonic.models.Child; import com.cappielloantonio.tempo.subsonic.models.Child;
import com.cappielloantonio.tempo.subsonic.models.Share; import com.cappielloantonio.tempo.subsonic.models.Share;
import com.cappielloantonio.tempo.util.Constants;
import com.cappielloantonio.tempo.util.DownloadUtil; import com.cappielloantonio.tempo.util.DownloadUtil;
import com.cappielloantonio.tempo.util.MappingUtil; import com.cappielloantonio.tempo.util.MappingUtil;
import com.cappielloantonio.tempo.util.NetworkUtil; import com.cappielloantonio.tempo.util.NetworkUtil;
@ -128,7 +129,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(), 20).observe(owner, instantMix::postValue); songRepository.getInstantMix(media.getId(), Constants.SEEDTYPE.TRACK, 20).observe(owner, instantMix::postValue);
return instantMix; return instantMix;
} }