Compare commits
21
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee1153b909 | ||
|
|
9e549079b4 | ||
|
|
673660f610 | ||
|
|
1865392a65 | ||
|
|
d52d9f594c | ||
|
|
b1fbe458f8 | ||
|
|
4070ad3a03 | ||
|
|
80c317120e | ||
|
|
c4cea050da | ||
|
|
444887a191 | ||
|
|
9bfc39c0f1 | ||
|
|
9ec443cde0 | ||
|
|
68270f8afb | ||
|
|
b8d7330398 | ||
|
|
c79b419f5e | ||
|
|
ba3f9b9afe | ||
|
|
66e2ae5b93 | ||
|
|
af2296e8ff | ||
|
|
433811531a | ||
|
|
0f5ccfd5ba | ||
|
|
38d775a538 |
@@ -9,6 +9,7 @@ import com.vk.music.common.MusicPlaybackLaunchContext;
|
|||||||
import com.vtosters.lite.R;
|
import com.vtosters.lite.R;
|
||||||
import ru.vtosters.lite.downloaders.AudioDownloader;
|
import ru.vtosters.lite.downloaders.AudioDownloader;
|
||||||
import ru.vtosters.lite.music.cache.MusicCacheImpl;
|
import ru.vtosters.lite.music.cache.MusicCacheImpl;
|
||||||
|
import ru.vtosters.lite.music.cache.delegate.PlaylistCacheDbDelegate;
|
||||||
import ru.vtosters.lite.utils.AndroidUtils;
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
import ru.vtosters.lite.utils.music.VKXUtils;
|
import ru.vtosters.lite.utils.music.VKXUtils;
|
||||||
|
|
||||||
@@ -19,18 +20,29 @@ import static ru.vtosters.hooks.other.Preferences.milkshake;
|
|||||||
import static ru.vtosters.lite.utils.NetworkUtils.isNetworkConnected;
|
import static ru.vtosters.lite.utils.NetworkUtils.isNetworkConnected;
|
||||||
|
|
||||||
public class MusicBottomSheetHook {
|
public class MusicBottomSheetHook {
|
||||||
public static ArrayList<MusicAction> hook(final ArrayList<MusicAction> actions, final MusicTrack musicTrack) {
|
public static ArrayList<MusicAction> hook(ArrayList<MusicAction> actions, MusicTrack musicTrack) {
|
||||||
if (musicTrack.F1()) return actions;
|
if (musicTrack.F1()) return actions;
|
||||||
final var trackId = asId(musicTrack);
|
|
||||||
|
String trackId = asId(musicTrack);
|
||||||
|
|
||||||
if (isVkxInstalled()) actions.add(getPlayInVKXAction());
|
if (isVkxInstalled()) actions.add(getPlayInVKXAction());
|
||||||
|
|
||||||
if (isIntegrationEnabled()) {
|
if (isIntegrationEnabled()) {
|
||||||
if (VKXUtils.isVkxCached(trackId)) actions.add(getRemoveCacheTrackVkxAction());
|
if (VKXUtils.isVkxCached(trackId)) {
|
||||||
else if (isNetworkConnected()) actions.add(addToCacheTrackVkxAction());
|
actions.add(getRemoveCacheTrackVkxAction());
|
||||||
} else {
|
} else if (isNetworkConnected()) {
|
||||||
if (MusicCacheImpl.isCachedTrack(trackId)) actions.add(getRemoveCacheTrackAction());
|
actions.add(addToCacheTrackVkxAction());
|
||||||
else if (isNetworkConnected()) actions.add(addToCacheTrackAction());
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (MusicCacheImpl.isCachedTrack(trackId)) {
|
||||||
|
actions.add(getRemoveCacheTrackAction());
|
||||||
|
} else if (isNetworkConnected()) {
|
||||||
|
actions.add(addToCacheTrackAction());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isNetworkConnected()) actions.add(downloadAsMp3Action());
|
if (isNetworkConnected()) actions.add(downloadAsMp3Action());
|
||||||
|
|
||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,9 +84,13 @@ public class MusicBottomSheetHook {
|
|||||||
} else if (isNetworkConnected()) {
|
} else if (isNetworkConnected()) {
|
||||||
actions.add(addToCacheTrackVkxAction());
|
actions.add(addToCacheTrackVkxAction());
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (PlaylistCacheDbDelegate.isCachedPlaylist(AndroidUtils.getGlobalContext(), playlist.v1())) {
|
||||||
|
actions.add(getRemoveCacheTrackAction());
|
||||||
} else if (isNetworkConnected()) {
|
} else if (isNetworkConnected()) {
|
||||||
actions.add(addToCacheTrackAction());
|
actions.add(addToCacheTrackAction());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isNetworkConnected()) actions.add(downloadAsMp3Action());
|
if (isNetworkConnected()) actions.add(downloadAsMp3Action());
|
||||||
|
|
||||||
@@ -134,10 +150,14 @@ public class MusicBottomSheetHook {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean injectOnClick(int actionId, Playlist playlist) { // playlist inj
|
public static boolean injectOnClick(int actionId, Playlist playlist) { // playlist inj
|
||||||
if (actionId == R.id.play_in_vkx)
|
if (actionId == R.id.play_in_vkx) {
|
||||||
return tryPlayInVKX(null, null, playlist);
|
return tryPlayInVKX(null, null, playlist);
|
||||||
|
}
|
||||||
|
|
||||||
if (actionId == R.id.add_to_cache) {
|
if (actionId == R.id.remove_from_cache) {
|
||||||
|
PlaylistCacheDbDelegate.deletePlaylist(AndroidUtils.getGlobalContext(), playlist.v1());
|
||||||
|
return true;
|
||||||
|
} else if (actionId == R.id.add_to_cache) {
|
||||||
AudioDownloader.cachePlaylist(playlist);
|
AudioDownloader.cachePlaylist(playlist);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -147,7 +167,7 @@ public class MusicBottomSheetHook {
|
|||||||
try {
|
try {
|
||||||
service.deletePlaylistFromCache(playlist.a, playlist.b);
|
service.deletePlaylistFromCache(playlist.a, playlist.b);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
e.printStackTrace();
|
e.fillInStackTrace();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
@@ -156,7 +176,7 @@ public class MusicBottomSheetHook {
|
|||||||
try {
|
try {
|
||||||
service.addPlaylistToCache(playlist.a, playlist.b, (playlist.Q != null) ? playlist.Q : "");
|
service.addPlaylistToCache(playlist.a, playlist.b, (playlist.Q != null) ? playlist.Q : "");
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
e.printStackTrace();
|
e.fillInStackTrace();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
@@ -168,7 +188,7 @@ public class MusicBottomSheetHook {
|
|||||||
try {
|
try {
|
||||||
service.downloadPlaylist(playlist.a, playlist.b, (playlist.Q != null) ? playlist.Q : "");
|
service.downloadPlaylist(playlist.a, playlist.b, (playlist.Q != null) ? playlist.Q : "");
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
e.printStackTrace();
|
e.fillInStackTrace();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import org.json.JSONArray;
|
|||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import ru.vtosters.hooks.other.Preferences;
|
import ru.vtosters.hooks.other.Preferences;
|
||||||
|
import ru.vtosters.lite.music.cache.helpers.PlaylistHelper;
|
||||||
import ru.vtosters.lite.music.cache.MusicCacheImpl;
|
import ru.vtosters.lite.music.cache.MusicCacheImpl;
|
||||||
import ru.vtosters.lite.utils.AccountManagerUtils;
|
import ru.vtosters.lite.utils.AccountManagerUtils;
|
||||||
import ru.vtosters.lite.utils.AndroidUtils;
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
@@ -51,19 +52,13 @@ public class CatalogJsonInjector {
|
|||||||
if (!MusicCacheImpl.isEmpty() && !LibVKXClient.isIntegrationEnabled()) { // inj in playlist list
|
if (!MusicCacheImpl.isEmpty() && !LibVKXClient.isIntegrationEnabled()) { // inj in playlist list
|
||||||
var noPlaylists = !json.has("playlists");
|
var noPlaylists = !json.has("playlists");
|
||||||
|
|
||||||
if (noPlaylists) {
|
PlaylistHelper.addCachedPlaylists(json.optJSONArray("playlists"), noPlaylists);
|
||||||
json.put("playlists", new JSONArray().put(getPlaylist()));
|
|
||||||
Log.d("catalogInjector", "added new pl");
|
|
||||||
} else {
|
|
||||||
json.optJSONArray("playlists").put(getPlaylist());
|
|
||||||
Log.d("catalogInjector", "added to exist pl");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!useOldAppVer || noPlaylists) {
|
if (!useOldAppVer || noPlaylists) {
|
||||||
var newBlocks = new JSONArray();
|
var newBlocks = new JSONArray();
|
||||||
|
|
||||||
newBlocks
|
newBlocks
|
||||||
.put(getCatalogHeader())
|
.put(getCatalogHeader("Кешированные плейлисты"))
|
||||||
.put(getCatalogPlaylist())
|
.put(getCatalogPlaylist())
|
||||||
.put(getCatalogSeparator());
|
.put(getCatalogSeparator());
|
||||||
|
|
||||||
@@ -98,23 +93,13 @@ public class CatalogJsonInjector {
|
|||||||
|
|
||||||
var noPlaylists = !json.has("playlists");
|
var noPlaylists = !json.has("playlists");
|
||||||
|
|
||||||
if (noPlaylists) {
|
PlaylistHelper.addCachedPlaylists(json.optJSONArray("playlists"), noPlaylists);
|
||||||
json.put("playlists", new JSONArray().put(getPlaylist()));
|
|
||||||
Log.d("catalogInjector", "added new pl");
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
json.optJSONArray("playlists").put(getPlaylist());
|
|
||||||
Log.d("catalogInjector", "added to exist pl");
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.d("catalogInjector", e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!useOldAppVer && !noPlaylists) {
|
if (!useOldAppVer && !noPlaylists) {
|
||||||
var newBlocks = new JSONArray();
|
var newBlocks = new JSONArray();
|
||||||
|
|
||||||
newBlocks
|
newBlocks
|
||||||
.put(getCatalogHeader())
|
.put(getCatalogHeader("Кешированные плейлисты"))
|
||||||
.put(getCatalogPlaylist())
|
.put(getCatalogPlaylist())
|
||||||
.put(getCatalogSeparator());
|
.put(getCatalogSeparator());
|
||||||
|
|
||||||
@@ -132,12 +117,15 @@ public class CatalogJsonInjector {
|
|||||||
|
|
||||||
if (type.equals("music_playlists") && j.has("playlists_ids")) {
|
if (type.equals("music_playlists") && j.has("playlists_ids")) {
|
||||||
var newarr = new JSONArray();
|
var newarr = new JSONArray();
|
||||||
var playlists_ids = j.optJSONArray("playlists_ids");
|
var playlists_ids = j.getJSONArray("playlists_ids");
|
||||||
|
var savedPlaylists = PlaylistHelper.getCachedPlaylistsIds();
|
||||||
|
|
||||||
newarr.put(getUserId() + "_-1");
|
for (int n = 0; n < savedPlaylists.length(); n++) {
|
||||||
|
newarr.put(savedPlaylists.getString(n));
|
||||||
|
}
|
||||||
|
|
||||||
for (int n = 0; n < playlists_ids.length(); n++) {
|
for (int n = 0; n < playlists_ids.length(); n++) {
|
||||||
newarr.put(playlists_ids.optString(n));
|
newarr.put(playlists_ids.getString(n));
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d("catalogInjector", "added to pl ids");
|
Log.d("catalogInjector", "added to pl ids");
|
||||||
@@ -220,26 +208,32 @@ public class CatalogJsonInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void removeUnsupportedLayouts(JSONArray blocks) throws JSONException {
|
private static void removeUnsupportedLayouts(JSONArray blocks) throws JSONException {
|
||||||
for (int i = 0; i < blocks.length(); i++) {
|
for (int i = blocks.length() - 1; i >= 0; i--) {
|
||||||
var block = blocks.optJSONObject(i);
|
JSONObject block = blocks.optJSONObject(i);
|
||||||
var layout = block.optJSONObject("layout");
|
JSONObject layout = block.optJSONObject("layout");
|
||||||
var data_type = block.optString("data_type");
|
String data_type = block.optString("data_type");
|
||||||
|
|
||||||
if (Objects.equals(data_type, "music_recommended_playlists")) {
|
if (Objects.equals(data_type, "music_recommended_playlists") || Objects.equals(data_type, "radiostations")) {
|
||||||
blocks.remove(i);
|
blocks.remove(i); // block
|
||||||
blocks.remove(i - 1);
|
if (i - 1 >= 0) blocks.remove(i - 1); // text
|
||||||
blocks.remove(i - 2);
|
if (i - 2 >= 0) blocks.remove(i - 2); // divider
|
||||||
|
continue; // Skip to the next iteration after removing
|
||||||
}
|
}
|
||||||
|
|
||||||
if (layout != null) {
|
if (layout == null) continue; // Skip to the next iteration if layout is null
|
||||||
var name = layout.optString("name");
|
|
||||||
if (name.equals("header_extended")) {
|
String name = layout.optString("name");
|
||||||
if (layout.has("top_title")) blocks.remove(i);
|
if (!name.equals("header_extended")) {
|
||||||
|
continue; // Skip to the next iteration if not header_extended
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layout.has("top_title")) {
|
||||||
|
blocks.remove(i);
|
||||||
|
}
|
||||||
layout.put("name", "header");
|
layout.put("name", "header");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void setDefaultAudioPage(JSONArray jsonArray, JSONObject catalog) throws JSONException {
|
private static void setDefaultAudioPage(JSONArray jsonArray, JSONObject catalog) throws JSONException {
|
||||||
for (int i = 0; i < jsonArray.length(); i++) {
|
for (int i = 0; i < jsonArray.length(); i++) {
|
||||||
@@ -273,7 +267,9 @@ public class CatalogJsonInjector {
|
|||||||
+ "&access_token="
|
+ "&access_token="
|
||||||
+ AccountManagerUtils.getUserToken())
|
+ AccountManagerUtils.getUserToken())
|
||||||
.a("Accept-Encoding", "gzip")
|
.a("Accept-Encoding", "gzip")
|
||||||
.a(Headers.a("User-Agent", Network.l.c().a(), "Content-Type", "application/x-www-form-urlencoded; charset=utf-8")).a();
|
.a("User-Agent", Network.l.c().a())
|
||||||
|
.a("Content-Type", "application/x-www-form-urlencoded; charset=utf-8")
|
||||||
|
.a();
|
||||||
try (Response resp = Network.b(CLIENT_API).a(request).execute()) {
|
try (Response resp = Network.b(CLIENT_API).a(request).execute()) {
|
||||||
var json = new JSONObject(GzipDecompressor.decompressResponse(resp)).getJSONObject("response");
|
var json = new JSONObject(GzipDecompressor.decompressResponse(resp)).getJSONObject("response");
|
||||||
var catalogarr = json.optJSONObject("catalog").optJSONArray("sections").optJSONObject(0);
|
var catalogarr = json.optJSONObject("catalog").optJSONArray("sections").optJSONObject(0);
|
||||||
|
|||||||
@@ -18,8 +18,12 @@ import ru.vtosters.lite.music.cache.helpers.TracklistHelper;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
public class PlaylistInjector {
|
public class PlaylistInjector {
|
||||||
|
private static final ExecutorService executor = Executors.newCachedThreadPool();
|
||||||
public final static String CHANNEL_NAME = "VTCH";
|
public final static String CHANNEL_NAME = "VTCH";
|
||||||
|
|
||||||
public static void injectDownloadPlaylist(Playlist playlist) {
|
public static void injectDownloadPlaylist(Playlist playlist) {
|
||||||
@@ -32,7 +36,7 @@ public class PlaylistInjector {
|
|||||||
|
|
||||||
public static Observable<AudioGetPlaylist.c> injectGetPlaylist(AudioGetPlaylist audioGetPlaylist) {
|
public static Observable<AudioGetPlaylist.c> injectGetPlaylist(AudioGetPlaylist audioGetPlaylist) {
|
||||||
try {
|
try {
|
||||||
if (!eligibleForOfflineCaching())
|
if (MusicCacheImpl.isEmpty())
|
||||||
return null;
|
return null;
|
||||||
var requestArgs = audioGetPlaylist.b();
|
var requestArgs = audioGetPlaylist.b();
|
||||||
|
|
||||||
@@ -42,8 +46,6 @@ public class PlaylistInjector {
|
|||||||
boolean isVirtualPlaylist = accessKey != null && (accessKey.equals("cache"));
|
boolean isVirtualPlaylist = accessKey != null && (accessKey.equals("cache"));
|
||||||
boolean isAlbumVirtualPlaylist = accessKey != null && (accessKey.equals("cacheAlbum"));
|
boolean isAlbumVirtualPlaylist = accessKey != null && (accessKey.equals("cacheAlbum"));
|
||||||
|
|
||||||
Log.d("PlaylistInjector", "id = " + id + " / owner = " + ownerId + " / access = " + accessKey + " / " + isVirtualPlaylist + " + " + isAlbumVirtualPlaylist);
|
|
||||||
|
|
||||||
if (TextUtils.isEmpty(id) || (!isVirtualPlaylist && !isAlbumVirtualPlaylist))
|
if (TextUtils.isEmpty(id) || (!isVirtualPlaylist && !isAlbumVirtualPlaylist))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@@ -70,7 +72,7 @@ public class PlaylistInjector {
|
|||||||
|
|
||||||
response.c = tracks;
|
response.c = tracks;
|
||||||
} catch (RemoteException | JSONException e) {
|
} catch (RemoteException | JSONException e) {
|
||||||
e.printStackTrace();
|
e.fillInStackTrace();
|
||||||
response.c = new ArrayList<>();
|
response.c = new ArrayList<>();
|
||||||
}
|
}
|
||||||
observableEmitter.b(response);
|
observableEmitter.b(response);
|
||||||
@@ -79,18 +81,20 @@ public class PlaylistInjector {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isVirtualPlaylist) {
|
response.c = (ArrayList<MusicTrack>) MusicCacheImpl.getPlaylistSongs(ownerId, id);
|
||||||
response.c = (ArrayList<MusicTrack>) TracklistHelper.getMyCachedMusicTracks();
|
|
||||||
|
Log.d("Playlist", "Open playlist " + ownerId + "_" + id);
|
||||||
|
|
||||||
|
if (Objects.equals(id, "-1")) {
|
||||||
response.b = PlaylistHelper.createCachedPlaylistMetadata();
|
response.b = PlaylistHelper.createCachedPlaylistMetadata();
|
||||||
} else {
|
} else {
|
||||||
response.c = (ArrayList<MusicTrack>) TracklistHelper.getTracks(requestArgs.get("owner_id"));
|
response.b = MusicCacheImpl.getPlaylist(id, ownerId);
|
||||||
response.b = PlaylistHelper.createCachedPlaylistMetadata(requestArgs.get("owner_id"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
observableEmitter.b(response);
|
observableEmitter.b(response);
|
||||||
});
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.fillInStackTrace();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import org.json.JSONArray;
|
|||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import ru.vtosters.lite.music.cache.MusicCacheImpl;
|
import ru.vtosters.lite.music.cache.MusicCacheImpl;
|
||||||
|
import ru.vtosters.lite.music.cache.delegate.PlaylistCacheDbDelegate;
|
||||||
import ru.vtosters.lite.music.cache.helpers.PlaylistHelper;
|
import ru.vtosters.lite.music.cache.helpers.PlaylistHelper;
|
||||||
import ru.vtosters.lite.music.cache.helpers.TracklistHelper;
|
import ru.vtosters.lite.music.cache.helpers.TracklistHelper;
|
||||||
import ru.vtosters.lite.utils.AccountManagerUtils;
|
import ru.vtosters.lite.utils.AccountManagerUtils;
|
||||||
@@ -22,6 +23,8 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static ru.vtosters.lite.music.cache.helpers.PlaylistHelper.*;
|
||||||
|
|
||||||
public class TracklistInjector {
|
public class TracklistInjector {
|
||||||
|
|
||||||
public static boolean eligibleForOfflineCaching() {
|
public static boolean eligibleForOfflineCaching() {
|
||||||
@@ -59,7 +62,9 @@ public class TracklistInjector {
|
|||||||
if (MusicCacheImpl.isEmpty()) {
|
if (MusicCacheImpl.isEmpty()) {
|
||||||
observableEmitter.b(parser.c(getEmptyCatalog()));
|
observableEmitter.b(parser.c(getEmptyCatalog()));
|
||||||
} else {
|
} else {
|
||||||
observableEmitter.b(parser.c(createVirtualCatalog(TracklistHelper.getTracks())));
|
observableEmitter.b(parser.c(createVirtualCatalog(
|
||||||
|
PlaylistCacheDbDelegate.getTracksInPlaylist(AndroidUtils.getGlobalContext(),
|
||||||
|
AccountManagerUtils.getUserId() + "_-1"))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -162,16 +167,12 @@ public class TracklistInjector {
|
|||||||
boolean noPlaylists = hasNoPlaylists(blocks);
|
boolean noPlaylists = hasNoPlaylists(blocks);
|
||||||
|
|
||||||
if (noPlaylists) {
|
if (noPlaylists) {
|
||||||
catalogNode.put("playlists", new JSONArray().put(PlaylistHelper.getPlaylist()));
|
PlaylistHelper.addCachedPlaylists(catalogNode.optJSONArray("playlists"), true);
|
||||||
|
|
||||||
JSONArray newBlocks = createNewBlocks(blocks);
|
firstSection.put("blocks", createNewBlocks(blocks));
|
||||||
|
|
||||||
Log.d("catalogInjector", "added cache catalog playlist");
|
|
||||||
|
|
||||||
firstSection.put("blocks", newBlocks);
|
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
catalogNode.optJSONArray("playlists").put(PlaylistHelper.getPlaylist());
|
PlaylistHelper.addCachedPlaylists(catalogNode.optJSONArray("playlists"), false);
|
||||||
|
|
||||||
updatePlaylistsIds(blocks);
|
updatePlaylistsIds(blocks);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -188,7 +189,7 @@ public class TracklistInjector {
|
|||||||
return firstSection.getString("url").equals("https://vk.com/audios" + AccountManagerUtils.getUserId() + "?section=all");
|
return firstSection.getString("url").equals("https://vk.com/audios" + AccountManagerUtils.getUserId() + "?section=all");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hasNoPlaylists(JSONArray blocks) throws JSONException {
|
private static boolean hasNoPlaylists(JSONArray blocks) {
|
||||||
return blocks.optJSONObject(0).optJSONArray("buttons").optJSONObject(0).optJSONObject("action").optString("type").equals("create_playlist");
|
return blocks.optJSONObject(0).optJSONArray("buttons").optJSONObject(0).optJSONObject("action").optString("type").equals("create_playlist");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,13 +197,14 @@ public class TracklistInjector {
|
|||||||
JSONArray newBlocks = new JSONArray();
|
JSONArray newBlocks = new JSONArray();
|
||||||
|
|
||||||
newBlocks
|
newBlocks
|
||||||
.put(PlaylistHelper.getCatalogHeader())
|
.put(PlaylistHelper.getCatalogHeader(AndroidUtils.getString(R.string.cached_tracks_title)))
|
||||||
.put(PlaylistHelper.getCatalogPlaylist())
|
.put(PlaylistHelper.getCatalogPlaylist())
|
||||||
.put(PlaylistHelper.getCatalogSeparator());
|
.put(PlaylistHelper.getCatalogSeparator());
|
||||||
|
|
||||||
for (int i = 0; i < blocks.length(); i++) {
|
for (int i = 0; i < blocks.length(); i++) {
|
||||||
newBlocks.put(blocks.optJSONObject(i));
|
newBlocks.put(blocks.optJSONObject(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
return newBlocks;
|
return newBlocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,16 +214,15 @@ public class TracklistInjector {
|
|||||||
String type = j.optString("data_type");
|
String type = j.optString("data_type");
|
||||||
|
|
||||||
if (type.equals("music_playlists") && j.has("playlists_ids")) {
|
if (type.equals("music_playlists") && j.has("playlists_ids")) {
|
||||||
JSONArray newarr = new JSONArray();
|
JSONArray newarr = PlaylistHelper.getCachedPlaylistsIds();
|
||||||
JSONArray playlists_ids = j.optJSONArray("playlists_ids");
|
JSONArray playlists_ids = j.optJSONArray("playlists_ids");
|
||||||
|
|
||||||
newarr.put(AccountManagerUtils.getUserId() + "_-1");
|
|
||||||
|
|
||||||
for (int n = 0; n < playlists_ids.length(); n++) {
|
for (int n = 0; n < playlists_ids.length(); n++) {
|
||||||
newarr.put(playlists_ids.optString(n));
|
newarr.put(playlists_ids.optString(n));
|
||||||
}
|
}
|
||||||
|
|
||||||
j.put("playlists_ids", newarr);
|
j.put("playlists_ids", newarr);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -240,7 +241,19 @@ public class TracklistInjector {
|
|||||||
|
|
||||||
audiosBlock.put("layout", layout);
|
audiosBlock.put("layout", layout);
|
||||||
|
|
||||||
var blocks = new JSONArray().put(getShuffleButton()).put(audiosBlock);
|
var blocks = new JSONArray();
|
||||||
|
|
||||||
|
if (MusicCacheImpl.hasPlaylist()) {
|
||||||
|
blocks.put(getCatalogHeader("Кешированные плейлисты"))
|
||||||
|
.put(getCatalogPlaylist());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!MusicCacheImpl.getAllOwnTracks().isEmpty()) {
|
||||||
|
if (MusicCacheImpl.hasPlaylist()) blocks.put(getCatalogSeparator());
|
||||||
|
blocks.put(getCatalogHeader(AndroidUtils.getString(R.string.cached_tracks_title)))
|
||||||
|
.put(getShuffleButton())
|
||||||
|
.put(audiosBlock);
|
||||||
|
}
|
||||||
|
|
||||||
var randomId = getRandomId();
|
var randomId = getRandomId();
|
||||||
|
|
||||||
@@ -259,6 +272,7 @@ public class TracklistInjector {
|
|||||||
|
|
||||||
return new JSONObject()
|
return new JSONObject()
|
||||||
.put("catalog", catalog)
|
.put("catalog", catalog)
|
||||||
|
.put("playlists", addCachedPlaylists(new JSONArray(), false))
|
||||||
.put("audios", TracklistHelper.tracksToJsons(tracks));
|
.put("audios", TracklistHelper.tracksToJsons(tracks));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,35 @@
|
|||||||
package ru.vtosters.lite.downloaders;
|
package ru.vtosters.lite.downloaders;
|
||||||
|
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
|
import android.util.Log;
|
||||||
|
import androidx.core.app.NotificationCompat;
|
||||||
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
|
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
|
||||||
import com.vk.core.util.ToastUtils;
|
import com.vk.core.util.ToastUtils;
|
||||||
import com.vk.dto.music.MusicTrack;
|
import com.vk.dto.music.MusicTrack;
|
||||||
import com.vk.dto.music.Playlist;
|
import com.vk.dto.music.Playlist;
|
||||||
import com.vtosters.lite.R;
|
import com.vtosters.lite.R;
|
||||||
|
|
||||||
import java8.util.concurrent.CompletableFuture;
|
import org.json.JSONObject;
|
||||||
import ru.vtosters.hooks.music.MusicCacheFilesHook;
|
import ru.vtosters.hooks.music.MusicCacheFilesHook;
|
||||||
import ru.vtosters.hooks.other.Preferences;
|
import ru.vtosters.hooks.other.Preferences;
|
||||||
import ru.vtosters.lite.music.cache.MusicCacheImpl;
|
import ru.vtosters.lite.music.cache.MusicCacheImpl;
|
||||||
|
import ru.vtosters.lite.music.cache.delegate.PlaylistCacheDbDelegate;
|
||||||
|
import ru.vtosters.lite.music.cache.helpers.PlaylistHelper;
|
||||||
import ru.vtosters.lite.music.callback.MusicCallbackBuilder;
|
import ru.vtosters.lite.music.callback.MusicCallbackBuilder;
|
||||||
import ru.vtosters.lite.music.converter.playlist.PlaylistConverter;
|
import ru.vtosters.lite.music.converter.playlist.PlaylistConverter;
|
||||||
import ru.vtosters.lite.music.downloader.AudioGet;
|
import ru.vtosters.lite.music.downloader.AudioGet;
|
||||||
import ru.vtosters.lite.music.downloader.PlaylistDownloader;
|
import ru.vtosters.lite.music.downloader.PlaylistDownloader;
|
||||||
|
import ru.vtosters.lite.music.downloader.ThumbnailPlaylistDownloader;
|
||||||
import ru.vtosters.lite.music.downloader.TrackDownloader;
|
import ru.vtosters.lite.music.downloader.TrackDownloader;
|
||||||
|
import ru.vtosters.lite.music.interfaces.Callback;
|
||||||
import ru.vtosters.lite.music.notification.MusicNotificationBuilder;
|
import ru.vtosters.lite.music.notification.MusicNotificationBuilder;
|
||||||
import ru.vtosters.lite.utils.AccountManagerUtils;
|
import ru.vtosters.lite.utils.AccountManagerUtils;
|
||||||
import ru.vtosters.lite.utils.AndroidUtils;
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
import ru.vtosters.lite.utils.IOUtils;
|
import ru.vtosters.lite.utils.IOUtils;
|
||||||
|
import ru.vtosters.lite.utils.music.PlaylistUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
||||||
@@ -35,18 +43,18 @@ import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
|||||||
public class AudioDownloader {
|
public class AudioDownloader {
|
||||||
public static final String dlpath = Preferences.getBoolValue("dldir", false) ? Environment.DIRECTORY_DOWNLOADS : Environment.DIRECTORY_MUSIC;
|
public static final String dlpath = Preferences.getBoolValue("dldir", false) ? Environment.DIRECTORY_DOWNLOADS : Environment.DIRECTORY_MUSIC;
|
||||||
|
|
||||||
public static CompletableFuture<Void> downloadPlaylist(Playlist playlist) {
|
public static void downloadPlaylist(Playlist playlist) {
|
||||||
var tracks = PlaylistConverter.getPlaylist(playlist);
|
List<MusicTrack> tracks = PlaylistConverter.getPlaylist(playlist);
|
||||||
|
|
||||||
var playlistName = IOUtils.getValidFileName(playlist.g);
|
String playlistName = IOUtils.getValidFileName(playlist.g);
|
||||||
|
|
||||||
var musicPath = Environment.getExternalStoragePublicDirectory(dlpath).getAbsolutePath();
|
String musicPath = Environment.getExternalStoragePublicDirectory(dlpath).getAbsolutePath();
|
||||||
var downloadPath = musicPath + File.separator + playlistName;
|
String downloadPath = musicPath + File.separator + playlistName;
|
||||||
|
|
||||||
var notificationId = playlistName.hashCode();
|
int notificationId = playlistName.hashCode();
|
||||||
var notification = MusicNotificationBuilder.buildPlaylistDownloadNotification(playlistName, notificationId);
|
NotificationCompat.Builder notification = MusicNotificationBuilder.buildPlaylistDownloadNotification(playlistName, notificationId);
|
||||||
|
|
||||||
return PlaylistDownloader.downloadPlaylist(
|
PlaylistDownloader.downloadPlaylist(
|
||||||
tracks,
|
tracks,
|
||||||
IOUtils.getValidFileName(playlist.g),
|
IOUtils.getValidFileName(playlist.g),
|
||||||
downloadPath,
|
downloadPath,
|
||||||
@@ -60,53 +68,96 @@ public class AudioDownloader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void cacheTrack(MusicTrack track) {
|
public static void cacheTrack(MusicTrack track) {
|
||||||
var trackId = LibVKXClient.asId(track);
|
String trackId = LibVKXClient.asId(track);
|
||||||
|
|
||||||
if (MusicCacheImpl.isCachedTrack(trackId)) {
|
if (MusicCacheImpl.isCachedTrack(trackId)) {
|
||||||
MusicCacheImpl.removeTrack(trackId);
|
MusicCacheImpl.removeTrack(trackId);
|
||||||
AndroidUtils.sendToast(AndroidUtils.getString("audio_deleted_from_cache"));
|
AndroidUtils.sendToast(AndroidUtils.getString("audio_deleted_from_cache"));
|
||||||
} else {
|
} else {
|
||||||
var trackFile = MusicCacheFilesHook.getTrackFile(trackId);
|
File trackFile = MusicCacheFilesHook.getTrackFile(trackId);
|
||||||
if (!trackFile.exists())
|
if (!trackFile.exists()) {
|
||||||
trackFile.getParentFile().mkdirs();
|
trackFile.getParentFile().mkdirs();
|
||||||
|
}
|
||||||
downloadM3U8(track, true);
|
downloadM3U8(track, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CompletableFuture<Void> cachePlaylist(Playlist playlist) {
|
public static void cachePlaylist(Playlist playlist) {
|
||||||
var tracks = PlaylistConverter.getPlaylist(playlist);
|
List<MusicTrack> tracks = PlaylistConverter.getPlaylist(playlist);
|
||||||
|
|
||||||
var notificationId = playlist.g.hashCode();
|
int notificationId = playlist.g.hashCode();
|
||||||
var notification = MusicNotificationBuilder.buildPlaylistDownloadNotification(playlist.g, notificationId);
|
NotificationCompat.Builder notification = MusicNotificationBuilder.buildPlaylistDownloadNotification(playlist.g, notificationId);
|
||||||
|
|
||||||
return PlaylistDownloader.cachePlaylist(
|
JSONObject thumbs = PlaylistUtils.getThumb(playlist);
|
||||||
|
|
||||||
|
if (thumbs != null) {
|
||||||
|
new ThumbnailPlaylistDownloader().download(null, new Callback() {
|
||||||
|
@Override
|
||||||
|
public void onProgress(int progress) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess() {
|
||||||
|
PlaylistCacheDbDelegate.addPlaylist(AndroidUtils.getGlobalContext(), playlist);
|
||||||
|
|
||||||
|
Log.d("Playlist", "adding to cache with thumbs " + playlist.v1());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSizeReceived(long size, long header) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}, playlist);
|
||||||
|
} else {
|
||||||
|
PlaylistCacheDbDelegate.addPlaylist(AndroidUtils.getGlobalContext(), playlist);
|
||||||
|
|
||||||
|
Log.d("Playlist", "adding to cache without thumbs " + playlist.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
PlaylistDownloader.cachePlaylist(
|
||||||
tracks,
|
tracks,
|
||||||
MusicCallbackBuilder.buildPlaylistCallback(tracks.size(), notification, notificationId)
|
MusicCallbackBuilder.buildPlaylistCallback(tracks.size(), notification, notificationId),
|
||||||
|
playlist
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void cacheAllAudios() {
|
public static void cacheAllAudios() {
|
||||||
var tracks = AudioGet.getAudios();
|
List<MusicTrack> tracks = AudioGet.getAudios();
|
||||||
|
|
||||||
var notificationId = AccountManagerUtils.getUserId();
|
int notificationId = AccountManagerUtils.getUserId();
|
||||||
var notification = MusicNotificationBuilder.buildAllAudiosDownloadNotification(notificationId);
|
NotificationCompat.Builder notification = MusicNotificationBuilder.buildAllAudiosDownloadNotification(notificationId);
|
||||||
|
|
||||||
|
if (tracks == null || tracks.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
PlaylistDownloader.cachePlaylist(
|
PlaylistDownloader.cachePlaylist(
|
||||||
tracks,
|
tracks,
|
||||||
MusicCallbackBuilder.buildPlaylistCallback(tracks.size(), notification, notificationId)
|
MusicCallbackBuilder.buildPlaylistCallback(tracks.size(), notification, notificationId),
|
||||||
|
PlaylistHelper.createCachedPlaylistMetadata()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void downloadAllAudios() {
|
public static void downloadAllAudios() {
|
||||||
var tracks = AudioGet.getAudios();
|
List<MusicTrack> tracks = AudioGet.getAudios();
|
||||||
|
|
||||||
var notificationId = AccountManagerUtils.getUserId();
|
int notificationId = AccountManagerUtils.getUserId();
|
||||||
var notification = MusicNotificationBuilder.buildAllAudiosDownloadNotification(notificationId);
|
NotificationCompat.Builder notification = MusicNotificationBuilder.buildAllAudiosDownloadNotification(notificationId);
|
||||||
|
|
||||||
var playlistName = "Audios of " + AccountManagerUtils.getUserId();
|
String playlistName = "Audios of " + AccountManagerUtils.getUserId();
|
||||||
|
|
||||||
var musicPath = Environment.getExternalStoragePublicDirectory(dlpath).getAbsolutePath();
|
String musicPath = Environment.getExternalStoragePublicDirectory(dlpath).getAbsolutePath();
|
||||||
var downloadPath = musicPath + File.separator + playlistName;
|
String downloadPath = musicPath + File.separator + playlistName;
|
||||||
|
|
||||||
|
if (tracks == null || tracks.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
PlaylistDownloader.downloadPlaylist(
|
PlaylistDownloader.downloadPlaylist(
|
||||||
tracks,
|
tracks,
|
||||||
@@ -116,21 +167,21 @@ public class AudioDownloader {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void
|
private static void downloadM3U8(MusicTrack track, boolean cache) {
|
||||||
downloadM3U8(MusicTrack track, boolean cache) {
|
|
||||||
Objects.requireNonNull(track.D, () -> {
|
Objects.requireNonNull(track.D, () -> {
|
||||||
ToastUtils.a(getString(R.string.link_audio_error));
|
ToastUtils.a(getString(R.string.link_audio_error));
|
||||||
return "link must not be null";
|
return "link must not be null";
|
||||||
});
|
});
|
||||||
var musicPath = Environment.getExternalStoragePublicDirectory(dlpath).getAbsolutePath();
|
String musicPath = Environment.getExternalStoragePublicDirectory(dlpath).getAbsolutePath();
|
||||||
var tempId = track.d;
|
int tempId = track.d;
|
||||||
var downloadPath = musicPath + File.separator;
|
String downloadPath = musicPath + File.separator;
|
||||||
var notification = MusicNotificationBuilder.buildDownloadNotification(track, tempId);
|
NotificationCompat.Builder notification = MusicNotificationBuilder.buildDownloadNotification(track, tempId);
|
||||||
|
|
||||||
var cb = MusicCallbackBuilder.buildOneTrackCallback(tempId, notification);
|
Callback cb = MusicCallbackBuilder.buildOneTrackCallback(tempId, notification);
|
||||||
if (cache)
|
if (cache) {
|
||||||
TrackDownloader.cacheTrack(track, cb);
|
TrackDownloader.cacheTrack(track, cb, PlaylistHelper.createCachedPlaylistMetadata());
|
||||||
else
|
} else {
|
||||||
TrackDownloader.downloadTrack(track, downloadPath, cb);
|
TrackDownloader.downloadTrack(track, downloadPath, cb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,10 @@ import java.util.List;
|
|||||||
import bruhcollective.itaysonlab.libvkx.ILibVkxService;
|
import bruhcollective.itaysonlab.libvkx.ILibVkxService;
|
||||||
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
|
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
|
||||||
import bruhcollective.itaysonlab.libvkx.client.LibVKXClientImpl;
|
import bruhcollective.itaysonlab.libvkx.client.LibVKXClientImpl;
|
||||||
|
import com.vk.dto.music.Playlist;
|
||||||
|
import ru.vtosters.lite.music.cache.delegate.MusicCacheDbDelegate;
|
||||||
|
import ru.vtosters.lite.music.cache.delegate.PlaylistCacheDbDelegate;
|
||||||
|
import ru.vtosters.lite.utils.AccountManagerUtils;
|
||||||
import ru.vtosters.lite.utils.AndroidUtils;
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
|
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
|
||||||
import ru.vtosters.lite.utils.music.MusicTrackUtils;
|
import ru.vtosters.lite.utils.music.MusicTrackUtils;
|
||||||
@@ -34,20 +38,28 @@ public class MusicCacheImpl {
|
|||||||
MusicCacheStorageUtils.removeTrackDirById(trackId);
|
MusicCacheStorageUtils.removeTrackDirById(trackId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<MusicTrack> getAllTracks() {
|
public static List<MusicTrack> getAllOwnTracks() {
|
||||||
return MusicCacheDbDelegate.getAllTracks(AndroidUtils.getGlobalContext());
|
return PlaylistCacheDbDelegate.getTracksInPlaylist(AndroidUtils.getGlobalContext(), AccountManagerUtils.getUserId() + "_-1");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<MusicTrack> getAlbumById(String albumId) {
|
public static List<MusicTrack> getPlaylistSongs(String owner_id, String playlist_id) {
|
||||||
return MusicCacheDbDelegate.getAlbumById(AndroidUtils.getGlobalContext(), albumId);
|
return PlaylistCacheDbDelegate.getTracksInPlaylist(AndroidUtils.getGlobalContext(), owner_id + "_" + playlist_id); // get songs from playlist
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<MusicTrack> getFirstAlbumTrack(String albumId) {
|
public static Playlist getPlaylist(String playlist_id, String owner_id) {
|
||||||
return MusicCacheDbDelegate.getFirstAlbumTrack(AndroidUtils.getGlobalContext(), albumId);
|
return getPlaylistById(owner_id + "_" + playlist_id); // get playlist from db
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<MusicTrack> getPlaylist() {
|
public static List<Playlist> getPlaylists() {
|
||||||
return MusicCacheDbDelegate.getPlaylist(AndroidUtils.getGlobalContext());
|
return PlaylistCacheDbDelegate.getAllPlaylists(AndroidUtils.getGlobalContext()); // get all cached playlists
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Playlist getPlaylistById(String query) {
|
||||||
|
return PlaylistCacheDbDelegate.getPlaylistById(AndroidUtils.getGlobalContext(), query); // get playlist from db
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasPlaylist() {
|
||||||
|
return PlaylistCacheDbDelegate.getPlaylistsCount(AndroidUtils.getGlobalContext()) != 0; // has playlists or not
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long getTracksCount() {
|
public static long getTracksCount() {
|
||||||
@@ -60,7 +72,7 @@ public class MusicCacheImpl {
|
|||||||
try {
|
try {
|
||||||
return (long) service.getCache().size();
|
return (long) service.getCache().size();
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
e.printStackTrace();
|
e.fillInStackTrace();
|
||||||
return defaultValue();
|
return defaultValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,6 +94,7 @@ public class MusicCacheImpl {
|
|||||||
|
|
||||||
public static void clear() {
|
public static void clear() {
|
||||||
MusicCacheDbDelegate.drop(AndroidUtils.getGlobalContext());
|
MusicCacheDbDelegate.drop(AndroidUtils.getGlobalContext());
|
||||||
|
PlaylistCacheDbDelegate.drop(AndroidUtils.getGlobalContext());
|
||||||
MusicCacheStorageUtils.clear();
|
MusicCacheStorageUtils.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24,6 +24,8 @@ import java.net.URLDecoder;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import ru.vtosters.lite.music.cache.delegate.PlaylistCacheDbDelegate;
|
||||||
|
import ru.vtosters.lite.utils.AccountManagerUtils;
|
||||||
import ru.vtosters.lite.utils.AndroidUtils;
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
|
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
|
||||||
import ru.vtosters.lite.utils.music.MusicTrackUtils;
|
import ru.vtosters.lite.utils.music.MusicTrackUtils;
|
||||||
@@ -127,6 +129,8 @@ public class MusicCacheDb extends SQLiteOpenHelper implements AutoCloseable { //
|
|||||||
vals.put(Constants.COLUMN_DURATION, track.h);
|
vals.put(Constants.COLUMN_DURATION, track.h);
|
||||||
vals.put(Constants.COLUMN_HAS_ARTWORK, track.I != null && track.I.u1() != null);
|
vals.put(Constants.COLUMN_HAS_ARTWORK, track.I != null && track.I.u1() != null);
|
||||||
db.insert(Constants.TABLE_NAME, null, vals);
|
db.insert(Constants.TABLE_NAME, null, vals);
|
||||||
|
|
||||||
|
PlaylistCacheDbDelegate.addTrackToPlaylist(AndroidUtils.getGlobalContext(), AccountManagerUtils.getUserId() + "_-1", track.y1());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,18 +174,21 @@ public class MusicCacheDb extends SQLiteOpenHelper implements AutoCloseable { //
|
|||||||
return DatabaseUtils.queryNumEntries(getReadableDatabase(), Constants.TABLE_NAME);
|
return DatabaseUtils.queryNumEntries(getReadableDatabase(), Constants.TABLE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MusicTrack getTrackById(String trackId) {
|
||||||
|
SQLiteDatabase db = getReadableDatabase();
|
||||||
|
Cursor cursor = db.query(Constants.TABLE_NAME, null, Constants.COLUMN_TRACK_ID + " = ?", new String[]{trackId}, null, null, null);
|
||||||
|
MusicTrack track = null;
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
track = fromCursor(cursor);
|
||||||
|
}
|
||||||
|
cursor.close();
|
||||||
|
return track;
|
||||||
|
}
|
||||||
|
|
||||||
public List<MusicTrack> getAllTracks() {
|
public List<MusicTrack> getAllTracks() {
|
||||||
return getTracksWithCursor(getReadableDatabase().query(Constants.TABLE_NAME, null, null, null, null, null, null));
|
return getTracksWithCursor(getReadableDatabase().query(Constants.TABLE_NAME, null, null, null, null, null, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<MusicTrack> getAlbum(String albumId) {
|
|
||||||
return getAlbumTracks(albumId, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<MusicTrack> getFirstAlbumTrack(String albumId) {
|
|
||||||
return getAlbumTracks(albumId, "0,1");
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<MusicTrack> getAlbumTracks(String albumId, @Nullable String limit) {
|
private List<MusicTrack> getAlbumTracks(String albumId, @Nullable String limit) {
|
||||||
return getTracksWithCursor(getReadableDatabase().query(
|
return getTracksWithCursor(getReadableDatabase().query(
|
||||||
Constants.TABLE_NAME,
|
Constants.TABLE_NAME,
|
||||||
@@ -220,7 +227,7 @@ public class MusicCacheDb extends SQLiteOpenHelper implements AutoCloseable { //
|
|||||||
|
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
public @interface Constants {
|
public @interface Constants {
|
||||||
int DV_VERSION = 0x2;
|
int DV_VERSION = 0x3;
|
||||||
String DB_NAME = "vt_lite_cache.db";
|
String DB_NAME = "vt_lite_cache.db";
|
||||||
String TABLE_NAME = "tracks";
|
String TABLE_NAME = "tracks";
|
||||||
//columns
|
//columns
|
||||||
|
|||||||
@@ -0,0 +1,254 @@
|
|||||||
|
package ru.vtosters.lite.music.cache.db;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.DatabaseUtils;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
import android.util.Log;
|
||||||
|
import com.vk.dto.music.MusicTrack;
|
||||||
|
import com.vk.dto.music.Playlist;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import ru.vtosters.lite.music.cache.MusicCacheImpl;
|
||||||
|
import ru.vtosters.lite.music.cache.helpers.PlaylistHelper;
|
||||||
|
import ru.vtosters.lite.music.cache.helpers.TracklistHelper;
|
||||||
|
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static ru.vtosters.lite.utils.music.MusicCacheStorageUtils.*;
|
||||||
|
|
||||||
|
public class PlaylistCacheDb extends SQLiteOpenHelper implements AutoCloseable {
|
||||||
|
public PlaylistCacheDb(Context context) {
|
||||||
|
super(context, Constants.DB_NAME, null, Constants.DV_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(SQLiteDatabase db) {
|
||||||
|
db.execSQL(Constants.CREATE_QUERY);
|
||||||
|
db.execSQL(Constants.CREATE_PLAYLIST_TRACKS_QUERY);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Playlist fromCursor(Cursor cur) throws JSONException {
|
||||||
|
@SuppressLint("Range")
|
||||||
|
String photoString = cur.getString(cur.getColumnIndex(Constants.COLUMN_PHOTO));
|
||||||
|
JSONObject photo = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
photo = new JSONObject(photoString);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
// ok and
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("Range")
|
||||||
|
JSONObject playlist = PlaylistHelper.generatePlaylist(
|
||||||
|
cur.getInt(cur.getColumnIndex(Constants.COLUMN_ID)),
|
||||||
|
cur.getInt(cur.getColumnIndex(Constants.COLUMN_OWNER_ID)),
|
||||||
|
Boolean.parseBoolean(cur.getString(cur.getColumnIndex(Constants.COLUMN_IS_EXPLICIT))),
|
||||||
|
cur.getString(cur.getColumnIndex(Constants.COLUMN_TITLE)),
|
||||||
|
cur.getString(cur.getColumnIndex(Constants.COLUMN_DESCRIPTION)),
|
||||||
|
photo,
|
||||||
|
0);
|
||||||
|
|
||||||
|
Log.d("Playlist", "generated " + Playlist.U.a(playlist).v1());
|
||||||
|
|
||||||
|
return Playlist.U.a(playlist);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
|
db.execSQL(Constants.DROP_QUERY);
|
||||||
|
onCreate(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPlaylist(int id, int ownerId, boolean isExplicit, String title, String description, JSONObject photo) {
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(Constants.COLUMN_ID, id);
|
||||||
|
values.put(Constants.COLUMN_OWNER_ID, ownerId);
|
||||||
|
values.put(Constants.COLUMN_IS_EXPLICIT, isExplicit);
|
||||||
|
values.put(Constants.COLUMN_TITLE, title);
|
||||||
|
values.put(Constants.COLUMN_DESCRIPTION, description);
|
||||||
|
values.put(Constants.COLUMN_PHOTO, String.valueOf(photo));
|
||||||
|
|
||||||
|
getWritableDatabase().insert(Constants.TABLE_NAME, null, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deletePlaylist(int id, int ownerId) {
|
||||||
|
getWritableDatabase().delete(Constants.TABLE_NAME, Constants.COLUMN_ID + " = ? AND " + Constants.COLUMN_OWNER_ID + " = ?", new String[]{String.valueOf(id), String.valueOf(ownerId)});
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCachedPlaylist(int id, int ownerId) {
|
||||||
|
Cursor cursor = getReadableDatabase().query(Constants.TABLE_NAME, new String[]{Constants.COLUMN_ID}, Constants.COLUMN_ID + " = ? AND " + Constants.COLUMN_OWNER_ID + " = ?", new String[]{String.valueOf(id), String.valueOf(ownerId)}, null, null, null);
|
||||||
|
boolean exists = cursor.getCount() > 0;
|
||||||
|
cursor.close();
|
||||||
|
return exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getPlaylistsCount() {
|
||||||
|
return DatabaseUtils.queryNumEntries(getReadableDatabase(), Constants.TABLE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deletePlaylistWithTracks(String playlistId) {
|
||||||
|
SQLiteDatabase db = getWritableDatabase();
|
||||||
|
String[] parts = playlistId.split("_");
|
||||||
|
int id = Integer.parseInt(parts[1]);
|
||||||
|
int ownerId = Integer.parseInt(parts[0]);
|
||||||
|
|
||||||
|
// Get all tracks in the playlist
|
||||||
|
List<MusicTrack> tracks = getTracksInPlaylist(playlistId);
|
||||||
|
|
||||||
|
// Delete the playlist
|
||||||
|
deletePlaylist(id, ownerId);
|
||||||
|
|
||||||
|
// Delete each track from the playlist_tracks table
|
||||||
|
for (MusicTrack track : tracks) {
|
||||||
|
String trackId = track.y1();
|
||||||
|
removeTrackFromPlaylist(playlistId, trackId);
|
||||||
|
removePlaylistThumb(playlistId);
|
||||||
|
|
||||||
|
if (!isTrackInAnyOtherPlaylist(trackId, playlistId)) {
|
||||||
|
MusicCacheImpl.removeTrack(trackId);
|
||||||
|
Log.d("Playlist", "removed " + trackId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete all entries from the playlist_tracks table for this playlist
|
||||||
|
db.delete(Constants.TABLE_PLAYLIST_TRACKS, Constants.COLUMN_PLAYLIST_ID + " = ?", new String[]{playlistId});
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isTrackInAnyOtherPlaylist(String trackId, String playlistId) {
|
||||||
|
SQLiteDatabase db = getReadableDatabase();
|
||||||
|
Cursor cursor = db.query(
|
||||||
|
Constants.TABLE_PLAYLIST_TRACKS,
|
||||||
|
null,
|
||||||
|
Constants.COLUMN_TRACK_ID + " = ? AND " + Constants.COLUMN_PLAYLIST_ID + " != ?",
|
||||||
|
new String[]{trackId, playlistId},
|
||||||
|
null, null, null
|
||||||
|
);
|
||||||
|
boolean exists = cursor.getCount() > 0;
|
||||||
|
cursor.close();
|
||||||
|
return exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Playlist> getAllPlaylists() {
|
||||||
|
Cursor cursor = getReadableDatabase().query(Constants.TABLE_NAME, null, null, null, null, null, null);
|
||||||
|
List<Playlist> playlists = getPlaylistsWithCursor(cursor);
|
||||||
|
cursor.close();
|
||||||
|
return playlists;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Playlist getPlaylistById(int id, int ownerId) {
|
||||||
|
SQLiteDatabase db = getReadableDatabase();
|
||||||
|
Cursor cursor = db.query(Constants.TABLE_NAME, null, Constants.COLUMN_ID + " = ? AND " + Constants.COLUMN_OWNER_ID + " = ?", new String[]{String.valueOf(id), String.valueOf(ownerId)}, null, null, null);
|
||||||
|
Playlist playlist = null;
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
try {
|
||||||
|
playlist = fromCursor(cursor);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cursor.close();
|
||||||
|
return playlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Playlist> getPlaylistsWithCursor(Cursor cur) {
|
||||||
|
List<Playlist> playlists = new ArrayList<>();
|
||||||
|
while (cur.moveToNext()) {
|
||||||
|
try {
|
||||||
|
playlists.add(fromCursor(cur));
|
||||||
|
} catch (JSONException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return playlists;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTrackToPlaylist(String playlistId, String trackId) {
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(Constants.COLUMN_PLAYLIST_ID, playlistId);
|
||||||
|
values.put(Constants.COLUMN_TRACK_ID, trackId);
|
||||||
|
getWritableDatabase().insert(Constants.TABLE_PLAYLIST_TRACKS, null, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeTrackFromPlaylist(String playlistId, String trackId) {
|
||||||
|
getWritableDatabase().delete(Constants.TABLE_PLAYLIST_TRACKS, Constants.COLUMN_PLAYLIST_ID + " = ? AND " + Constants.COLUMN_TRACK_ID + " = ?", new String[]{playlistId, trackId});
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<MusicTrack> getTracksInPlaylist(String playlistId) {
|
||||||
|
SQLiteDatabase db = getReadableDatabase();
|
||||||
|
Cursor cursor = db.query(
|
||||||
|
Constants.TABLE_PLAYLIST_TRACKS,
|
||||||
|
new String[]{Constants.COLUMN_TRACK_ID},
|
||||||
|
Constants.COLUMN_PLAYLIST_ID + " = ?",
|
||||||
|
new String[]{playlistId},
|
||||||
|
null, null, null
|
||||||
|
);
|
||||||
|
|
||||||
|
List<MusicTrack> tracks = new ArrayList<>();
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
@SuppressLint("Range")
|
||||||
|
String trackId = cursor.getString(cursor.getColumnIndex(Constants.COLUMN_TRACK_ID));
|
||||||
|
MusicTrack track = TracklistHelper.getTrack(trackId);
|
||||||
|
|
||||||
|
Log.d("Playlist", "add " + trackId + " to playlist " + playlistId);
|
||||||
|
|
||||||
|
if (track != null) {
|
||||||
|
tracks.add(track);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.close();
|
||||||
|
return tracks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConfigure(SQLiteDatabase db) {
|
||||||
|
super.onConfigure(db);
|
||||||
|
db.setForeignKeyConstraintsEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface Constants {
|
||||||
|
int DV_VERSION = 0x2;
|
||||||
|
String DB_NAME = "vt_lite_cache_playlists.db";
|
||||||
|
String TABLE_NAME = "playlists";
|
||||||
|
|
||||||
|
//columns
|
||||||
|
String COLUMN_ID = "id";
|
||||||
|
String COLUMN_OWNER_ID = "owner_id";
|
||||||
|
String COLUMN_IS_EXPLICIT = "is_explicit";
|
||||||
|
String COLUMN_TITLE = "title";
|
||||||
|
String COLUMN_DESCRIPTION = "description";
|
||||||
|
String COLUMN_PHOTO = "photo";
|
||||||
|
|
||||||
|
// tracks
|
||||||
|
String COLUMN_TRACK_ID = "track_id";
|
||||||
|
String TABLE_PLAYLIST_TRACKS = "playlist_tracks";
|
||||||
|
String COLUMN_PLAYLIST_ID = "playlist_id";
|
||||||
|
|
||||||
|
//queries
|
||||||
|
String CREATE_QUERY = "create table " + TABLE_NAME
|
||||||
|
+ "("
|
||||||
|
+ COLUMN_ID + " INTEGER PRIMARY KEY autoincrement,"
|
||||||
|
+ COLUMN_OWNER_ID + " INTEGER not null,"
|
||||||
|
+ COLUMN_IS_EXPLICIT + " TEXT not null,"
|
||||||
|
+ COLUMN_TITLE + " TEXT not null,"
|
||||||
|
+ COLUMN_DESCRIPTION + " TEXT not null,"
|
||||||
|
+ COLUMN_PHOTO + " TEXT not null"
|
||||||
|
+ ")";
|
||||||
|
|
||||||
|
String CREATE_PLAYLIST_TRACKS_QUERY = "create table " + TABLE_PLAYLIST_TRACKS + "("
|
||||||
|
+ COLUMN_PLAYLIST_ID + " TEXT not null,"
|
||||||
|
+ COLUMN_TRACK_ID + " TEXT not null"
|
||||||
|
+ ")";
|
||||||
|
|
||||||
|
String DROP_QUERY = "drop table if exists " + TABLE_NAME;
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-21
@@ -1,4 +1,4 @@
|
|||||||
package ru.vtosters.lite.music.cache;
|
package ru.vtosters.lite.music.cache.delegate;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
@@ -43,27 +43,9 @@ public class MusicCacheDbDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<MusicTrack> getAllTracks(Context context) {
|
public static MusicTrack getTrackById(Context context, String trackId) {
|
||||||
try (var db = connectToDb(context)) {
|
try (var db = connectToDb(context)) {
|
||||||
return db.getAllTracks();
|
return db.getTrackById(trackId);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<MusicTrack> getAlbumById(Context context, String albumId) {
|
|
||||||
try (var db = connectToDb(context)) {
|
|
||||||
return db.getAlbum(albumId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<MusicTrack> getFirstAlbumTrack(Context context, String albumId) {
|
|
||||||
try (var db = connectToDb(context)) {
|
|
||||||
return db.getFirstAlbumTrack(albumId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<MusicTrack> getPlaylist(Context context) {
|
|
||||||
try (var db = connectToDb(context)) {
|
|
||||||
return db.getPlaylist();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+118
@@ -0,0 +1,118 @@
|
|||||||
|
package ru.vtosters.lite.music.cache.delegate;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Log;
|
||||||
|
import com.vk.dto.music.MusicTrack;
|
||||||
|
import com.vk.dto.music.Playlist;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import ru.vtosters.lite.music.cache.db.PlaylistCacheDb;
|
||||||
|
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
|
||||||
|
import ru.vtosters.lite.utils.music.PlaylistUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PlaylistCacheDbDelegate {
|
||||||
|
static synchronized private PlaylistCacheDb connectToDb(Context context) {
|
||||||
|
synchronized (PlaylistCacheDb.class) {
|
||||||
|
return new PlaylistCacheDb(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addPlaylist(Context context, Playlist playlist) {
|
||||||
|
try (var db = connectToDb(context)) {
|
||||||
|
db.addPlaylist(playlist.a, playlist.b, playlist.C, playlist.g, playlist.B, generatePhotoJSON(playlist));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JSONObject generatePhotoJSON(Playlist playlist) {
|
||||||
|
if (PlaylistUtils.getThumb(playlist) != null) {
|
||||||
|
JSONObject photo = new JSONObject();
|
||||||
|
String playlistId = playlist.v1();
|
||||||
|
try {
|
||||||
|
photo.put("height", 300);
|
||||||
|
photo.put("width", 300);
|
||||||
|
|
||||||
|
addPhotoSizeToJSON(photo, playlistId, 600, "photo_600");
|
||||||
|
addPhotoSizeToJSON(photo, playlistId, 34, "photo_34");
|
||||||
|
addPhotoSizeToJSON(photo, playlistId, 1200, "photo_1200");
|
||||||
|
addPhotoSizeToJSON(photo, playlistId, 68, "photo_68");
|
||||||
|
addPhotoSizeToJSON(photo, playlistId, 135, "photo_135");
|
||||||
|
addPhotoSizeToJSON(photo, playlistId, 300, "photo_300");
|
||||||
|
addPhotoSizeToJSON(photo, playlistId, 270, "photo_270");
|
||||||
|
|
||||||
|
return photo;
|
||||||
|
} catch (JSONException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addPhotoSizeToJSON(JSONObject photo, String playlistId, int size, String key) throws JSONException {
|
||||||
|
photo.put(key, Uri.fromFile(MusicCacheStorageUtils.getPlaylistThumb(playlistId, size)).toString());
|
||||||
|
Log.d("Playlist", "thumb link " + Uri.fromFile(MusicCacheStorageUtils.getPlaylistThumb(playlistId, size)).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void deletePlaylist(Context context, String playlistId) {
|
||||||
|
try (var db = connectToDb(context)) {
|
||||||
|
db.deletePlaylistWithTracks(playlistId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isCachedPlaylist(Context context, String id) {
|
||||||
|
try (var db = connectToDb(context)) {
|
||||||
|
String[] parts = id.split("_");
|
||||||
|
int playlistId = Integer.parseInt(parts[1]);
|
||||||
|
int ownerId = Integer.parseInt(parts[0]);
|
||||||
|
|
||||||
|
return db.isCachedPlaylist(playlistId, ownerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getPlaylistsCount(Context context) {
|
||||||
|
try (var db = connectToDb(context)) {
|
||||||
|
return db.getPlaylistsCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Playlist> getAllPlaylists(Context context) {
|
||||||
|
try (var db = connectToDb(context)) {
|
||||||
|
return db.getAllPlaylists();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Playlist getPlaylistById(Context context, String playlistId) {
|
||||||
|
try (var db = connectToDb(context)) {
|
||||||
|
String[] parts = playlistId.split("_");
|
||||||
|
int id = Integer.parseInt(parts[1]);
|
||||||
|
int ownerId = Integer.parseInt(parts[0]);
|
||||||
|
|
||||||
|
return db.getPlaylistById(id, ownerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addTrackToPlaylist(Context context, String playlistId, String trackId) {
|
||||||
|
try (var db = connectToDb(context)) {
|
||||||
|
db.addTrackToPlaylist(playlistId, trackId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeTrackFromPlaylist(Context context, String playlistId, String trackId) {
|
||||||
|
try (var db = connectToDb(context)) {
|
||||||
|
db.removeTrackFromPlaylist(playlistId, trackId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<MusicTrack> getTracksInPlaylist(Context context, String playlistId) {
|
||||||
|
try (var db = connectToDb(context)) {
|
||||||
|
return db.getTracksInPlaylist(playlistId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void drop(Context context) {
|
||||||
|
context.deleteDatabase(PlaylistCacheDb.Constants.DB_NAME);
|
||||||
|
}
|
||||||
|
}
|
||||||
+70
-71
@@ -1,96 +1,82 @@
|
|||||||
package ru.vtosters.lite.music.cache.helpers;
|
package ru.vtosters.lite.music.cache.helpers;
|
||||||
|
|
||||||
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserId;
|
import android.util.Log;
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
|
|
||||||
import com.vk.dto.music.Playlist;
|
import com.vk.dto.music.Playlist;
|
||||||
import com.vtosters.lite.R;
|
import com.vtosters.lite.R;
|
||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import ru.vtosters.lite.music.cache.MusicCacheImpl;
|
import ru.vtosters.lite.music.cache.MusicCacheImpl;
|
||||||
|
import ru.vtosters.lite.utils.AccountManagerUtils;
|
||||||
import ru.vtosters.lite.utils.AndroidUtils;
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
|
import ru.vtosters.lite.utils.NetworkUtils;
|
||||||
import ru.vtosters.lite.utils.music.MusicTrackUtils;
|
import ru.vtosters.lite.utils.music.PlaylistUtils;
|
||||||
|
|
||||||
public class PlaylistHelper {
|
public class PlaylistHelper {
|
||||||
|
|
||||||
public static Playlist createCachedPlaylistMetadata() {
|
public static Playlist createCachedPlaylistMetadata() {
|
||||||
try {
|
try {
|
||||||
return new Playlist(getPlaylist());
|
return new Playlist(getCachedSongsPlaylist());
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Playlist createCachedPlaylistMetadata(String id) {
|
public static JSONArray addCachedPlaylists(JSONArray jsonArray, Boolean noPlaylists) {
|
||||||
var track = TracklistHelper.getTracksOne(id).get(0);
|
|
||||||
var albumLink = track.I;
|
|
||||||
var thumb = albumLink.u1();
|
|
||||||
return createAlbum(
|
|
||||||
thumb != null ? thumb.h(600) : "",
|
|
||||||
albumLink.getTitle(),
|
|
||||||
MusicTrackUtils.getArtists(track),
|
|
||||||
track.y1(),
|
|
||||||
albumLink.getId()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Playlist createAlbum(String photo, String title, String artist, String trackId, int albumId) {
|
|
||||||
try {
|
try {
|
||||||
var jPhoto = new JSONObject();
|
if (noPlaylists) {
|
||||||
if (!photo.isEmpty())
|
jsonArray.put(new JSONArray().put(getCachedSongsPlaylist()));
|
||||||
jPhoto.put("height", 600)
|
} else {
|
||||||
.put("width", 600)
|
jsonArray.put(getCachedSongsPlaylist());
|
||||||
.putOpt("photo_300", Uri.fromFile(MusicCacheStorageUtils.getTrackThumb(trackId, 300)).toString())
|
}
|
||||||
.putOpt("photo_600", Uri.fromFile(MusicCacheStorageUtils.getTrackThumb(trackId, 600)).toString());
|
|
||||||
var obj = new JSONObject()
|
for (Playlist playlist : MusicCacheImpl.getPlaylists()) {
|
||||||
.put("id", -2)
|
if (noPlaylists) {
|
||||||
.put("owner_id", albumId)
|
jsonArray.put(new JSONArray().put(generatePlaylist(playlist.a, playlist.b, playlist.C, playlist.g, playlist.B, PlaylistUtils.getThumb(playlist), playlist.O)));
|
||||||
.put("type", 1)
|
} else {
|
||||||
.put("album_type", "main_only")
|
jsonArray.put(generatePlaylist(playlist.a, playlist.b, playlist.C, playlist.g, playlist.B, PlaylistUtils.getThumb(playlist), playlist.O));
|
||||||
.put("title", title)
|
}
|
||||||
.put("description", albumId + "/ " + AndroidUtils.getString(R.string.cached_album_warning))
|
|
||||||
.put("main_artists", new JSONArray()
|
Log.d("PlaylistHelper", "Playlist cache added: " + playlist.a + " " + playlist.b + " " + playlist.C + " " + playlist.g + " " + playlist.B);
|
||||||
.put(new JSONObject().put("name", artist)
|
}
|
||||||
.put("id", "-1")
|
|
||||||
.put("domain", "-1")))
|
|
||||||
.putOpt("photo", jPhoto)
|
|
||||||
.put("count", 0);
|
|
||||||
return new Playlist(obj);
|
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
e.printStackTrace();
|
e.fillInStackTrace();
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return jsonArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Playlist> getAlbumPlaylists() {
|
public static JSONObject generatePlaylist(int id, int owner_id, boolean is_explicit, String title, String description, JSONObject photo, int count) throws JSONException {
|
||||||
List<Playlist> list = new ArrayList<>();
|
JSONObject playlist = new JSONObject()
|
||||||
for (var track : MusicCacheImpl.getPlaylist()) {
|
.put("id", id)
|
||||||
var albumLink = track.I;
|
.put("owner_id", owner_id)
|
||||||
var thumb = albumLink.u1();
|
.put("type", 0)
|
||||||
list.add(createAlbum(
|
.put("album_type", "playlist")
|
||||||
thumb != null ? thumb.h(600) : "",
|
.put("is_explicit", is_explicit)
|
||||||
albumLink.getTitle(),
|
.put("title", title)
|
||||||
MusicTrackUtils.getArtists(track),
|
.put("description", description)
|
||||||
track.y1(),
|
.put("access_key", "cache")
|
||||||
albumLink.getId()
|
.put("genres", new JSONArray())
|
||||||
));
|
.put("is_following", false)
|
||||||
}
|
.put("followers", 0)
|
||||||
return list;
|
.put("plays", 0)
|
||||||
|
.put("create_time", 0)
|
||||||
|
.put("update_time", 0)
|
||||||
|
.put("subtitle", "")
|
||||||
|
.put("meta", new JSONObject()
|
||||||
|
.put("view", "compact"))
|
||||||
|
.put("count", count);
|
||||||
|
|
||||||
|
if (photo != null) {
|
||||||
|
playlist.put("photo", photo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JSONObject getPlaylist() throws JSONException {
|
return playlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JSONObject getCachedSongsPlaylist() throws JSONException {
|
||||||
return new JSONObject()
|
return new JSONObject()
|
||||||
.put("id", -1)
|
.put("id", -1)
|
||||||
.put("owner_id", getUserId())
|
.put("owner_id", AccountManagerUtils.getUserId())
|
||||||
.put("type", 0)
|
.put("type", 0)
|
||||||
.put("album_type", "playlist")
|
.put("album_type", "playlist")
|
||||||
.put("title", AndroidUtils.getString(R.string.cached_tracks_title))
|
.put("title", AndroidUtils.getString(R.string.cached_tracks_title))
|
||||||
@@ -115,7 +101,21 @@ public class PlaylistHelper {
|
|||||||
.put("height", 600))
|
.put("height", 600))
|
||||||
.put("meta", new JSONObject()
|
.put("meta", new JSONObject()
|
||||||
.put("view", "compact"))
|
.put("view", "compact"))
|
||||||
.put("count", 0);
|
.put("count", MusicCacheImpl.getAllOwnTracks().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JSONArray getCachedPlaylistsIds() {
|
||||||
|
JSONArray arr = new JSONArray();
|
||||||
|
|
||||||
|
if (NetworkUtils.isNetworkConnected() && !MusicCacheImpl.getAllOwnTracks().isEmpty()) {
|
||||||
|
arr.put(AccountManagerUtils.getUserId() + "_-1");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var playlist : MusicCacheImpl.getPlaylists()) {
|
||||||
|
arr.put(playlist.v1());
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JSONObject getCatalogPlaylist() throws JSONException {
|
public static JSONObject getCatalogPlaylist() throws JSONException {
|
||||||
@@ -125,18 +125,17 @@ public class PlaylistHelper {
|
|||||||
.put("layout", new JSONObject()
|
.put("layout", new JSONObject()
|
||||||
.put("name", "large_slider")
|
.put("name", "large_slider")
|
||||||
.put("is_editable", 0)
|
.put("is_editable", 0)
|
||||||
.put("owner_id", getUserId()))
|
.put("owner_id", AccountManagerUtils.getUserId()))
|
||||||
.put("playlists_ids", new JSONArray()
|
.put("playlists_ids", getCachedPlaylistsIds());
|
||||||
.put(getUserId() + "_-1"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JSONObject getCatalogHeader() throws JSONException {
|
public static JSONObject getCatalogHeader(String text) throws JSONException {
|
||||||
return new JSONObject()
|
return new JSONObject()
|
||||||
.put("id", "cache")
|
.put("id", "cache")
|
||||||
.put("data_type", "none")
|
.put("data_type", "none")
|
||||||
.put("layout", new JSONObject()
|
.put("layout", new JSONObject()
|
||||||
.put("name", "header")
|
.put("name", "header")
|
||||||
.put("title", AndroidUtils.getString(R.string.cached_tracks_title)));
|
.put("title", text));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JSONObject getCatalogSeparator() throws JSONException {
|
public static JSONObject getCatalogSeparator() throws JSONException {
|
||||||
|
|||||||
+5
-8
@@ -8,6 +8,8 @@ import org.json.JSONException;
|
|||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import ru.vtosters.hooks.other.Preferences;
|
import ru.vtosters.hooks.other.Preferences;
|
||||||
import ru.vtosters.lite.music.cache.MusicCacheImpl;
|
import ru.vtosters.lite.music.cache.MusicCacheImpl;
|
||||||
|
import ru.vtosters.lite.music.cache.delegate.MusicCacheDbDelegate;
|
||||||
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
|
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -22,7 +24,7 @@ public class TracklistHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static List<MusicTrack> getTracks() {
|
public static List<MusicTrack> getTracks() {
|
||||||
List<MusicTrack> tracks = MusicCacheImpl.getAllTracks();
|
List<MusicTrack> tracks = MusicCacheImpl.getAllOwnTracks();
|
||||||
|
|
||||||
boolean doNotinvertOrder = Preferences.getBoolValue("invertCachedTracks", false);
|
boolean doNotinvertOrder = Preferences.getBoolValue("invertCachedTracks", false);
|
||||||
|
|
||||||
@@ -33,13 +35,8 @@ public class TracklistHelper {
|
|||||||
return tracks;
|
return tracks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static MusicTrack getTrack(String id) {
|
||||||
public static List<MusicTrack> getTracks(String id) {
|
return MusicCacheDbDelegate.getTrackById(AndroidUtils.getGlobalContext(), id);
|
||||||
return MusicCacheImpl.getAlbumById(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<MusicTrack> getTracksOne(String id) {
|
|
||||||
return MusicCacheImpl.getFirstAlbumTrack(id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JSONArray tracksToIds(List<MusicTrack> tracks) {
|
public static JSONArray tracksToIds(List<MusicTrack> tracks) {
|
||||||
|
|||||||
@@ -53,7 +53,9 @@ public class PlaylistConverter {
|
|||||||
var request = new Request.a()
|
var request = new Request.a()
|
||||||
.b(requestUrl)
|
.b(requestUrl)
|
||||||
.a("Accept-Encoding", "gzip")
|
.a("Accept-Encoding", "gzip")
|
||||||
.a(Headers.a("User-Agent", Network.l.c().a(), "Content-Type", "application/x-www-form-urlencoded; charset=utf-8")).a();
|
.a("User-Agent", Network.l.c().a())
|
||||||
|
.a("Content-Type", "application/x-www-form-urlencoded; charset=utf-8")
|
||||||
|
.a();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var response = CompletableFuture.supplyAsync(() -> {
|
var response = CompletableFuture.supplyAsync(() -> {
|
||||||
|
|||||||
@@ -47,8 +47,9 @@ public class AudioGet {
|
|||||||
var request = new Request.a()
|
var request = new Request.a()
|
||||||
.b(requestUrl)
|
.b(requestUrl)
|
||||||
.a("Accept-Encoding", "gzip")
|
.a("Accept-Encoding", "gzip")
|
||||||
.a(Headers.a("User-Agent", Network.l.c().a(), "Content-Type", "application/x-www-form-urlencoded; charset=utf-8")).a();
|
.a("User-Agent", Network.l.c().a())
|
||||||
|
.a("Content-Type", "application/x-www-form-urlencoded; charset=utf-8")
|
||||||
|
.a();
|
||||||
try {
|
try {
|
||||||
var response = CompletableFuture.supplyAsync(() -> {
|
var response = CompletableFuture.supplyAsync(() -> {
|
||||||
try (Response resp = Network.b(CLIENT_API).a(request).execute()) {
|
try (Response resp = Network.b(CLIENT_API).a(request).execute()) {
|
||||||
|
|||||||
@@ -2,9 +2,12 @@ package ru.vtosters.lite.music.downloader;
|
|||||||
|
|
||||||
import com.vk.dto.music.MusicTrack;
|
import com.vk.dto.music.MusicTrack;
|
||||||
|
|
||||||
|
import com.vk.dto.music.Playlist;
|
||||||
import ru.vtosters.lite.music.cache.MusicCacheImpl;
|
import ru.vtosters.lite.music.cache.MusicCacheImpl;
|
||||||
|
import ru.vtosters.lite.music.cache.delegate.PlaylistCacheDbDelegate;
|
||||||
import ru.vtosters.lite.music.interfaces.Callback;
|
import ru.vtosters.lite.music.interfaces.Callback;
|
||||||
import ru.vtosters.lite.music.interfaces.ITrackDownloader;
|
import ru.vtosters.lite.music.interfaces.ITrackDownloader;
|
||||||
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
|
|
||||||
public final class CachedDownloader implements ITrackDownloader {
|
public final class CachedDownloader implements ITrackDownloader {
|
||||||
|
|
||||||
@@ -14,9 +17,8 @@ public final class CachedDownloader implements ITrackDownloader {
|
|||||||
this.origin = origin;
|
this.origin = origin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void download(MusicTrack track, Callback callback) {
|
public void download(MusicTrack track, Callback callback, Playlist playlist) {
|
||||||
origin.download(track, new Callback() {
|
origin.download(track, new Callback() {
|
||||||
@Override
|
@Override
|
||||||
public void onProgress(int progress) {
|
public void onProgress(int progress) {
|
||||||
@@ -25,18 +27,26 @@ public final class CachedDownloader implements ITrackDownloader {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess() {
|
public void onSuccess() {
|
||||||
new ThumbnailDownloader().download(track, new Callback() {
|
new ThumbnailTrackDownloader().download(track, new Callback() {
|
||||||
@Override public void onProgress(int progress) {}
|
@Override public void onProgress(int progress) {
|
||||||
|
|
||||||
@Override public void onSuccess() {}
|
}
|
||||||
|
|
||||||
|
@Override public void onSuccess() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Throwable e) {
|
public void onFailure(Throwable e) {
|
||||||
callback.onFailure(e);
|
callback.onFailure(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void onSizeReceived(long size, long header) {}
|
@Override public void onSizeReceived(long size, long header) {
|
||||||
});
|
|
||||||
|
}
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
PlaylistCacheDbDelegate.addTrackToPlaylist(AndroidUtils.getGlobalContext(), playlist.v1(), track.y1());
|
||||||
MusicCacheImpl.addTrack(track);
|
MusicCacheImpl.addTrack(track);
|
||||||
callback.onSuccess();
|
callback.onSuccess();
|
||||||
}
|
}
|
||||||
@@ -50,6 +60,6 @@ public final class CachedDownloader implements ITrackDownloader {
|
|||||||
public void onSizeReceived(long size, long header) {
|
public void onSizeReceived(long size, long header) {
|
||||||
callback.onSizeReceived(size, header);
|
callback.onSizeReceived(size, header);
|
||||||
}
|
}
|
||||||
});
|
}, playlist);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,6 +20,7 @@ import javax.crypto.Cipher;
|
|||||||
import javax.crypto.spec.IvParameterSpec;
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
|
import com.vk.dto.music.Playlist;
|
||||||
import ru.vtosters.hooks.other.Preferences;
|
import ru.vtosters.hooks.other.Preferences;
|
||||||
import ru.vtosters.lite.music.converter.ts.MpegDemuxer;
|
import ru.vtosters.lite.music.converter.ts.MpegDemuxer;
|
||||||
import ru.vtosters.lite.music.interfaces.Callback;
|
import ru.vtosters.lite.music.interfaces.Callback;
|
||||||
@@ -34,7 +35,7 @@ public final class Mp3Downloader implements ITrackDownloader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void download(MusicTrack track, Callback callback) {
|
public void download(MusicTrack track, Callback callback, Playlist playlist) {
|
||||||
String uri = track.D;
|
String uri = track.D;
|
||||||
if (Objects.requireNonNull(uri).isEmpty()) {
|
if (Objects.requireNonNull(uri).isEmpty()) {
|
||||||
String msg = "link error: " + track.y1() + ", title: " + Mp3Downloader.getTitle(track);
|
String msg = "link error: " + track.y1() + ", title: " + Mp3Downloader.getTitle(track);
|
||||||
|
|||||||
@@ -3,23 +3,23 @@ package ru.vtosters.lite.music.downloader;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.vk.dto.music.MusicTrack;
|
import com.vk.dto.music.MusicTrack;
|
||||||
|
import com.vk.dto.music.Playlist;
|
||||||
|
import ru.vtosters.lite.music.interfaces.Callback;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import java8.util.concurrent.CompletableFuture;
|
import java8.util.concurrent.CompletableFuture;
|
||||||
import ru.vtosters.lite.music.interfaces.Callback;
|
|
||||||
|
|
||||||
public class PlaylistDownloader {
|
public class PlaylistDownloader {
|
||||||
public static CompletableFuture<Void>
|
public static void downloadPlaylist(List<MusicTrack> playlist, String playlistName, String path, Callback callback) {
|
||||||
downloadPlaylist(List<MusicTrack> playlist, String playlistName, String path, Callback callback) {
|
|
||||||
var outDir = new File(path);
|
var outDir = new File(path);
|
||||||
if (!outDir.exists())
|
if (!outDir.exists())
|
||||||
if (outDir.mkdirs()) Log.v("PlaylistDownloader", "Directory created");
|
if (outDir.mkdirs()) Log.v("PlaylistDownloader", "Directory created");
|
||||||
else Log.e("PlaylistDownloader", "Directory creation failed");
|
else Log.e("PlaylistDownloader", "Directory creation failed");
|
||||||
Callback delegate = new ProgressCallback(callback);
|
Callback delegate = new ProgressCallback(callback);
|
||||||
return CompletableFuture.allOf(playlist
|
CompletableFuture.allOf(playlist
|
||||||
.stream()
|
.stream()
|
||||||
.map(x -> {
|
.map(x -> {
|
||||||
Callback.CompletableFutureCallback d = new
|
Callback.CompletableFutureCallback d = new
|
||||||
@@ -30,14 +30,13 @@ public class PlaylistDownloader {
|
|||||||
.toArray(CompletableFuture[]::new));
|
.toArray(CompletableFuture[]::new));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CompletableFuture<Void> cachePlaylist(List<MusicTrack> playlist, Callback callback) {
|
public static void cachePlaylist(List<MusicTrack> playlist, Callback callback, Playlist playlistId) {
|
||||||
Callback delegate = new ProgressCallback(callback);
|
Callback delegate = new ProgressCallback(callback);
|
||||||
return CompletableFuture.allOf(playlist
|
CompletableFuture.allOf(playlist
|
||||||
.stream()
|
.stream()
|
||||||
.map(x -> {
|
.map(x -> {
|
||||||
Callback.CompletableFutureCallback d = new
|
Callback.CompletableFutureCallback d = new Callback.CompletableFutureCallback(delegate);
|
||||||
Callback.CompletableFutureCallback(delegate);
|
TrackDownloader.cacheTrack(x, d, playlistId);
|
||||||
TrackDownloader.cacheTrack(x, d);
|
|
||||||
return d;
|
return d;
|
||||||
})
|
})
|
||||||
.toArray(CompletableFuture[]::new));
|
.toArray(CompletableFuture[]::new));
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package ru.vtosters.lite.music.downloader;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
import com.vk.dto.music.MusicTrack;
|
||||||
|
import com.vk.dto.music.Playlist;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import ru.vtosters.lite.music.interfaces.Callback;
|
||||||
|
import ru.vtosters.lite.music.interfaces.ITrackDownloader;
|
||||||
|
import ru.vtosters.lite.utils.IOUtils;
|
||||||
|
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
|
||||||
|
import ru.vtosters.lite.utils.music.PlaylistUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ThumbnailPlaylistDownloader implements ITrackDownloader {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void download(MusicTrack track, Callback callback, Playlist playlist) {
|
||||||
|
JSONObject json = PlaylistUtils.getThumb(playlist); // get thumbnail photo
|
||||||
|
|
||||||
|
if (json == null) {
|
||||||
|
callback.onFailure(new IOException("No thumbnail found"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
List<String> sizes = List.of("600", "1200", "68", "135", "300", "270"); // define the order of sizes to download
|
||||||
|
JSONArray sizesArray = json.getJSONArray("sizes");
|
||||||
|
for (String size : sizes) {
|
||||||
|
for (int i = 0; i < sizesArray.length(); i++) {
|
||||||
|
JSONObject sizeObj = sizesArray.getJSONObject(i);
|
||||||
|
if (sizeObj.getInt("width") == Integer.parseInt(size)) {
|
||||||
|
String url = sizeObj.getString("src");
|
||||||
|
|
||||||
|
if (url.isEmpty()) continue;
|
||||||
|
|
||||||
|
Log.d("Playlist", "downloading thumb " + url);
|
||||||
|
|
||||||
|
downloadThumbnailPlaylist(url, Integer.parseInt(size), playlist.v1());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callback.onSuccess();
|
||||||
|
} catch (IOException | JSONException e) {
|
||||||
|
callback.onFailure(e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void downloadThumbnailPlaylist(String url, int res, String playlistId) throws IOException {
|
||||||
|
IOUtils.writeToFile(MusicCacheStorageUtils.getPlaylistThumb(playlistId, res), IOUtils.readFully(new URL(url).openStream()));
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-3
@@ -2,6 +2,7 @@ package ru.vtosters.lite.music.downloader;
|
|||||||
|
|
||||||
import com.vk.dto.music.MusicTrack;
|
import com.vk.dto.music.MusicTrack;
|
||||||
|
|
||||||
|
import com.vk.dto.music.Playlist;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
@@ -14,10 +15,10 @@ import ru.vtosters.lite.music.interfaces.ITrackDownloader;
|
|||||||
import ru.vtosters.lite.utils.IOUtils;
|
import ru.vtosters.lite.utils.IOUtils;
|
||||||
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
|
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
|
||||||
|
|
||||||
public final class ThumbnailDownloader implements ITrackDownloader {
|
public final class ThumbnailTrackDownloader implements ITrackDownloader {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void download(MusicTrack track, Callback callback) {
|
public void download(MusicTrack track, Callback callback, Playlist playlist) {
|
||||||
JSONObject json = track.J();
|
JSONObject json = track.J();
|
||||||
JSONObject album = json.optJSONObject("album");
|
JSONObject album = json.optJSONObject("album");
|
||||||
if (album == null) return;
|
if (album == null) return;
|
||||||
@@ -50,5 +51,4 @@ public final class ThumbnailDownloader implements ITrackDownloader {
|
|||||||
private static void downloadThumbnail(String url, int res, String trackId) throws IOException {
|
private static void downloadThumbnail(String url, int res, String trackId) throws IOException {
|
||||||
IOUtils.writeToFile(MusicCacheStorageUtils.getTrackThumb(trackId, res), IOUtils.readFully(new URL(url).openStream()));
|
IOUtils.writeToFile(MusicCacheStorageUtils.getTrackThumb(trackId, res), IOUtils.readFully(new URL(url).openStream()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,7 @@ import com.vk.dto.music.MusicTrack;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
|
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
|
||||||
|
import com.vk.dto.music.Playlist;
|
||||||
import ru.vtosters.lite.concurrent.VTExecutors;
|
import ru.vtosters.lite.concurrent.VTExecutors;
|
||||||
import ru.vtosters.lite.music.cache.MusicCacheImpl;
|
import ru.vtosters.lite.music.cache.MusicCacheImpl;
|
||||||
import ru.vtosters.lite.music.interfaces.Callback;
|
import ru.vtosters.lite.music.interfaces.Callback;
|
||||||
@@ -21,25 +22,24 @@ public final class TrackDownloader {
|
|||||||
public static void downloadTrack(MusicTrack track, String path, Callback callback) {
|
public static void downloadTrack(MusicTrack track, String path, Callback callback) {
|
||||||
File outDir = new File(path);
|
File outDir = new File(path);
|
||||||
if (!outDir.exists()) {
|
if (!outDir.exists()) {
|
||||||
if (outDir.mkdir())
|
if (outDir.mkdir()) {
|
||||||
Log.v("TrackDownloader", "Directory created");
|
Log.v("TrackDownloader", "Directory created");
|
||||||
else
|
} else {
|
||||||
Log.e("TrackDownloader", "Directory creation failed");
|
Log.e("TrackDownloader", "Directory creation failed");
|
||||||
}
|
}
|
||||||
var outputFile = new File(outDir, IOUtils.getValidFileName(
|
}
|
||||||
MusicTrackUtils.getArtists(track) + " - " + Mp3Downloader.getTitle(track)) + ".mp3");
|
File outputFile = new File(outDir, IOUtils.getValidFileName(MusicTrackUtils.getArtists(track) + " - " + Mp3Downloader.getTitle(track)) + ".mp3");
|
||||||
|
|
||||||
VTExecutors.getMusicDownloadExecutor().submit(() -> new Mp3Downloader(outputFile).download(track, callback));
|
VTExecutors.getMusicDownloadExecutor().submit(() -> new Mp3Downloader(outputFile).download(track, callback, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void cacheTrack(MusicTrack track, Callback callback) {
|
public static void cacheTrack(MusicTrack track, Callback callback, Playlist playlist) {
|
||||||
if (MusicCacheImpl.isCachedTrack(LibVKXClient.asId(track)))
|
if (MusicCacheImpl.isCachedTrack(LibVKXClient.asId(track))) {
|
||||||
return;
|
return;
|
||||||
var outputFile = MusicCacheStorageUtils
|
}
|
||||||
.getTrackFile(LibVKXClient.asId(track));
|
|
||||||
|
|
||||||
VTExecutors.getMusicDownloadExecutor().submit(() -> new CachedDownloader(
|
File outputFile = MusicCacheStorageUtils.getTrackFile(LibVKXClient.asId(track));
|
||||||
new Mp3Downloader(outputFile)
|
|
||||||
).download(track, callback));
|
VTExecutors.getMusicDownloadExecutor().submit(() -> new CachedDownloader(new Mp3Downloader(outputFile)).download(track, callback, playlist));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package ru.vtosters.lite.music.interfaces;
|
package ru.vtosters.lite.music.interfaces;
|
||||||
|
|
||||||
import com.vk.dto.music.MusicTrack;
|
import com.vk.dto.music.MusicTrack;
|
||||||
|
import com.vk.dto.music.Playlist;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for track downloaders
|
* Interface for track downloaders
|
||||||
@@ -11,6 +12,7 @@ public interface ITrackDownloader {
|
|||||||
*
|
*
|
||||||
* @param track Music track
|
* @param track Music track
|
||||||
* @param callback Callback to call when download status changes
|
* @param callback Callback to call when download status changes
|
||||||
|
* @param playlist
|
||||||
*/
|
*/
|
||||||
void download(MusicTrack track, Callback callback);
|
void download(MusicTrack track, Callback callback, Playlist playlist);
|
||||||
}
|
}
|
||||||
@@ -1,29 +1,33 @@
|
|||||||
package ru.vtosters.lite.ui.fragments;
|
package ru.vtosters.lite.ui.fragments;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import androidx.preference.Preference;
|
|
||||||
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
|
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
|
||||||
import com.vk.core.dialogs.alert.VkAlertDialog;
|
import com.vk.core.dialogs.alert.VkAlertDialog;
|
||||||
|
import com.vk.dto.music.Playlist;
|
||||||
import com.vtosters.lite.ui.SummaryListPreference;
|
import com.vtosters.lite.ui.SummaryListPreference;
|
||||||
import ru.vtosters.hooks.other.Preferences;
|
import ru.vtosters.hooks.other.Preferences;
|
||||||
import ru.vtosters.hooks.other.ThemesUtils;
|
import ru.vtosters.hooks.other.ThemesUtils;
|
||||||
import ru.vtosters.lite.downloaders.AudioDownloader;
|
import ru.vtosters.lite.downloaders.AudioDownloader;
|
||||||
import ru.vtosters.lite.music.LastFMScrobbler;
|
import ru.vtosters.lite.music.LastFMScrobbler;
|
||||||
import ru.vtosters.lite.music.cache.MusicCacheImpl;
|
import ru.vtosters.lite.music.cache.MusicCacheImpl;
|
||||||
|
import ru.vtosters.lite.music.cache.delegate.PlaylistCacheDbDelegate;
|
||||||
import ru.vtosters.lite.ui.PreferenceFragmentUtils;
|
import ru.vtosters.lite.ui.PreferenceFragmentUtils;
|
||||||
import ru.vtosters.lite.utils.AndroidUtils;
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
import ru.vtosters.lite.utils.LifecycleUtils;
|
import ru.vtosters.lite.utils.LifecycleUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
public class MusicFragment extends TrackedMaterialPreferenceToolbarFragment {
|
public class MusicFragment extends TrackedMaterialPreferenceToolbarFragment {
|
||||||
private static final ExecutorService executor = Executors.newCachedThreadPool();
|
private static final ExecutorService executor = Executors.newCachedThreadPool();
|
||||||
|
|
||||||
|
@SuppressLint("DefaultLocale")
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle bundle) {
|
public void onCreate(Bundle bundle) {
|
||||||
super.onCreate(bundle);
|
super.onCreate(bundle);
|
||||||
@@ -50,6 +54,20 @@ public class MusicFragment extends TrackedMaterialPreferenceToolbarFragment {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (MusicCacheImpl.hasPlaylist()) {
|
||||||
|
PreferenceFragmentUtils.addPreference(
|
||||||
|
getPreferenceScreen(),
|
||||||
|
"cached_playlists",
|
||||||
|
"Кешированные плейлисты",
|
||||||
|
String.format("Скачано плейлистов: %d", MusicCacheImpl.getPlaylists().size()),
|
||||||
|
null,
|
||||||
|
preference -> {
|
||||||
|
cachedPlaylistsDialog(requireContext());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
PreferenceFragmentUtils.addPreference(
|
PreferenceFragmentUtils.addPreference(
|
||||||
getPreferenceScreen(),
|
getPreferenceScreen(),
|
||||||
"audio_download",
|
"audio_download",
|
||||||
@@ -322,6 +340,26 @@ public class MusicFragment extends TrackedMaterialPreferenceToolbarFragment {
|
|||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void cachedPlaylistsDialog(Context ctx) {
|
||||||
|
List<Playlist> playlists = PlaylistCacheDbDelegate.getAllPlaylists(ctx);
|
||||||
|
String[] playlistNames = new String[playlists.size()];
|
||||||
|
|
||||||
|
for (int i = 0; i < playlists.size(); i++) {
|
||||||
|
playlistNames[i] = playlists.get(i).g; // Assuming Playlist class has a getTitle() method
|
||||||
|
}
|
||||||
|
|
||||||
|
VkAlertDialog.Builder builder = new VkAlertDialog.Builder(ctx);
|
||||||
|
builder.setTitle("Скачанные плейлисты");
|
||||||
|
builder.setItems(playlistNames, (dialog, which) -> {
|
||||||
|
Playlist selectedPlaylist = playlists.get(which);
|
||||||
|
String playlistId = selectedPlaylist.v1();
|
||||||
|
PlaylistCacheDbDelegate.deletePlaylist(ctx, playlistId);
|
||||||
|
AndroidUtils.sendToast("Плейлист удален");
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.show();
|
||||||
|
}
|
||||||
|
|
||||||
private void delcache(Context ctx) {
|
private void delcache(Context ctx) {
|
||||||
new VkAlertDialog.Builder(ctx)
|
new VkAlertDialog.Builder(ctx)
|
||||||
.setTitle(com.vtosters.lite.R.string.warning)
|
.setTitle(com.vtosters.lite.R.string.warning)
|
||||||
|
|||||||
@@ -11,28 +11,36 @@ public class MusicCacheStorageUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||||
public static File getTrackDirById(final String trackId) {
|
public static File getTrackDirById(String trackId) {
|
||||||
final var res = new File(getCacheStorageDir(), trackId);
|
File res = new File(getCacheStorageDir(), trackId);
|
||||||
res.mkdirs();
|
res.mkdirs();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File getTrackFile(final String trackId) {
|
public static File getTrackFile(String trackId) {
|
||||||
return new File(getTrackDirById(trackId), "track.mp3");
|
return new File(getTrackDirById(trackId), "track.mp3");
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||||
public static File getThumbDirById(final String trackId) {
|
public static File getThumbDirById(String trackId) {
|
||||||
final var res = new File(getTrackDirById(trackId), "thumbs");
|
File res = new File(getTrackDirById(trackId), "thumbs");
|
||||||
res.mkdirs();
|
res.mkdirs();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File getTrackThumb(final String trackId, final int factor) {
|
public static File getTrackThumb(String trackId, int factor) {
|
||||||
return new File(getThumbDirById(trackId), "thumb_" + factor + ".png");
|
return new File(getThumbDirById(trackId), "thumb_" + factor + ".png");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void removeTrackDirById(final String trackId) {
|
public static File getPlaylistThumb(String playlistId, int factor) {
|
||||||
|
return new File(getThumbDirById(playlistId), "thumb_playlist_" + factor + ".png");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removePlaylistThumb(String playlistId) {
|
||||||
|
IOUtils.deleteRecursive(getThumbDirById(playlistId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeTrackDirById(String trackId) {
|
||||||
IOUtils.deleteRecursive(getTrackDirById(trackId));
|
IOUtils.deleteRecursive(getTrackDirById(trackId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package ru.vtosters.lite.utils.music;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
import com.vk.dto.music.Playlist;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
public class PlaylistUtils {
|
||||||
|
public static JSONObject getThumb(Playlist playlist) {
|
||||||
|
try {
|
||||||
|
Log.d("Playlist", "thumb is not null");
|
||||||
|
return playlist.F.J();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.d("Playlist", "thumb is null");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user