Removed Jellyfin dependencies

This commit is contained in:
CappielloAntonio 2021-07-27 14:53:03 +02:00
parent 737eaa1822
commit 9495fbd83d
16 changed files with 62 additions and 310 deletions

View file

@ -35,9 +35,6 @@ android {
dependencies { dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"]) implementation fileTree(dir: "libs", include: ["*.jar"])
// Jellyfin
implementation 'com.github.jellyfin.jellyfin-apiclient-java:android:0.7.7'
// AndroidX // AndroidX
implementation 'androidx.core:core-ktx:1.6.0' implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'androidx.appcompat:appcompat:1.3.1'
@ -88,8 +85,6 @@ dependencies {
annotationProcessor 'com.tickaroo.tikxml:processor:0.9.0_11-SNAPSHOT' annotationProcessor 'com.tickaroo.tikxml:processor:0.9.0_11-SNAPSHOT'
implementation 'com.tickaroo.tikxml:converter-date-rfc3339:0.9.0_11-SNAPSHOT' implementation 'com.tickaroo.tikxml:converter-date-rfc3339:0.9.0_11-SNAPSHOT'
// implementation 'net.beardbot:subsonic-client:0.2.0'
// Database debugger via adb // Database debugger via adb
// debugImplementation 'com.amitshekhar.android:debug-db:1.0.6' // debugImplementation 'com.amitshekhar.android:debug-db:1.0.6'

View file

@ -11,20 +11,9 @@ import com.cappielloantonio.play.subsonic.Subsonic;
import com.cappielloantonio.play.subsonic.SubsonicPreferences; import com.cappielloantonio.play.subsonic.SubsonicPreferences;
import com.cappielloantonio.play.util.PreferenceUtil; import com.cappielloantonio.play.util.PreferenceUtil;
import org.jellyfin.apiclient.AppInfo;
import org.jellyfin.apiclient.BuildConfig;
import org.jellyfin.apiclient.Jellyfin;
import org.jellyfin.apiclient.JellyfinAndroidKt;
import org.jellyfin.apiclient.JellyfinOptions;
import org.jellyfin.apiclient.interaction.AndroidDevice;
import org.jellyfin.apiclient.interaction.ApiClient;
import org.jellyfin.apiclient.interaction.ApiEventListener;
import org.jellyfin.apiclient.logging.NullLogger;
public class App extends Application { public class App extends Application {
private static final String TAG = "App"; private static final String TAG = "App";
private static App instance; private static App instance;
private static ApiClient apiClient;
private static Subsonic subsonic; private static Subsonic subsonic;
@Override @Override
@ -44,26 +33,6 @@ public class App extends Application {
return instance; return instance;
} }
public static ApiClient getApiClientInstance(Context context) {
if (apiClient == null) {
apiClient = getApiClient(context);
}
return apiClient;
}
private static ApiClient getApiClient(Context context) {
String server = "http://192.168.1.81:8096";
JellyfinOptions.Builder options = new JellyfinOptions.Builder();
options.setLogger(new NullLogger());
options.setAppInfo(new AppInfo(context.getString(R.string.app_name), BuildConfig.VERSION_NAME));
JellyfinAndroidKt.android(options, context);
Jellyfin jellyfin = new Jellyfin(options.build());
return jellyfin.createApi(server, null, AndroidDevice.fromContext(context), new ApiEventListener());
}
public static Subsonic getSubsonicClientInstance(Context context, boolean override) { public static Subsonic getSubsonicClientInstance(Context context, boolean override) {
if (subsonic == null || override) { if (subsonic == null || override) {
subsonic = getSubsonicClient(context); subsonic = getSubsonicClient(context);

View file

@ -18,9 +18,6 @@ import com.cappielloantonio.play.R;
import com.cappielloantonio.play.util.MusicUtil; import com.cappielloantonio.play.util.MusicUtil;
import com.wolt.blurhashkt.BlurHashDecoder; import com.wolt.blurhashkt.BlurHashDecoder;
import org.jellyfin.apiclient.model.dto.ImageOptions;
import org.jellyfin.apiclient.model.entities.ImageType;
import java.util.Map; import java.util.Map;
public class CustomGlideRequest { public class CustomGlideRequest {
@ -50,7 +47,7 @@ public class CustomGlideRequest {
String url = App.getSubsonicClientInstance(App.getInstance(), false).getUrl(); String url = App.getSubsonicClientInstance(App.getInstance(), false).getUrl();
Map<String, String> params = App.getSubsonicClientInstance(App.getInstance(), false).getParams(); Map<String, String> params = App.getSubsonicClientInstance(App.getInstance(), false).getParams();
String sb = url + "/rest/" + "getCoverArt" + String sb = url + "getCoverArt" +
"?u=" + params.get("u") + "?u=" + params.get("u") +
"&s=" + params.get("s") + "&s=" + params.get("s") +
"&t=" + params.get("t") + "&t=" + params.get("t") +

View file

@ -2,7 +2,6 @@ package com.cappielloantonio.play.model;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.room.ColumnInfo; import androidx.room.ColumnInfo;
@ -12,13 +11,6 @@ import androidx.room.PrimaryKey;
import com.cappielloantonio.play.subsonic.models.AlbumID3; import com.cappielloantonio.play.subsonic.models.AlbumID3;
import org.jellyfin.apiclient.model.dto.BaseItemDto;
import org.jellyfin.apiclient.model.entities.ImageType;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@Entity(tableName = "album") @Entity(tableName = "album")
public class Album implements Parcelable { public class Album implements Parcelable {
private static final String TAG = "Album"; private static final String TAG = "Album";

View file

@ -2,7 +2,6 @@ package com.cappielloantonio.play.model;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.room.ColumnInfo; import androidx.room.ColumnInfo;
@ -10,10 +9,6 @@ import androidx.room.Entity;
import androidx.room.Ignore; import androidx.room.Ignore;
import androidx.room.PrimaryKey; import androidx.room.PrimaryKey;
import org.jellyfin.apiclient.model.dto.BaseItemDto;
import org.jellyfin.apiclient.model.dto.GenreDto;
import org.jellyfin.apiclient.model.entities.ImageType;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -56,39 +51,6 @@ public class Artist implements Parcelable {
this.backdropBlurHash = backdropBlurHash; this.backdropBlurHash = backdropBlurHash;
} }
@Ignore
public Artist(BaseItemDto itemDto) {
this.id = itemDto.getId();
this.name = itemDto.getName();
this.primary = itemDto.getImageTags().containsKey(ImageType.Primary) ? id : null;
if (itemDto.getImageBlurHashes() != null && itemDto.getImageBlurHashes().get(ImageType.Primary) != null) {
this.primaryBlurHash = (String) itemDto.getImageBlurHashes().get(ImageType.Primary).values().toArray()[0];
}
try {
this.backdrop = !itemDto.getBackdropImageTags().get(0).isEmpty() ? id : null;
if (itemDto.getImageBlurHashes() != null && itemDto.getBackdropImageTags().get(0) != null) {
this.backdropBlurHash = (String) itemDto.getImageBlurHashes().get(ImageType.Backdrop).values().toArray()[0];
}
}
catch (IndexOutOfBoundsException exception) {
this.backdrop = null;
this.backdropBlurHash = null;
}
this.genres = new ArrayList<>();
this.albums = new ArrayList<>();
this.songs = new ArrayList<>();
if (itemDto.getGenreItems() != null) {
for (GenreDto genre : itemDto.getGenreItems()) {
genres.add(new Genre(genre));
}
}
}
@Ignore @Ignore
public Artist(String id, String name) { public Artist(String id, String name) {
this.id = id; this.id = id;

View file

@ -6,15 +6,8 @@ import android.os.Parcelable;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.room.ColumnInfo; import androidx.room.ColumnInfo;
import androidx.room.Entity; import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.PrimaryKey; import androidx.room.PrimaryKey;
import org.jellyfin.apiclient.model.dto.BaseItemDto;
import org.jellyfin.apiclient.model.dto.GenreDto;
import org.jellyfin.apiclient.model.entities.ImageType;
import java.util.UUID;
@Entity(tableName = "genre") @Entity(tableName = "genre")
public class Genre implements Parcelable { public class Genre implements Parcelable {
@NonNull @NonNull
@ -42,25 +35,6 @@ public class Genre implements Parcelable {
this.blurHash = blurHash; this.blurHash = blurHash;
} }
@Ignore
public Genre(GenreDto genreDto) {
this.id = genreDto.getId();
this.name = genreDto.getName();
this.songCount = 0;
}
@Ignore
public Genre(BaseItemDto itemDto) {
this.id = itemDto.getId();
this.name = itemDto.getName();
this.songCount = itemDto.getSongCount() != null ? itemDto.getSongCount() : 0;
this.primary = itemDto.getImageTags().containsKey(ImageType.Primary) ? id : null;
if (itemDto.getImageBlurHashes() != null && itemDto.getImageBlurHashes().get(ImageType.Primary) != null) {
this.blurHash = (String) itemDto.getImageBlurHashes().get(ImageType.Primary).values().toArray()[0];
}
}
@NonNull @NonNull
public String getId() { public String getId() {
return id; return id;

View file

@ -6,12 +6,8 @@ import android.os.Parcelable;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.room.ColumnInfo; import androidx.room.ColumnInfo;
import androidx.room.Entity; import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.PrimaryKey; import androidx.room.PrimaryKey;
import org.jellyfin.apiclient.model.dto.BaseItemDto;
import org.jellyfin.apiclient.model.entities.ImageType;
@Entity(tableName = "playlist") @Entity(tableName = "playlist")
public class Playlist implements Parcelable { public class Playlist implements Parcelable {
@NonNull @NonNull
@ -35,17 +31,6 @@ public class Playlist implements Parcelable {
this.blurHash = blurHash; this.blurHash = blurHash;
} }
@Ignore
public Playlist(BaseItemDto itemDto) {
this.id = itemDto.getId();
this.name = itemDto.getName();
this.primary = itemDto.getImageTags().containsKey(ImageType.Primary) ? id : null;
if (itemDto.getImageBlurHashes() != null && itemDto.getImageBlurHashes().get(ImageType.Primary) != null) {
this.blurHash = (String) itemDto.getImageBlurHashes().get(ImageType.Primary).values().toArray()[0];
}
}
@NonNull @NonNull
public String getId() { public String getId() {
return id; return id;

View file

@ -3,7 +3,6 @@ package com.cappielloantonio.play.model;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.room.ColumnInfo; import androidx.room.ColumnInfo;
@ -13,15 +12,7 @@ import androidx.room.PrimaryKey;
import com.cappielloantonio.play.subsonic.models.Child; import com.cappielloantonio.play.subsonic.models.Child;
import org.jellyfin.apiclient.model.dto.BaseItemDto;
import org.jellyfin.apiclient.model.dto.MediaSourceInfo;
import org.jellyfin.apiclient.model.entities.ImageType;
import org.jellyfin.apiclient.model.entities.MediaStream;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID; import java.util.UUID;
@Entity(tableName = "song") @Entity(tableName = "song")
@ -341,7 +332,9 @@ public class Song implements Parcelable {
this.playCount = playCount; this.playCount = playCount;
} }
public void setOffline(boolean offline) { this.offline = offline; } public void setOffline(boolean offline) {
this.offline = offline;
}
/* /*
Log.i(TAG, "increasePlayCount: " + isIncreased); Log.i(TAG, "increasePlayCount: " + isIncreased);
@ -354,7 +347,7 @@ public class Song implements Parcelable {
public boolean nowPlaying() { public boolean nowPlaying() {
long startPlayTime = Instant.now().toEpochMilli(); long startPlayTime = Instant.now().toEpochMilli();
if(startPlayTime - (getDuration()/2) > getLastPlay()) { if (startPlayTime - (getDuration() / 2) > getLastPlay()) {
this.playCount++; this.playCount++;
this.lastPlay = startPlayTime; this.lastPlay = startPlayTime;
return true; return true;

View file

@ -6,17 +6,8 @@ import android.os.Parcelable;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.room.ColumnInfo; import androidx.room.ColumnInfo;
import androidx.room.Entity; import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.PrimaryKey; import androidx.room.PrimaryKey;
import org.jellyfin.apiclient.model.dto.BaseItemDto;
import org.jellyfin.apiclient.model.dto.MediaSourceInfo;
import org.jellyfin.apiclient.model.entities.ImageType;
import org.jellyfin.apiclient.model.entities.MediaStream;
import java.time.Instant;
import java.util.UUID;
@Entity(tableName = "song_genre_cross") @Entity(tableName = "song_genre_cross")
public class SongGenreCross implements Parcelable { public class SongGenreCross implements Parcelable {
@NonNull @NonNull

View file

@ -13,6 +13,7 @@ import com.cappielloantonio.play.interfaces.MediaCallback;
import com.cappielloantonio.play.model.Song; import com.cappielloantonio.play.model.Song;
import com.cappielloantonio.play.model.SongGenreCross; import com.cappielloantonio.play.model.SongGenreCross;
import com.cappielloantonio.play.subsonic.api.albumsonglist.AlbumSongListClient; import com.cappielloantonio.play.subsonic.api.albumsonglist.AlbumSongListClient;
import com.cappielloantonio.play.subsonic.api.browsing.BrowsingClient;
import com.cappielloantonio.play.subsonic.models.ResponseStatus; import com.cappielloantonio.play.subsonic.models.ResponseStatus;
import com.cappielloantonio.play.subsonic.models.SubsonicResponse; import com.cappielloantonio.play.subsonic.models.SubsonicResponse;
import com.cappielloantonio.play.util.MappingUtil; import com.cappielloantonio.play.util.MappingUtil;
@ -29,6 +30,7 @@ public class SongRepository {
private static final String TAG = "SongRepository"; private static final String TAG = "SongRepository";
private AlbumSongListClient albumSongListClient; private AlbumSongListClient albumSongListClient;
private BrowsingClient browsingClient;
private SongDao songDao; private SongDao songDao;
private SongGenreCrossDao songGenreCrossDao; private SongGenreCrossDao songGenreCrossDao;
@ -50,6 +52,7 @@ public class SongRepository {
public SongRepository(Application application) { public SongRepository(Application application) {
albumSongListClient = App.getSubsonicClientInstance(application, false).getAlbumSongListClient(); albumSongListClient = App.getSubsonicClientInstance(application, false).getAlbumSongListClient();
browsingClient = App.getSubsonicClientInstance(application, false).getBrowsingClient();
AppDatabase database = AppDatabase.getInstance(application); AppDatabase database = AppDatabase.getInstance(application);
songDao = database.songDao(); songDao = database.songDao();
@ -79,6 +82,33 @@ public class SongRepository {
return liveSongs; return liveSongs;
} }
public void getInstantMix(Song song, int count, MediaCallback callback) {
browsingClient
.getSimilarSongs2(song.getId(), count)
.enqueue(new Callback<SubsonicResponse>() {
@Override
public void onResponse(Call<SubsonicResponse> call, Response<SubsonicResponse> response) {
if (response.body().getStatus().getValue().equals(ResponseStatus.OK)) {
List<Song> songs = new ArrayList<>(MappingUtil.mapSong(response.body().getSimilarSongs2().getSongs()));
if(songs.size() > 1) {
callback.onLoadMedia(songs);
}
else {
songs.add(song);
callback.onLoadMedia(songs);
}
}
}
@Override
public void onFailure(Call<SubsonicResponse> call, Throwable t) {
List<Song> songs = new ArrayList<>();
songs.add(song);
callback.onLoadMedia(songs);
}
});
}
public LiveData<List<Song>> searchListLiveSong(String title, int limit) { public LiveData<List<Song>> searchListLiveSong(String title, int limit) {
searchListLiveSongs = songDao.searchSong(title, limit); searchListLiveSongs = songDao.searchSong(title, limit);
return searchListLiveSongs; return searchListLiveSongs;

View file

@ -37,20 +37,12 @@ import com.cappielloantonio.play.repository.SongRepository;
import com.cappielloantonio.play.ui.notification.PlayingNotification; import com.cappielloantonio.play.ui.notification.PlayingNotification;
import com.cappielloantonio.play.util.PreferenceUtil; import com.cappielloantonio.play.util.PreferenceUtil;
import org.jellyfin.apiclient.interaction.EmptyResponse;
import org.jellyfin.apiclient.interaction.Response;
import org.jellyfin.apiclient.model.session.PlaybackProgressInfo;
import org.jellyfin.apiclient.model.session.PlaybackStartInfo;
import org.jellyfin.apiclient.model.session.PlaybackStopInfo;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static com.google.android.exoplayer2.Player.MEDIA_ITEM_TRANSITION_REASON_AUTO; import static com.google.android.exoplayer2.Player.MEDIA_ITEM_TRANSITION_REASON_AUTO;
import static com.google.android.exoplayer2.Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED; import static com.google.android.exoplayer2.Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED;
@ -794,7 +786,6 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
private final WeakReference<MusicService> mService; private final WeakReference<MusicService> mService;
private ScheduledExecutorService executorService; private ScheduledExecutorService executorService;
private Future<?> task;
public ProgressHandler(MusicService service, Looper looper) { public ProgressHandler(MusicService service, Looper looper) {
super(looper); super(looper);
@ -806,67 +797,10 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
public void handleMessage(@NonNull final Message msg) { public void handleMessage(@NonNull final Message msg) {
switch (msg.what) { switch (msg.what) {
case TRACK_STARTED: case TRACK_STARTED:
onStart(); case TRACK_CHANGED: break;
case TRACK_CHANGED:
onNext();
break;
case TRACK_ENDED: case TRACK_ENDED:
onStop();
} }
} }
public void onStart() {
if (executorService != null) executorService.shutdownNow();
executorService = Executors.newScheduledThreadPool(1);
task = executorService.scheduleAtFixedRate(this::onProgress, 10, 10, TimeUnit.SECONDS);
}
public void onNext() {
PlaybackStartInfo startInfo = new PlaybackStartInfo();
startInfo.setItemId(mService.get().getCurrentSong().getId());
startInfo.setVolumeLevel(mService.get().playback.getVolume());
startInfo.setCanSeek(true);
startInfo.setIsPaused(false);
App.getApiClientInstance(App.getInstance()).ensureWebSocket();
App.getApiClientInstance(App.getInstance()).ReportPlaybackStartAsync(startInfo, new EmptyResponse());
}
public void onProgress() {
PlaybackProgressInfo progressInfo = new PlaybackProgressInfo();
// TODO these cause a wrong thread error
long progress = mService.get().getSongProgressMillis();
double duration = mService.get().getSongDurationMillis();
if (progress / duration > 0.9) {
Song current = mService.get().getCurrentSong();
String user = App.getApiClientInstance(App.getInstance()).getCurrentUserId();
Date time = new Date(System.currentTimeMillis());
App.getApiClientInstance(App.getInstance()).MarkPlayedAsync(current.getId(), user, time, new Response<>());
}
progressInfo.setItemId(mService.get().getCurrentSong().getId());
progressInfo.setPositionTicks(progress * 10000);
progressInfo.setVolumeLevel(mService.get().playback.getVolume());
progressInfo.setIsPaused(!mService.get().playback.isPlaying());
progressInfo.setCanSeek(true);
App.getApiClientInstance(App.getInstance()).ReportPlaybackProgressAsync(progressInfo, new EmptyResponse());
}
public void onStop() {
PlaybackStopInfo info = new PlaybackStopInfo();
long progress = mService.get().getSongProgressMillis();
info.setItemId(mService.get().getCurrentSong().getId());
info.setPositionTicks(progress * 10000);
if (task != null) task.cancel(true);
if (executorService != null) executorService.shutdownNow();
}
} }
public class MusicBinder extends Binder { public class MusicBinder extends Binder {

View file

@ -30,7 +30,7 @@ public class MediaRetrievalClient {
this.mediaRetrievalService = retrofit.create(MediaRetrievalService.class); this.mediaRetrievalService = retrofit.create(MediaRetrievalService.class);
} }
public Call<SubsonicResponse> stream(String id, int maxBitRate, String format) { public Call<SubsonicResponse> stream(String id, Integer maxBitRate, String format) {
Log.d(TAG, "stream()"); Log.d(TAG, "stream()");
return mediaRetrievalService.stream(subsonic.getParams(), id, maxBitRate, format); return mediaRetrievalService.stream(subsonic.getParams(), id, maxBitRate, format);
} }

View file

@ -6,12 +6,13 @@ import java.util.Map;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.Query;
import retrofit2.http.QueryMap; import retrofit2.http.QueryMap;
public interface MediaRetrievalService { public interface MediaRetrievalService {
@GET("stream?id={id}?maxBitRate={maxBitRate}?format={format}") @GET("stream")
Call<SubsonicResponse> stream(@QueryMap Map<String, String> params, String id, int maxBitRate, String format); Call<SubsonicResponse> stream(@QueryMap Map<String, String> params, @Query("id") String id, @Query("maxBitRate") Integer maxBitRate, @Query("format") String format);
@GET("download?id={id}") @GET("download")
Call<SubsonicResponse> download(@QueryMap Map<String, String> params, String id); Call<SubsonicResponse> download(@QueryMap Map<String, String> params, @Query("id") String id);
} }

View file

@ -3,9 +3,7 @@ package com.cappielloantonio.play.ui.activity;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import android.view.View; import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
@ -14,25 +12,18 @@ import androidx.navigation.NavController;
import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.fragment.NavHostFragment;
import androidx.navigation.ui.NavigationUI; import androidx.navigation.ui.NavigationUI;
import com.cappielloantonio.play.App;
import com.cappielloantonio.play.R; import com.cappielloantonio.play.R;
import com.cappielloantonio.play.broadcast.receiver.ConnectivityStatusBroadcastReceiver; import com.cappielloantonio.play.broadcast.receiver.ConnectivityStatusBroadcastReceiver;
import com.cappielloantonio.play.databinding.ActivityMainBinding; import com.cappielloantonio.play.databinding.ActivityMainBinding;
import com.cappielloantonio.play.service.MusicPlayerRemote;
import com.cappielloantonio.play.model.Song; import com.cappielloantonio.play.model.Song;
import com.cappielloantonio.play.service.MusicPlayerRemote;
import com.cappielloantonio.play.ui.activity.base.BaseActivity; import com.cappielloantonio.play.ui.activity.base.BaseActivity;
import com.cappielloantonio.play.ui.fragment.PlayerBottomSheetFragment; import com.cappielloantonio.play.ui.fragment.PlayerBottomSheetFragment;
import com.cappielloantonio.play.util.PreferenceUtil; import com.cappielloantonio.play.util.PreferenceUtil;
import com.cappielloantonio.play.util.SyncUtil;
import com.cappielloantonio.play.viewmodel.MainViewModel; import com.cappielloantonio.play.viewmodel.MainViewModel;
import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.bottomsheet.BottomSheetBehavior;
import org.jellyfin.apiclient.interaction.EmptyResponse;
import org.jellyfin.apiclient.interaction.Response;
import org.jellyfin.apiclient.model.session.ClientCapabilities;
import org.jellyfin.apiclient.model.system.SystemInfo;
import java.util.Objects; import java.util.Objects;
public class MainActivity extends BaseActivity { public class MainActivity extends BaseActivity {
@ -78,35 +69,12 @@ public class MainActivity extends BaseActivity {
initNavigation(); initNavigation();
if (PreferenceUtil.getInstance(this).getToken() != null) { if (PreferenceUtil.getInstance(this).getToken() != null) {
checkPreviousSession();
goFromLogin(); goFromLogin();
} else { } else {
goToLogin(); goToLogin();
} }
} }
private void checkPreviousSession() {
App.getApiClientInstance(getApplicationContext()).ChangeServerLocation(PreferenceUtil.getInstance(this).getServer());
App.getApiClientInstance(getApplicationContext()).SetAuthenticationInfo(PreferenceUtil.getInstance(this).getToken(), PreferenceUtil.getInstance(this).getUser());
App.getApiClientInstance(getApplicationContext()).GetSystemInfoAsync(new Response<SystemInfo>() {
@Override
public void onResponse(SystemInfo result) {
ClientCapabilities clientCapabilities = new ClientCapabilities();
clientCapabilities.setSupportsMediaControl(true);
clientCapabilities.setSupportsPersistentIdentifier(true);
App.getApiClientInstance(getApplicationContext()).ensureWebSocket();
App.getApiClientInstance(getApplicationContext()).ReportCapabilities(clientCapabilities, new EmptyResponse());
}
@Override
public void onError(Exception exception) {
Toast.makeText(getApplicationContext(), exception.getMessage(), Toast.LENGTH_LONG).show();
}
});
}
// BOTTOM SHEET/NAVIGATION // BOTTOM SHEET/NAVIGATION
private void initBottomSheet() { private void initBottomSheet() {
bottomSheetBehavior = BottomSheetBehavior.from(findViewById(R.id.player_bottom_sheet)); bottomSheetBehavior = BottomSheetBehavior.from(findViewById(R.id.player_bottom_sheet));
@ -176,7 +144,8 @@ public class MainActivity extends BaseActivity {
case BottomSheetBehavior.STATE_COLLAPSED: case BottomSheetBehavior.STATE_COLLAPSED:
case BottomSheetBehavior.STATE_SETTLING: case BottomSheetBehavior.STATE_SETTLING:
PlayerBottomSheetFragment playerBottomSheetFragment = (PlayerBottomSheetFragment) getSupportFragmentManager().findFragmentByTag("PlayerBottomSheet"); PlayerBottomSheetFragment playerBottomSheetFragment = (PlayerBottomSheetFragment) getSupportFragmentManager().findFragmentByTag("PlayerBottomSheet");
if (playerBottomSheetFragment != null) playerBottomSheetFragment.scrollOnTop(); if (playerBottomSheetFragment != null)
playerBottomSheetFragment.scrollOnTop();
break; break;
} }
} }
@ -186,8 +155,7 @@ public class MainActivity extends BaseActivity {
PlayerBottomSheetFragment playerBottomSheetFragment = (PlayerBottomSheetFragment) getSupportFragmentManager().findFragmentByTag("PlayerBottomSheet"); PlayerBottomSheetFragment playerBottomSheetFragment = (PlayerBottomSheetFragment) getSupportFragmentManager().findFragmentByTag("PlayerBottomSheet");
if (playerBottomSheetFragment == null) { if (playerBottomSheetFragment == null) {
return; return;
} } else {
else {
float condensedSlideOffset = Math.max(0.0f, Math.min(0.2f, slideOffset - 0.2f)) / 0.2f; float condensedSlideOffset = Math.max(0.0f, Math.min(0.2f, slideOffset - 0.2f)) / 0.2f;
playerBottomSheetFragment.getPlayerHeader().setAlpha(1 - condensedSlideOffset); playerBottomSheetFragment.getPlayerHeader().setAlpha(1 - condensedSlideOffset);
playerBottomSheetFragment.getPlayerHeader().setVisibility(condensedSlideOffset > 0.99 ? View.GONE : View.VISIBLE); playerBottomSheetFragment.getPlayerHeader().setVisibility(condensedSlideOffset > 0.99 ? View.GONE : View.VISIBLE);
@ -217,8 +185,7 @@ public class MainActivity extends BaseActivity {
if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.landingFragment) { if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.landingFragment) {
navController.navigate(R.id.action_landingFragment_to_homeFragment); navController.navigate(R.id.action_landingFragment_to_homeFragment);
} } else if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.loginFragment) {
else if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.loginFragment) {
navController.navigate(R.id.action_loginFragment_to_homeFragment); navController.navigate(R.id.action_loginFragment_to_homeFragment);
} }
} }

View file

@ -1,64 +1,34 @@
package com.cappielloantonio.play.util; package com.cappielloantonio.play.util;
import android.content.Context; import android.content.Context;
import android.util.Log;
import com.cappielloantonio.play.App; import com.cappielloantonio.play.App;
import com.cappielloantonio.play.R; import com.cappielloantonio.play.R;
import com.cappielloantonio.play.glide.CustomGlideRequest; import com.cappielloantonio.play.glide.CustomGlideRequest;
import com.cappielloantonio.play.model.DirectPlayCodec;
import com.cappielloantonio.play.model.Song; import com.cappielloantonio.play.model.Song;
import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.MediaItem;
import org.jellyfin.apiclient.interaction.ApiClient;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.Random; import java.util.Random;
public class MusicUtil { public class MusicUtil {
private static final String TAG = "MusicUtil";
public static String getSongFileUri(Song song) { public static String getSongFileUri(Song song) {
ApiClient apiClient = App.getApiClientInstance(App.getInstance()); String url = App.getSubsonicClientInstance(App.getInstance(), false).getUrl();
PreferenceUtil preferenceUtil = PreferenceUtil.getInstance(App.getInstance());
StringBuilder builder = new StringBuilder(256); Map<String, String> params = App.getSubsonicClientInstance(App.getInstance(), false).getParams();
builder.append(apiClient.getApiUrl());
builder.append("/Audio/");
builder.append(song.getId());
builder.append("/universal");
builder.append("?UserId=").append(apiClient.getCurrentUserId());
builder.append("&DeviceId=").append(apiClient.getDeviceId());
// web client maximum is 12444445 and 320kbps is 320000 return url + "stream" +
builder.append("&MaxStreamingBitrate=").append(preferenceUtil.getMaximumBitrate()); "?u=" + params.get("u") +
"&s=" + params.get("s") +
boolean containerAdded = false; "&t=" + params.get("t") +
for (DirectPlayCodec directPlayCodec : preferenceUtil.getDirectPlayCodecs()) { "&v=" + params.get("v") +
if (directPlayCodec.selected) { "&c=" + params.get("c") +
if (!containerAdded) { "&id=" + song.getId();
builder.append("&Container=");
containerAdded = true;
}
builder.append(directPlayCodec.codec.value).append(',');
}
}
if (containerAdded) {
// remove last comma
builder.deleteCharAt(builder.length() - 1);
}
builder.append("&TranscodingContainer=ts");
builder.append("&TranscodingProtocol=hls");
// preferred codec when transcoding
builder.append("&AudioCodec=").append(preferenceUtil.getTranscodeCodec());
builder.append("&api_key=").append(apiClient.getAccessToken());
Log.i(MusicUtil.class.getName(), "playing audio: " + builder);
return builder.toString();
} }
public static String getReadableDurationString(long songDurationMillis) { public static String getReadableDurationString(long songDurationMillis) {

View file

@ -5,22 +5,14 @@ import android.os.Bundle;
import com.cappielloantonio.play.App; import com.cappielloantonio.play.App;
import com.cappielloantonio.play.interfaces.MediaCallback; import com.cappielloantonio.play.interfaces.MediaCallback;
import com.cappielloantonio.play.model.PlaylistSongCross;
import com.cappielloantonio.play.model.Song; import com.cappielloantonio.play.model.Song;
import com.cappielloantonio.play.subsonic.models.AlbumID3; import com.cappielloantonio.play.subsonic.models.AlbumID3;
import com.cappielloantonio.play.subsonic.models.ArtistID3; import com.cappielloantonio.play.subsonic.models.ArtistID3;
import com.cappielloantonio.play.subsonic.models.Child;
import com.cappielloantonio.play.subsonic.models.IndexID3; import com.cappielloantonio.play.subsonic.models.IndexID3;
import com.cappielloantonio.play.subsonic.models.MusicFolder; import com.cappielloantonio.play.subsonic.models.MusicFolder;
import com.cappielloantonio.play.subsonic.models.ResponseStatus; import com.cappielloantonio.play.subsonic.models.ResponseStatus;
import com.cappielloantonio.play.subsonic.models.SubsonicResponse; import com.cappielloantonio.play.subsonic.models.SubsonicResponse;
import org.jellyfin.apiclient.interaction.Response;
import org.jellyfin.apiclient.model.dto.BaseItemDto;
import org.jellyfin.apiclient.model.playlists.PlaylistItemQuery;
import org.jellyfin.apiclient.model.querying.ItemFields;
import org.jellyfin.apiclient.model.querying.ItemsResult;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -203,7 +195,7 @@ public class SyncUtil {
} }
public static void getSongsPerPlaylist(Context context, MediaCallback callback, String playlistId) { public static void getSongsPerPlaylist(Context context, MediaCallback callback, String playlistId) {
PlaylistItemQuery query = new PlaylistItemQuery(); /*PlaylistItemQuery query = new PlaylistItemQuery();
query.setId(playlistId); query.setId(playlistId);
query.setUserId(App.getApiClientInstance(context).getCurrentUserId()); query.setUserId(App.getApiClientInstance(context).getCurrentUserId());
@ -227,7 +219,7 @@ public class SyncUtil {
public void onError(Exception exception) { public void onError(Exception exception) {
callback.onError(exception); callback.onError(exception);
} }
}); });*/
} }
public static void getInstantMix(Context context, MediaCallback callback, String resultType, String itemID, int limit) { public static void getInstantMix(Context context, MediaCallback callback, String resultType, String itemID, int limit) {
@ -308,7 +300,7 @@ public class SyncUtil {
} }
private static void updateSongData(Map<Integer, Song> library, List<Song> songs) { private static void updateSongData(Map<Integer, Song> library, List<Song> songs) {
for (Song song: songs) { for (Song song : songs) {
if (library.containsKey(song.hashCode())) { if (library.containsKey(song.hashCode())) {
Song oldSong = library.get(song.hashCode()); Song oldSong = library.get(song.hashCode());
song.setFavorite(oldSong.isFavorite()); song.setFavorite(oldSong.isFavorite());