beta stuff for music
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
||||
ea0425fab4379ae5f7e9db05a22d4b7afd709560
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
3b92d8876dd59d5afc60a53bb23b103cede6a194
|
||||
Binary file not shown.
Binary file not shown.
@@ -19,7 +19,7 @@ import ru.vtosters.lite.music.MP3Downloader;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class AudioDownloader {
|
||||
private static NotificationManagerCompat notificationManager = NotificationManagerCompat.from(AndroidUtils.getGlobalContext());
|
||||
private static NotificationManagerCompat notificationManager = NotificationChannels.getNotificationManager();
|
||||
|
||||
public static void downloadAudio(MusicTrack track) {
|
||||
if (AndroidUtils.getDefaultPrefs().getBoolean("new_music_downloading_way", false)) {
|
||||
@@ -93,7 +93,7 @@ public class AudioDownloader {
|
||||
}
|
||||
|
||||
private static void downloadMP3(MusicTrack track) {
|
||||
MP3Downloader.execute(track);
|
||||
MP3Downloader.downloadMP3(track);
|
||||
}
|
||||
|
||||
private static NotificationCompat.Builder buildDownloadNotification(MusicTrack track, int id) {
|
||||
@@ -103,8 +103,8 @@ public class AudioDownloader {
|
||||
.setContentText(track.toString())
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.setCategory(NotificationCompat.CATEGORY_PROGRESS)
|
||||
.setAutoCancel(true);
|
||||
notificationBuilder.setProgress(100, 0, false);
|
||||
.setAutoCancel(true)
|
||||
.setProgress(100, 0, false);
|
||||
notificationManager.notify(id, notificationBuilder.build());
|
||||
return notificationBuilder;
|
||||
}
|
||||
|
||||
@@ -13,8 +13,11 @@ import ru.vtosters.lite.utils.AndroidUtils;
|
||||
public class NotificationChannels {
|
||||
public static final String MUSIC_DOWNLOAD_CHANNEL_ID = "music_download_channel";
|
||||
public static final String MUSIC_DOWNLOAD_CHANNEL_NAME = "Загрузка музыки";
|
||||
public static final String MUSIC_CACHING_NAME = "Кеширование трека / Обложки";
|
||||
public static final String MUSIC_DOWNLOAD_CHANNEL_DESCRIPTION = "Прогресс загрузки музыки";
|
||||
|
||||
private static NotificationManagerCompat notificationManagerCompat;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public static void createChannels() {
|
||||
createMusicDownloadChannel();
|
||||
@@ -29,4 +32,10 @@ public class NotificationChannels {
|
||||
var notificationManager = NotificationManagerCompat.from(AndroidUtils.getGlobalContext());
|
||||
notificationManager.createNotificationChannel(musicDownloadChannel);
|
||||
}
|
||||
|
||||
public static NotificationManagerCompat getNotificationManager() {
|
||||
if (notificationManagerCompat == null)
|
||||
notificationManagerCompat = NotificationManagerCompat.from(AndroidUtils.getGlobalContext());
|
||||
return notificationManagerCompat;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package ru.vtosters.lite.music;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import ru.vtosters.lite.downloaders.notifications.NotificationChannels;
|
||||
import ru.vtosters.lite.music.cache.FileCacheImplementation;
|
||||
import ru.vtosters.lite.utils.IOUtils;
|
||||
|
||||
public class ArtworkDownloader {
|
||||
|
||||
public static void downloadArtworks(MusicTrack track) throws IOException {
|
||||
var obj = track.J();
|
||||
|
||||
if (!obj.has("album")) return;
|
||||
JSONObject album = obj.optJSONObject("album");
|
||||
|
||||
if (!album.has("thumb")) return;
|
||||
JSONObject thumb = album.optJSONObject("thumb");
|
||||
|
||||
var trackId = track.y1();
|
||||
if (thumb.has("photo_300"))
|
||||
downloadArtwork(thumb.optString("photo_300"), 300, trackId.hashCode(), trackId);
|
||||
if (thumb.has("photo_600"))
|
||||
downloadArtwork(thumb.optString("photo_600"), 600, trackId.hashCode(), trackId);
|
||||
if (thumb.has("photo_1200"))
|
||||
downloadArtwork(thumb.optString("photo_1200"), 1200, trackId.hashCode(), trackId);
|
||||
}
|
||||
|
||||
private static void downloadArtwork(String url, int res, int hash, String trackId) throws IOException {
|
||||
var content = IOUtils.readAllBytes(new URL(url).openStream());
|
||||
IOUtils.writeToFile(FileCacheImplementation.getTrackArtwork(trackId, res), content);
|
||||
Log.d("ArtworkDownloader", "Downloading " + url);
|
||||
//notifyArtwork(AndroidUtils.getGlobalContext(), hash, res);
|
||||
}
|
||||
|
||||
private static void notifyArtwork(Context ctx, int hash, int resolution) {
|
||||
Notification.Builder builder;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
builder = new Notification.Builder(ctx, NotificationChannels.MUSIC_DOWNLOAD_CHANNEL_ID);
|
||||
builder.setVisibility(Notification.VISIBILITY_PUBLIC);
|
||||
} else builder = new Notification.Builder(ctx);
|
||||
|
||||
builder.setSmallIcon(com.vtosters.lite.R.drawable.ic_download_outline_28)
|
||||
.setShowWhen(false)
|
||||
.setContentTitle(NotificationChannels.MUSIC_CACHING_NAME)
|
||||
.setContentText("Разрешение обложки: " + resolution + "x" + resolution);
|
||||
|
||||
NotificationChannels.getNotificationManager().notify(hash, builder.build());
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,23 @@
|
||||
package ru.vtosters.lite.music;
|
||||
|
||||
import android.app.DownloadManager;
|
||||
import android.app.Notification;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
import com.vk.dto.music.Playlist;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
@@ -19,19 +25,86 @@ import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import ru.vtosters.lite.downloaders.AudioDownloader;
|
||||
import ru.vtosters.lite.downloaders.notifications.NotificationChannels;
|
||||
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
|
||||
import ru.vtosters.lite.music.cache.FileCacheImplementation;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
import ru.vtosters.lite.utils.IOUtils;
|
||||
|
||||
public class MP3Downloader {
|
||||
|
||||
public final static int CACHE_STRATEGY = 0;
|
||||
public final static int EXTERNAL_STORAGE_STRATEGY = 1;
|
||||
|
||||
private static final String URL_FORMAT = "https://api.vk.com/method/audio.getById?v=5.131&access_token=%s&audios=%s©right=https://vtosters.app/";
|
||||
private static final OkHttpClient client = new OkHttpClient();
|
||||
|
||||
public static void execute(MusicTrack track) {
|
||||
private static int getSelectedStrategy() {
|
||||
return AndroidUtils.getPreferences().getInt("music_storing_strategy", CACHE_STRATEGY);
|
||||
}
|
||||
|
||||
public static void downloadPlaylist(Playlist playlist) {
|
||||
var token = AndroidUtils.getPreferences().getString("vk_admin_token", "");
|
||||
if (token.isEmpty()) {
|
||||
Toast.makeText(AndroidUtils.getGlobalContext(), "Обновите токен VK Admin в настройках", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
String trackIds = playlist.R
|
||||
.stream()
|
||||
.map(MusicTrack::y1)
|
||||
.collect(Collectors.joining(","));
|
||||
|
||||
var request = new Request.a()
|
||||
.b(String.format(URL_FORMAT, token, trackIds))
|
||||
.a();
|
||||
client.a(request).a(new Callback() {
|
||||
|
||||
@Override
|
||||
public void a(Call call, IOException e) {
|
||||
Log.d("MP3Downloader", e + "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void a(Call call, Response response) throws IOException {
|
||||
var payload = response.a().g();
|
||||
Log.d("MP3Downloader", payload);
|
||||
try {
|
||||
JSONObject jsonObj = new JSONObject(payload);
|
||||
if (!jsonObj.has("response"))
|
||||
return;
|
||||
var tracks = new JSONObject(payload).getJSONArray("response");
|
||||
for (int i = 0; i < tracks.length(); i++) {
|
||||
JSONObject responseNode = tracks.getJSONObject(i);
|
||||
if (!responseNode.has("url"))
|
||||
continue;
|
||||
var url = responseNode.optString("url");
|
||||
Log.d("MP3Downloader", url);
|
||||
if (TextUtils.isEmpty(url))
|
||||
return;
|
||||
var track = playlist.R.get(i);
|
||||
switch (getSelectedStrategy()) {
|
||||
case CACHE_STRATEGY:
|
||||
saveToCache(track, url);
|
||||
break;
|
||||
case EXTERNAL_STORAGE_STRATEGY:
|
||||
saveToDownloads(track.toString(), url);
|
||||
}
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.d("MP3Downloader", e + "");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void downloadMP3(MusicTrack track) {
|
||||
var token = AndroidUtils.getPreferences().getString("vk_admin_token", "");
|
||||
if (token.isEmpty()) {
|
||||
Toast.makeText(AndroidUtils.getGlobalContext(), "Обновите токен VK Admin в настройках", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
var request = new Request.a()
|
||||
.b(String.format(URL_FORMAT, token, track.y1()))
|
||||
.a();
|
||||
@@ -47,20 +120,28 @@ public class MP3Downloader {
|
||||
Log.d("MP3Downloader", payload);
|
||||
try {
|
||||
JSONObject jsonObj = new JSONObject(payload);
|
||||
if (jsonObj.has("response")) {
|
||||
JSONObject responseNode = new JSONObject(payload).getJSONArray("response")
|
||||
.getJSONObject(0);
|
||||
if (responseNode.has("url")) {
|
||||
var url = responseNode.optString("url");
|
||||
Log.d("MP3Downloader", url);
|
||||
if (url != null) {
|
||||
if (url.endsWith(".mp3")) {
|
||||
downloadMP3(track.toString(), url);
|
||||
} else if (VKM3UParser.isM3U8(url)) {
|
||||
AudioDownloader.downloadM3U8(track);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!jsonObj.has("response"))
|
||||
return;
|
||||
JSONObject responseNode = new JSONObject(payload).getJSONArray("response")
|
||||
.getJSONObject(0);
|
||||
if (!responseNode.has("url"))
|
||||
return;
|
||||
var url = responseNode.optString("url");
|
||||
Log.d("MP3Downloader", url);
|
||||
if (TextUtils.isEmpty(url))
|
||||
return;
|
||||
if (VKM3UParser.isM3U8(url)) {
|
||||
AudioDownloader.downloadM3U8(track);
|
||||
return;
|
||||
}
|
||||
switch (getSelectedStrategy()) {
|
||||
case CACHE_STRATEGY:
|
||||
ArtworkDownloader.downloadArtworks(track);
|
||||
saveToCache(track, url);
|
||||
break;
|
||||
case EXTERNAL_STORAGE_STRATEGY:
|
||||
saveToDownloads(track.toString(), url);
|
||||
break;
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.d("MP3Downloader", e + "");
|
||||
@@ -69,7 +150,37 @@ public class MP3Downloader {
|
||||
});
|
||||
}
|
||||
|
||||
private static void downloadMP3(String title, String url) {
|
||||
private static void saveToCache(MusicTrack track, String url) throws IOException {
|
||||
var trackId = track.y1();
|
||||
if (CacheDatabaseDelegate.isCached(trackId))
|
||||
return;
|
||||
|
||||
var content = IOUtils.readAllBytes(new URL(url).openStream());
|
||||
var trackFile = FileCacheImplementation.getTrackFile(trackId);
|
||||
if (!trackFile.exists())
|
||||
trackFile.getParentFile().mkdirs();
|
||||
IOUtils.writeToFile(trackFile, content);
|
||||
notifySavingToCache(track);
|
||||
CacheDatabaseDelegate.insertTrack(track);
|
||||
NotificationChannels.getNotificationManager().cancel(trackId.hashCode());
|
||||
}
|
||||
|
||||
private static void notifySavingToCache(MusicTrack track) {
|
||||
Notification.Builder builder;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
builder = new Notification.Builder(AndroidUtils.getGlobalContext(), NotificationChannels.MUSIC_DOWNLOAD_CHANNEL_ID);
|
||||
builder.setVisibility(Notification.VISIBILITY_PUBLIC);
|
||||
} else builder = new Notification.Builder(AndroidUtils.getGlobalContext());
|
||||
|
||||
builder.setSmallIcon(com.vtosters.lite.R.drawable.ic_download_outline_28)
|
||||
.setShowWhen(false)
|
||||
.setContentTitle("Закешированный трек: " + track.toString());
|
||||
|
||||
NotificationChannels.getNotificationManager().notify(track.y1().hashCode(), builder.build());
|
||||
}
|
||||
|
||||
private static void saveToDownloads(String title, String url) {
|
||||
var downloadManager = AndroidUtils.getGlobalContext().getSystemService(DownloadManager.class);
|
||||
var request = new DownloadManager.Request(Uri.parse(url))
|
||||
.setAllowedOverRoaming(true)
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
package ru.vtosters.lite.music.cache;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.database.SQLException;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.vk.core.util.ToastUtils;
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import bruhcollective.itaysonlab.libvkx.ILibVkxService;
|
||||
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
|
||||
import bruhcollective.itaysonlab.libvkx.client.LibVKXClientImpl;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
import ru.vtosters.lite.utils.IOUtils;
|
||||
|
||||
public class CacheDatabaseDelegate {
|
||||
private static final int DB_VERSION = 0x1;
|
||||
private static final String DB_NAME = "vt_lite_cache.db";
|
||||
private static final String TABLE_NAME = "tracks";
|
||||
private static final String COLUMN_ID = "id";
|
||||
private static final String COLUMN_TRACK_ID = "trackId";
|
||||
private static final String COLUMN_ALBUM_ID = "albumId";
|
||||
private static final String COLUMN_TITLE = "title";
|
||||
private static final String COLUMN_SUBTITLE = "subtitle";
|
||||
private static final String COLUMN_ARTIST = "artist";
|
||||
private static final String COLUMN_ALBUM_TITLE = "albumTitle";
|
||||
private static final String COLUMN_EXPLICIT = "explicit";
|
||||
private static final String COLUMN_HAS_ARTWORK = "hasArtwork";
|
||||
private static final String COLUMN_DURATION = "duration";
|
||||
private static DbHelper helperInstance;
|
||||
|
||||
protected static DbHelper getHelper() {
|
||||
if (helperInstance == null) helperInstance = new DbHelper(AndroidUtils.getGlobalContext());
|
||||
return helperInstance;
|
||||
}
|
||||
|
||||
// You MUST call this method ONLY AFTER TRACK IS DOWNLOADED!
|
||||
public static void insertTrack(MusicTrack track) {
|
||||
try {
|
||||
var json = track.J();
|
||||
var albumNode = json.optJSONObject("album");
|
||||
|
||||
var hasArtwork = false;
|
||||
var albumId = -1;
|
||||
var albumName = "";
|
||||
|
||||
if (albumNode != null) {
|
||||
hasArtwork = albumNode.has("thumb");
|
||||
albumId = albumNode.getInt("id");
|
||||
albumName = albumNode.getString("title");
|
||||
}
|
||||
|
||||
var formula = String.format(
|
||||
"INSERT INTO %s (%s, %s, %s, %s, %s, %s, %s, %s, %s) VALUES ('%s', %s, '%s', '%s', '%s', '%s', %s, %s, %s)",
|
||||
TABLE_NAME,
|
||||
COLUMN_TRACK_ID, COLUMN_ALBUM_ID, COLUMN_TITLE, COLUMN_SUBTITLE, COLUMN_ARTIST, COLUMN_ALBUM_TITLE, COLUMN_EXPLICIT, COLUMN_DURATION, COLUMN_HAS_ARTWORK,
|
||||
track.y1(),
|
||||
albumId,
|
||||
URLEncoder.encode(json.getString("title"), "UTF-8"),
|
||||
URLEncoder.encode(json.optString("subtitle"), "UTF-8"),
|
||||
URLEncoder.encode(json.getString("artist"), "UTF-8"),
|
||||
URLEncoder.encode(albumName, "UTF-8"),
|
||||
json.optInt("is_explicit"),
|
||||
json.getInt("duration"),
|
||||
hasArtwork ? 1 : 0
|
||||
);
|
||||
getHelper().getReadableDatabase().execSQL(formula);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
ToastUtils.a("Ошибка при записи трека в БД! [" + e.getMessage() + "]", true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
getHelper().getReadableDatabase().execSQL(String.format("DELETE FROM %s", TABLE_NAME));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static JSONObject reconstructFromDb(Cursor cursor) {
|
||||
try {
|
||||
var trackId = cursor.getString(getColumnIndex(cursor, COLUMN_TRACK_ID));
|
||||
var splits = trackId.split("_");
|
||||
var id = Integer.parseInt(splits[0]);
|
||||
var ownerId = Integer.parseInt(splits[1]);
|
||||
|
||||
return new JSONObject()
|
||||
.put("url", FileCacheImplementation.getServerPath())
|
||||
.put("id", id)
|
||||
.put("owner_id", ownerId)
|
||||
.put("title", URLDecoder.decode(cursor.getString(getColumnIndex(cursor, COLUMN_TITLE)), "UTF-8"))
|
||||
.put("subtitle", URLDecoder.decode(cursor.getString(getColumnIndex(cursor, COLUMN_SUBTITLE)), "UTF-8"))
|
||||
.put("artist", URLDecoder.decode(cursor.getString(getColumnIndex(cursor, COLUMN_ARTIST)), "UTF-8"))
|
||||
.put("duration", cursor.getInt(getColumnIndex(cursor, COLUMN_DURATION)))
|
||||
.put("is_explicit", cursor.getInt(getColumnIndex(cursor, COLUMN_EXPLICIT)))
|
||||
.put("access_key", 2281488)
|
||||
.putOpt("album", reconstructAlbumFromDb(cursor, trackId));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return new JSONObject();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static JSONObject reconstructAlbumFromDb(Cursor cursor, String trackId) {
|
||||
try {
|
||||
if (!FileCacheImplementation.getTrackArtwork(trackId, 300).exists()
|
||||
&& !FileCacheImplementation.getTrackArtwork(trackId, 600).exists()
|
||||
&& !FileCacheImplementation.getTrackArtwork(trackId, 1200).exists()
|
||||
) {
|
||||
var ownerId = Integer.parseInt(trackId.split("_")[1]);
|
||||
return new JSONObject()
|
||||
.put("id", cursor.getInt(getColumnIndex(cursor, COLUMN_ALBUM_ID)))
|
||||
.put("owner_id", ownerId)
|
||||
.put("title", URLDecoder.decode(cursor.getString(getColumnIndex(cursor, COLUMN_ALBUM_TITLE)), "UTF-8"))
|
||||
.putOpt("thumb", reconstructAlbumThumbFromDb(cursor, trackId));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static JSONObject reconstructAlbumThumbFromDb(Cursor cursor, String trackId) {
|
||||
try {
|
||||
if (cursor.getInt(getColumnIndex(cursor, COLUMN_HAS_ARTWORK)) != 0)
|
||||
new JSONObject()
|
||||
.put("height", 600)
|
||||
.put("width", 600)
|
||||
.putOpt("photo_300", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackArtwork(trackId, 300)))
|
||||
.putOpt("photo_600", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackArtwork(trackId, 600)))
|
||||
.putOpt("photo_1200", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackArtwork(trackId, 1200)));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int getColumnIndex(Cursor cursor, String name) {
|
||||
return cursor.getColumnIndexOrThrow(name);
|
||||
}
|
||||
|
||||
public static boolean hasTracks() {
|
||||
if (LibVKXClient.isIntegrationEnabled()) {
|
||||
return LibVKXClient.getInstance().runOnServiceSync(new LibVKXClientImpl.LibVKXActionGeneric<Boolean>() {
|
||||
@Override
|
||||
public Boolean run(ILibVkxService service) {
|
||||
try {
|
||||
return service.getCache().size() > 0;
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean defaultValue() {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var formula = String.format(
|
||||
"SELECT COUNT(%s) FROM %s",
|
||||
COLUMN_ID,
|
||||
TABLE_NAME
|
||||
);
|
||||
var cursor = getHelper().getReadableDatabase().rawQuery(formula, null);
|
||||
if (cursor != null) {
|
||||
cursor.moveToFirst();
|
||||
int count = cursor.getInt(0);
|
||||
cursor.close();
|
||||
return count > 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<MusicTrack> getTracks() {
|
||||
return getTracks(String.format(
|
||||
"SELECT * FROM %s",
|
||||
TABLE_NAME
|
||||
));
|
||||
}
|
||||
|
||||
public static List<MusicTrack> getTracksAsPlaylist() {
|
||||
return getTracks(String.format(
|
||||
"SELECT * FROM %s WHERE %s > -1 GROUP BY %s HAVING COUNT(*) > 1 ORDER by id DESC",
|
||||
TABLE_NAME,
|
||||
COLUMN_ALBUM_ID,
|
||||
COLUMN_ALBUM_ID
|
||||
));
|
||||
}
|
||||
|
||||
public static List<MusicTrack> getTracksByAlbum(String id) {
|
||||
return getTracks(String.format(
|
||||
"SELECT COUNT(%s) FROM %s WHERE %s = %s",
|
||||
COLUMN_ID,
|
||||
TABLE_NAME,
|
||||
COLUMN_ALBUM_ID,
|
||||
id
|
||||
));
|
||||
}
|
||||
|
||||
public static List<MusicTrack> getTracksByAlbumOne(String id) {
|
||||
return getTracks(String.format(
|
||||
"SELECT * FROM %s WHERE %s = %s LIMIT 1",
|
||||
TABLE_NAME,
|
||||
COLUMN_ALBUM_ID,
|
||||
id
|
||||
));
|
||||
}
|
||||
|
||||
public static List<MusicTrack> getTracks(String query) {
|
||||
List<MusicTrack> list = new ArrayList<>();
|
||||
try (Cursor cursor = getHelper().getReadableDatabase().rawQuery(query, new String[0])) {
|
||||
while (cursor.moveToNext()) {
|
||||
list.add(new MusicTrack(reconstructFromDb(cursor)));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static boolean isCached(String trackId) {
|
||||
if (!LibVKXClient.isIntegrationEnabled()) {
|
||||
var formula = String.format(
|
||||
"SELECT COUNT(%s) FROM %s WHERE %s = '%s'",
|
||||
COLUMN_ID,
|
||||
TABLE_NAME,
|
||||
COLUMN_TRACK_ID,
|
||||
trackId
|
||||
);
|
||||
try {
|
||||
Cursor size = getHelper().getReadableDatabase().rawQuery(formula, null);
|
||||
if (size != null) {
|
||||
size.moveToFirst();
|
||||
int count = size.getInt(0);
|
||||
size.close();
|
||||
return count != 0;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return LibVKXClient.getInstance().runOnServiceSync(new LibVKXClientImpl.LibVKXActionGeneric<Boolean>() {
|
||||
@Override
|
||||
public Boolean run(ILibVkxService service) {
|
||||
try {
|
||||
var splits = trackId.split("_");
|
||||
var id = Integer.parseInt(splits[0]);
|
||||
var owner = Integer.parseInt(splits[1]);
|
||||
return service.isTrackCached(id, owner);
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean defaultValue() {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void removeTrackFromCache(String trackId) {
|
||||
if (LibVKXClient.isIntegrationEnabled()) {
|
||||
LibVKXClient.getInstance().runOnService((service -> {
|
||||
try {
|
||||
var splits = trackId.split("_");
|
||||
var id = Integer.parseInt(splits[0]);
|
||||
var owner = Integer.parseInt(splits[1]);
|
||||
service.deleteTrackFromCache(id, owner);
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
IOUtils.deleteRecursive(FileCacheImplementation.getTrackFolder(trackId));
|
||||
|
||||
var formula = String.format(
|
||||
"DELETE FROM %s WHERE %s = '%s'",
|
||||
TABLE_NAME,
|
||||
COLUMN_TRACK_ID,
|
||||
trackId
|
||||
);
|
||||
getHelper().getWritableDatabase().execSQL(formula);
|
||||
}
|
||||
|
||||
public static long getTrackCount() {
|
||||
if (!LibVKXClient.isIntegrationEnabled())
|
||||
return DatabaseUtils.queryNumEntries(getHelper().getReadableDatabase(), TABLE_NAME);
|
||||
return LibVKXClient.getInstance().runOnServiceSync(new LibVKXClientImpl.LibVKXActionGeneric<Long>() {
|
||||
@Override
|
||||
public Long run(ILibVkxService service) {
|
||||
try {
|
||||
return (long) service.getCache().size();
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
return defaultValue();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long defaultValue() {
|
||||
return 0L;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static class DbHelper extends SQLiteOpenHelper {
|
||||
public DbHelper(Context context) {
|
||||
super(context, DB_NAME, null, DB_VERSION);
|
||||
}
|
||||
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
var formula = String.format(
|
||||
"CREATE TABLE %s (%s INTEGER PRIMARY KEY AUTOINCREMENT, %s TEXT, %s INTEGER, %s TEXT, %s TEXT, %s TEXT, %s TEXT, %s INTEGER, %s INTEGER, %s INTEGER)",
|
||||
TABLE_NAME,
|
||||
COLUMN_ID, COLUMN_TRACK_ID, COLUMN_ALBUM_ID, COLUMN_TITLE, COLUMN_SUBTITLE, COLUMN_ARTIST, COLUMN_ALBUM_TITLE, COLUMN_EXPLICIT, COLUMN_DURATION, COLUMN_HAS_ARTWORK
|
||||
);
|
||||
db.execSQL(formula);
|
||||
}
|
||||
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package ru.vtosters.lite.music.cache;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
import ru.vtosters.lite.utils.IOUtils;
|
||||
|
||||
public class FileCacheImplementation {
|
||||
|
||||
public static void clear() {
|
||||
CacheDatabaseDelegate.clear();
|
||||
IOUtils.deleteRecursive(getCacheDir(), false);
|
||||
}
|
||||
|
||||
public static File getCacheDir() {
|
||||
return AndroidUtils.getGlobalContext().getDir("vt_tracks", Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
public static File getTrackFolder(String trackId) {
|
||||
return new File(getCacheDir(), trackId);
|
||||
}
|
||||
|
||||
public static File getTrackFile(String trackId) {
|
||||
return new File(getTrackFolder(trackId), "track.mp3");
|
||||
}
|
||||
|
||||
public static File getTrackFile(MusicTrack track) {
|
||||
return getTrackFile(track.y1());
|
||||
}
|
||||
|
||||
public static File getTrackArtwork(String trackId, int factor) {
|
||||
return new File(getTrackFolder(trackId), "artwork_" + factor + ".png");
|
||||
}
|
||||
|
||||
public static String getFileUri(File file) {
|
||||
if (!file.exists()) return null;
|
||||
return Uri.fromFile(file).toString();
|
||||
}
|
||||
|
||||
public static String getServerPath() {
|
||||
return "https://vtosters.app";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package ru.vtosters.lite.music.cache;
|
||||
|
||||
import com.vk.dto.common.data.VKList;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Collection;
|
||||
|
||||
import ru.vtosters.lite.utils.ReflectionUtils;
|
||||
|
||||
public class VKListHelper {
|
||||
|
||||
public static void addToVKList(VKList<?> vkList, Object addition) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
ReflectionUtils.invokeStaticMethod(VKList.class, "add", new Class[]{Object.class}, addition);
|
||||
}
|
||||
|
||||
public static void addToVKList(VKList<?> vkList, int index, Object addition) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
ReflectionUtils.invokeStaticMethod(VKList.class, "add", new Class[]{int.class, Object.class}, index, addition);
|
||||
}
|
||||
|
||||
public static void addAllToVKList(VKList<?> list, Collection<?> addition) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
ReflectionUtils.invokeStaticMethod(VKList.class, "addAll", new Class[]{Collection.class}, addition);
|
||||
}
|
||||
|
||||
public static void addAllToVKList(VKList<?> list, int index, Collection<?> addition) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
ReflectionUtils.invokeStaticMethod(VKList.class, "addAll", new Class[]{int.class, Collection.class}, index, addition);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package ru.vtosters.lite.music.cache.helpers;
|
||||
|
||||
import com.vk.dto.music.Playlist;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
|
||||
import ru.vtosters.lite.music.cache.FileCacheImplementation;
|
||||
import ru.vtosters.lite.utils.AccountManagerUtils;
|
||||
|
||||
public class PlaylistHelper {
|
||||
|
||||
public static Playlist createCachedPlaylistMetadata() {
|
||||
try {
|
||||
var obj = new JSONObject()
|
||||
.put("id", -1)
|
||||
.put("owner_id", AccountManagerUtils.getUserId())
|
||||
.put("type", 0)
|
||||
.put("album_type", "playlist")
|
||||
.put("title", "Кешированные треки")
|
||||
.put("description", "Это - список всех кешированных треков. Данный плейлист НЕ СУЩЕСТВУЕТ в вашем профиле.\n\n" +
|
||||
"При отсутствии соединения с Интернетом данный плейлист превратится в обычный список треков.")
|
||||
.put("count", 0);
|
||||
return new Playlist(obj);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Playlist createCachedPlaylistMetadata(String id) {
|
||||
var track = TracklistHelper.getTracksOne(id).get(0);
|
||||
var albumLink = track.I;
|
||||
var thumb = albumLink.u1();
|
||||
return createAlbum(
|
||||
thumb != null ? thumb.h(600) : "",
|
||||
albumLink.getTitle(),
|
||||
track.C,
|
||||
track.y1(),
|
||||
albumLink.getId()
|
||||
);
|
||||
}
|
||||
|
||||
public static Playlist createAlbum(String photo, String title, String artist, String trackId, int albumId) {
|
||||
try {
|
||||
var jPhoto = new JSONObject();
|
||||
if (!photo.isEmpty())
|
||||
jPhoto.put("height", 600)
|
||||
.put("width", 600)
|
||||
.putOpt("photo_300", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackArtwork(trackId, 300)))
|
||||
.putOpt("photo_600", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackArtwork(trackId, 600)));
|
||||
var obj = new JSONObject()
|
||||
.put("id", -2)
|
||||
.put("owner_id", albumId)
|
||||
.put("type", 1)
|
||||
.put("album_type", "main_only")
|
||||
.put("title", title)
|
||||
.put("description", albumId + "/ Это кешированный альбом!")
|
||||
.put("main_artists", new JSONArray()
|
||||
.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) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Playlist> getAlbumPlaylists() {
|
||||
List<Playlist> list = new ArrayList<>();
|
||||
CacheDatabaseDelegate.getTracksAsPlaylist().forEach(track -> {
|
||||
var albumLink = track.I;
|
||||
var thumb = albumLink.u1();
|
||||
list.add(createAlbum(
|
||||
thumb != null ? thumb.h(600) : "",
|
||||
albumLink.getTitle(),
|
||||
track.C,
|
||||
track.y1(),
|
||||
albumLink.getId()
|
||||
));
|
||||
});
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package ru.vtosters.lite.music.cache.helpers;
|
||||
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
|
||||
import org.json.JSONArray;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
|
||||
|
||||
public class TracklistHelper {
|
||||
|
||||
public static List<MusicTrack> getTracks() {
|
||||
return CacheDatabaseDelegate.getTracks();
|
||||
}
|
||||
|
||||
public static List<MusicTrack> getTracks(String id) {
|
||||
return CacheDatabaseDelegate.getTracksByAlbum(id);
|
||||
}
|
||||
|
||||
public static List<MusicTrack> getTracksOne(String id) {
|
||||
return CacheDatabaseDelegate.getTracksByAlbumOne(id);
|
||||
}
|
||||
|
||||
public static JSONArray tracksToIds(List<MusicTrack> tracks) {
|
||||
var arr = new JSONArray();
|
||||
for (MusicTrack track : tracks) {
|
||||
arr.put(track.y1());
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public static JSONArray tracksToJsons(List<MusicTrack> tracks) {
|
||||
var arr = new JSONArray();
|
||||
for (MusicTrack track : tracks) {
|
||||
arr.put(track.J());
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
package ru.vtosters.lite.music.cache.injectors;
|
||||
|
||||
import android.os.RemoteException;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.vk.api.audio.AudioGetPlaylist;
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
import com.vk.dto.music.Playlist;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.ObservableOnSubscribe;
|
||||
import ru.vtosters.lite.music.MP3Downloader;
|
||||
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
|
||||
import ru.vtosters.lite.music.cache.helpers.PlaylistHelper;
|
||||
import ru.vtosters.lite.music.cache.helpers.TracklistHelper;
|
||||
import ru.vtosters.lite.utils.NetworkUtils;
|
||||
|
||||
public class PlaylistInjector {
|
||||
|
||||
public final static String CHANNEL_NAME = "VTCH";
|
||||
|
||||
public static void injectDownloadPlaylist(Playlist playlist) {
|
||||
MP3Downloader.downloadPlaylist(playlist);
|
||||
}
|
||||
|
||||
public static boolean eligibleForOfflineCaching() {
|
||||
return !NetworkUtils.isNetworkConnected() && CacheDatabaseDelegate.hasTracks();
|
||||
}
|
||||
|
||||
public static Observable<AudioGetPlaylist.c> injectGetPlaylist(AudioGetPlaylist audioGetPlaylist) {
|
||||
try {
|
||||
if (!eligibleForOfflineCaching())
|
||||
return null;
|
||||
var requestArgs = audioGetPlaylist.b();
|
||||
var id = requestArgs.get("id");
|
||||
if (TextUtils.isEmpty(id) || !id.equals("-1") && !id.equals("-2"))
|
||||
return null;
|
||||
|
||||
return Observable.a((ObservableOnSubscribe<AudioGetPlaylist.c>) observableEmitter -> {
|
||||
AudioGetPlaylist.c response = new AudioGetPlaylist.c();
|
||||
if (LibVKXClient.isIntegrationEnabled()) {
|
||||
response.b = PlaylistHelper.createCachedPlaylistMetadata();
|
||||
LibVKXClient.getInstance().runOnService((service) -> {
|
||||
try {
|
||||
List<String> cache = service.getCache();
|
||||
ArrayList<MusicTrack> tracks = new ArrayList<>();
|
||||
for (String json : cache) {
|
||||
tracks.add(new MusicTrack(new JSONObject(json)));
|
||||
}
|
||||
response.c = tracks;
|
||||
} catch (RemoteException | JSONException e) {
|
||||
e.printStackTrace();
|
||||
response.c = new ArrayList<>();
|
||||
}
|
||||
observableEmitter.b(response);
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (id.equals("-1")) {
|
||||
response.c = (ArrayList<MusicTrack>) TracklistHelper.getTracks();
|
||||
response.b = PlaylistHelper.createCachedPlaylistMetadata();
|
||||
} else {
|
||||
response.c = (ArrayList<MusicTrack>) TracklistHelper.getTracks(requestArgs.get("owner_id"));
|
||||
response.b = PlaylistHelper.createCachedPlaylistMetadata(requestArgs.get("owner_id"));
|
||||
}
|
||||
observableEmitter.b(response);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
package ru.vtosters.lite.music.cache.injectors;
|
||||
|
||||
import com.vk.catalog2.core.CatalogParser;
|
||||
import com.vk.catalog2.core.api.dto.CatalogResponse;
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.ObservableOnSubscribe;
|
||||
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
|
||||
import ru.vtosters.lite.music.cache.helpers.PlaylistHelper;
|
||||
import ru.vtosters.lite.music.cache.helpers.TracklistHelper;
|
||||
import ru.vtosters.lite.utils.AccountManagerUtils;
|
||||
import ru.vtosters.lite.utils.NetworkUtils;
|
||||
|
||||
public class TracklistInjector {
|
||||
|
||||
public static boolean eligibleForOfflineCaching() {
|
||||
return !NetworkUtils.isNetworkConnected() && CacheDatabaseDelegate.hasTracks();
|
||||
}
|
||||
|
||||
public static Observable<CatalogResponse> createOfflineRx(CatalogParser parser) {
|
||||
return Observable.a((ObservableOnSubscribe<CatalogResponse>) observableEmitter -> {
|
||||
if (LibVKXClient.isIntegrationEnabled()) {
|
||||
LibVKXClient.getInstance().runOnService((service) -> {
|
||||
try {
|
||||
List<MusicTrack> tracks = new ArrayList<>();
|
||||
List<String> cache = service.getCache();
|
||||
|
||||
for (String json : cache) {
|
||||
tracks.add(new MusicTrack(new JSONObject(json)));
|
||||
}
|
||||
|
||||
observableEmitter.b(parser.c(createVirtualCatalog(tracks)));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
observableEmitter.b(parser.c(createVirtualCatalog(TracklistHelper.getTracks())));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void injectIntoExistingCatalog(JSONObject catalogNode) {
|
||||
if (!CacheDatabaseDelegate.hasTracks()) return;
|
||||
try {
|
||||
var sectionsNode = catalogNode.getJSONObject("catalog")
|
||||
.getJSONArray("sections");
|
||||
var firstSection = sectionsNode.getJSONObject(0);
|
||||
if (!firstSection.getString("url").equals("https://vk.com/audios" + AccountManagerUtils.getUserId() + "?section=all"))
|
||||
return;
|
||||
|
||||
var foundPlaylistBlock = false;
|
||||
var suitableIndex = 0;
|
||||
|
||||
var blocks = firstSection.getJSONArray("blocks");
|
||||
for (int i = 0; i < blocks.length(); i++) {
|
||||
var block = blocks.getJSONObject(i);
|
||||
var dataType = block.getString("data_type");
|
||||
if ("music_audios".equals(dataType)) {
|
||||
suitableIndex = i - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!"music_playlists".equals(dataType))
|
||||
continue;
|
||||
|
||||
var playlistIds = block.getJSONArray("playlists_ids");
|
||||
var newPlaylistIds = new JSONArray()
|
||||
.put(AccountManagerUtils.getUserId() + "_-1");
|
||||
|
||||
for (int j = 0; j < playlistIds.length(); j++)
|
||||
newPlaylistIds.put(playlistIds.get(j));
|
||||
|
||||
block.put("playlists_ids", newPlaylistIds);
|
||||
|
||||
// need test
|
||||
blocks.remove(i);
|
||||
blocks.put(i, block);
|
||||
|
||||
foundPlaylistBlock = true;
|
||||
}
|
||||
|
||||
if (foundPlaylistBlock) {
|
||||
var playlists = catalogNode.getJSONArray("playlists")
|
||||
.put(0, PlaylistHelper.createCachedPlaylistMetadata().J());
|
||||
catalogNode.put("playlists", playlists);
|
||||
return;
|
||||
}
|
||||
|
||||
var ids = new JSONArray()
|
||||
.put(0, AccountManagerUtils.getUserId() + "_-1");
|
||||
|
||||
var playlistsBlock = new JSONObject()
|
||||
.put("id", getRandomId())
|
||||
.put("data_type", "music_playlists")
|
||||
.put("url", "synth:cache:playlists")
|
||||
.put("playlists_ids", ids);
|
||||
|
||||
var layout = new JSONObject()
|
||||
.put("name", "large_slider")
|
||||
.put("owner_id", AccountManagerUtils.getUserId());
|
||||
|
||||
playlistsBlock.put("layout", layout);
|
||||
|
||||
blocks.put(suitableIndex, playlistsBlock);
|
||||
|
||||
firstSection.put("blocks", blocks);
|
||||
|
||||
var playlists = new JSONArray()
|
||||
.put(0, PlaylistHelper.createCachedPlaylistMetadata().J());
|
||||
catalogNode.put("playlists", playlists);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static JSONObject createVirtualCatalog(List<MusicTrack> tracks) throws JSONException {
|
||||
var clickAction = new JSONObject()
|
||||
.put("type", "open_url")
|
||||
.put("target", "internal")
|
||||
.put("url", "https://vkx.app/index.html?app=vt_ctg");
|
||||
|
||||
var catalogBanner = new JSONObject()
|
||||
.put("id", 1234)
|
||||
.put("image_mode", "cover")
|
||||
.put("title", "Включите интеграцию VK X!")
|
||||
.put("text", "Получите доступ к хранилищу треков и возможности включать/кешировать треки из VTosters в VK X.\n\n" +
|
||||
"Подробнее - в Настройки > Настройки VTosters > Кешированные треки.")
|
||||
.put("click_action", new JSONObject().put("action", clickAction));
|
||||
var catalogBanners = new JSONArray()
|
||||
.put(catalogBanner);
|
||||
|
||||
var audiosBlock = new JSONObject()
|
||||
.put("id", getRandomId())
|
||||
.put("data_type", "music_audios")
|
||||
.put("url", "synth:cache:list")
|
||||
.put("audios_ids", TracklistHelper.tracksToIds(tracks));
|
||||
|
||||
var layout = new JSONObject()
|
||||
.put("name", "list")
|
||||
.put("owner_id", AccountManagerUtils.getUserId());
|
||||
|
||||
audiosBlock.put("layout", layout);
|
||||
|
||||
var blocks = new JSONArray()
|
||||
.put(audiosBlock);
|
||||
|
||||
// var vkxPostirony = new JSONObject()
|
||||
// .put("id", getRandomId())
|
||||
// .put("data_type", "catalog_banners")
|
||||
// .put("url", "synth:cache:vkx")
|
||||
// .put("catalog_banner_ids", new JSONArray().put(1234));
|
||||
//
|
||||
// var vkxlayout = new JSONObject()
|
||||
// .put("name", "large_list")
|
||||
// .put("owner_id", Globals.getUserId())
|
||||
// .put("infinite_repeat", true);
|
||||
//
|
||||
// vkxPostirony.put("layout", vkxlayout);
|
||||
//
|
||||
// if (eligibleForVkxAd())
|
||||
// blocks.put(vkxPostirony);
|
||||
var randomId = getRandomId();
|
||||
|
||||
var section = new JSONObject()
|
||||
.put("id", randomId)
|
||||
.put("title", "Сохраненные треки")
|
||||
.put("url", "synth:cache")
|
||||
.put("blocks", blocks);
|
||||
|
||||
var sections = new JSONArray()
|
||||
.put(section);
|
||||
|
||||
var catalog = new JSONObject()
|
||||
.put("sections", sections)
|
||||
.put("default_section", randomId);
|
||||
|
||||
return new JSONObject()
|
||||
.put("catalog", catalog)
|
||||
.put("audios", TracklistHelper.tracksToJsons(tracks))
|
||||
.put("catalog_banners", catalogBanners);
|
||||
}
|
||||
|
||||
private static String getRandomId() {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
}
|
||||
Vendored
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
||||
2966f42770fa9480b905416ecea04a6c4b401193
|
||||
Executable
BIN
Binary file not shown.
@@ -1 +0,0 @@
|
||||
0840244ca18d071426e1a4cf350bdb0d446ca68f
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
6278c6512808b17c9470c0b231c40ab1d01ef877
|
||||
@@ -89,6 +89,8 @@
|
||||
|
||||
invoke-static {p1, v1}, Lkotlin/jvm/internal/Intrinsics;->a(Ljava/lang/Object;Ljava/lang/String;)V
|
||||
|
||||
invoke-static/range {p1 .. p1}, Lru/vtosters/lite/music/cache/injectors/TracklistInjector;->injectIntoExistingCatalog(Lorg/json/JSONObject;)V
|
||||
|
||||
invoke-virtual {v0, p1}, Lcom/vk/catalog2/core/CatalogParser;->c(Lorg/json/JSONObject;)Lcom/vk/catalog2/core/api/dto/CatalogResponse;
|
||||
|
||||
move-result-object p1
|
||||
|
||||
+1
-1
@@ -153,7 +153,7 @@
|
||||
|
||||
iget-object v0, p0, Lcom/vk/music/bottomsheets/playlist/PlaylistBottomSheetClickListener;->e:Lcom/vk/dto/music/Playlist;
|
||||
|
||||
invoke-interface {p1, p2, v0}, Lcom/vk/music/bottomsheets/playlist/PlaylistBottomSheetModel;->a(Landroid/content/Context;Lcom/vk/dto/music/Playlist;)V
|
||||
invoke-static/range {v0 .. v0}, Lru/vtosters/lite/music/cache/injectors/PlaylistInjector;->injectDownloadPlaylist(Lcom/vk/dto/music/Playlist;)V
|
||||
|
||||
goto/16 :goto_0
|
||||
|
||||
|
||||
@@ -65,6 +65,23 @@
|
||||
}
|
||||
.end annotation
|
||||
|
||||
invoke-static {}, Lru/vtosters/lite/music/cache/injectors/TracklistInjector;->eligibleForOfflineCaching()Z
|
||||
|
||||
move-result v0
|
||||
|
||||
if-eqz v0, :cond_custom
|
||||
|
||||
invoke-virtual {p0}, Lcom/vk/catalog2/core/VkCatalogConfiguration;->j()Lcom/vk/catalog2/core/CatalogParser;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
invoke-static {v0}, Lru/vtosters/lite/music/cache/injectors/TracklistInjector;->createOfflineRx(Lcom/vk/catalog2/core/CatalogParser;)Lio/reactivex/Observable;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
return-object v0
|
||||
|
||||
:cond_custom
|
||||
.line 1
|
||||
new-instance v0, Lcom/vk/catalog2/core/api/music/CatalogGetAudio;
|
||||
|
||||
|
||||
@@ -239,11 +239,23 @@
|
||||
|
||||
const/4 v1, 0x1
|
||||
|
||||
.line 14
|
||||
#invoke-static {p1}, Lru/vtosters/lite/music/cache/injectors/PlaylistInjector;->injectGetPlaylist(Lcom/vk/api/audio/AudioGetPlaylist;)Lio/reactivex/Observable;
|
||||
|
||||
#move-result-object v2
|
||||
|
||||
#if-eqz v2, :cond_custom
|
||||
|
||||
#move-object p1, v2
|
||||
|
||||
#goto :goto_20
|
||||
|
||||
#:cond_custom
|
||||
|
||||
invoke-static {p1, v0, v1, v0}, Lcom/vk/api/base/ApiRequest;->d(Lcom/vk/api/base/ApiRequest;Lcom/vk/api/base/ApiThreadHolder;ILjava/lang/Object;)Lio/reactivex/Observable;
|
||||
|
||||
move-result-object p1
|
||||
|
||||
:goto_20
|
||||
.line 15
|
||||
new-instance v6, Lcom/vk/music/playlist/ModernPlaylistModel$d;
|
||||
|
||||
|
||||
@@ -593,6 +593,10 @@
|
||||
|
||||
move-result-object v0
|
||||
|
||||
invoke-static/range {v0 .. v0}, Lru/vtosters/lite/music/cache/injectors/PlaylistInjector;->injectDownloadPlaylist(Lcom/vk/dto/music/Playlist;)V
|
||||
|
||||
return-void
|
||||
|
||||
if-eqz v0, :cond_1
|
||||
|
||||
.line 3
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
.locals 10
|
||||
|
||||
.line 5
|
||||
invoke-static/range {p1 .. p1}, Lru/vtosters/lite/music/cache/FileCacheImplementation;->getTrackFile(Ljava/lang/String;)Ljava/io/File;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
return-object v0
|
||||
|
||||
invoke-static {p0}, Landroidx/core/content/ContextCompat;->getExternalCacheDirs(Landroid/content/Context;)[Ljava/io/File;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
@@ -452,6 +452,8 @@
|
||||
.method public static a([BIIJ)V
|
||||
.locals 10
|
||||
|
||||
return-void
|
||||
|
||||
move-wide v0, p3
|
||||
|
||||
move p3, p1
|
||||
|
||||
Reference in New Issue
Block a user